Fork me on GitHub
#beginners
<
2018-10-31
>
cybersapiens9704:10:25

anyone have a real basic web app idea for a beginner to do some training with compojure+ring?

seancorfield04:10:05

@cybersapiens97 I guess the classic ones would be a To-Do list or an Address Book.

cybersapiens9704:10:57

the address book seems nice, i'll give a try, what database you recommend for this ?

seancorfield04:10:07

You could use any of the simple, embedded/semi-embedded DBs. SQLite, H2, HSQLDB, Derby...

seancorfield04:10:34

Then all you need is org.clojure/java.jdbc and the driver for whichever DB you pick -- no external process to run.

cybersapiens9704:10:19

cool, thanks 😀

seancorfield04:10:00

If you have any questions about that aspect, hit me up in #sql (it's easy for me to keep an eye open for questions there).

seancorfield04:10:30

Have you thought about how you might generate the HTML web pages? (assuming you're going to build a traditional server-side web app)

cybersapiens9704:10:18

no i haven't yet, i assume i can either feed them to the user directly with ring, or use a specialized lib for this? also, days ago i think i saw some lib which could automatically generate html pages. (if didn't knew about it, probably i would write by hand the entire html)

seancorfield04:10:45

Hiccup is one option: generates HTML from Clojure data structures. Selmer is another: Django-like templates that can include basic loops, conditionals, and variable substitutions.

seancorfield04:10:52

I like Selmer, since it keeps the "views" separate from code, as near-HTML templates that can easily be edited and worked with using any web-capable tooling.

seancorfield04:10:11

(and you can easily view the template directly in a browser)

cybersapiens9704:10:01

seems better suited for me, because on future projects i'll have a friend working on the front end

cybersapiens9704:10:55

still don't know how we are going to glue things together haha

cybersapiens9704:10:22

but i'm excited doing web development in clojure

seancorfield04:10:33

Feel free to read over this small example https://github.com/framework-one/fw1-clj/blob/master/examples/usermanager/main.clj -- it shows how to put together Component, Compojure, Ring, Selmer...

seancorfield04:10:21

It uses Derby for the DB.

seancorfield04:10:00

There's a story behind that example 🙂 About a decade ago, I created a fairly minimal MVC framework which become of the two most popular frameworks in the CFML (ColdFusion) world. After learning Clojure, I ported it from CFML to Clojure -- and we used it at work as we migrated from CFML to Clojure -- but after using it for a while, I decided the framework didn't add enough value in the Clojure world, so I stripped it out of that example, to show what a simple DB-backed MVC web app might look like, with just libraries used together.

cybersapiens9704:10:10

seems nice as example and you haven't lost your work along the way

cybersapiens9704:10:19

i'm kindly rejecting Luminus and it's book, seems too much to digest, and i'm feeling very easy and pleasant using just libraries for things i need

seancorfield04:10:20

My feeling about Luminus is that, although it's "only" a template, it has too many moving parts. I've seen a lot of beginners go down that path and then get horribly confused when they try to move outside the guidance of the book and discover all the pieces of the template that they don't understand (or even know about) yet.

cybersapiens9704:10:03

yes, that was my initial reaction, and i felt lost

cybersapiens9704:10:21

and when i started reading just the wiki section of ring and then compojure, it's so easy and simple to use

cybersapiens9704:10:13

and you can even do lot's of little things to understand how you can communicate with the http protocol, everything seems less confusing

seancorfield04:10:11

I agree that starting from scratch with a basic "Hello World!" with Ring and Compojure and building up from there is probably the best way to learn how the pieces work.

cybersapiens9706:10:30

i'm trying to render a html file with selmer, and it's documentation says By default the templates are located relative to the ClassLoader URL. but what is the classloader URL ?

cybersapiens9706:10:54

and btw my file is at resources/public and i'm seeing an error saying that it doesn't found the file with this code (render-file "register.html" {:title "title"}) and this one doesn't work too, same error: (render-file "resources/public/register.html" {:title "title"}) also tried to put the file inside src folder

stardiviner06:10:45

Is there anybody use clj-http for store session? Just like Python requests.Session().get? I checked clj-http README.md document, found cookies support, but have not found the session support.

cybersapiens9706:10:27

never used sessions before, but i've seen it on Ring documentation

jarvinenemil06:10:31

Fellow Clojurians I have a problem which I am trying to solve, would appreciate some advice. I have this datastructure: A vector of maps

[{:identifier "1337" :colors [:yellow :blue :brown] :size [:s :m :l :xl]}
 {:identifier "1338" :colors [:yellow :brown] :size [:s :m]}
 {:identifier "1339 ":colors [:blue] :size [:xl]}]
I want to group this collection by matching colors and sizes, in this case I want to return 1337 and 1339 since both have the
:colors :blue
and the
:size :s
. I am a bit unsure what to use to solve this problem to be honest/what to look at.

schmee07:10:23

is it possible to change the :colors and :size to sets instead of list? that would simplify things

schmee07:10:37

also, shouldn’t :size be :sizes?

schmee07:10:53

also, 1337 and 1339 don’t have :size :s in common, do you mean 1337 and 1338? 🤔

schmee07:10:38

or :size :xl?

jarvinenemil07:10:31

xl and blue are matching so I want 1337 and 1339

jarvinenemil07:10:31

the return I want would be something like this:

{:identifiers ["1337" "1339"] :size :xl :color :blue}

jarvinenemil07:10:20

and yes, they could be sets

schmee07:10:31

ok, this will get you almost all the way:

(def items
  [{:identifier "1337" :colors #{:yellow :blue :brown} :sizes #{:s :m :l :xl}}
   {:identifier "1338" :colors #{:yellow :brown} :sizes #{:s :m}}
   {:identifier "1339 ":colors #{:blue} :sizes #{:xl}}])

(defn find-by-color-and-size [color size items]
  (filter
    #(and (contains? (:colors %) color)
          (contains? (:sizes %) size))
    items))

user=> (find-by-color-and-size :blue :xl items)
({:identifier "1337", :colors #{:yellow :blue :brown}, :sizes #{:m :s :l :xl}} 
 {:identifier "1339 ", :colors #{:blue}, :sizes #{:xl}})

schmee07:10:14

then you can map over the result to get it in the desired format

jarvinenemil07:10:35

I see thank you very much:wizard:✌️, I will do some more reading

schmee07:10:01

you’re welcome! 🙇

orestis07:10:42

If this is a large dataset and you want to do this kind of lookup often, I’d look into a database though.

orestis07:10:56

(Because lookup time this way is linear, so if the dataset gets too large it might be slow).

jarvinenemil08:10:58

@ i read ur solution now more in detail (was in a hurry to work) and I don't want to have to send in color and size. I want to find 1339 and 1337 by seeing that it has the same color in its :colors set and the same size in the size set.

jarvinenemil08:10:19

Might actually go with the database for this. It's gonna suck to do that lookup when I have 100k lines in the vector. Or I will have to re-think my strategy.

schmee08:10:38

that’s database territory for sure

schmee08:10:36

if you use datomic it’s trivial, or if you can fit your entire data set in memory you can use https://github.com/tonsky/datascript/

schmee08:10:16

but it should be doable with most databases, with varying degrees of difficulty

jarvinenemil08:10:23

in that case I have to do a lookup as soon as a new Entry is added in Datomic (which might actually be really smoth.. they have some QUEUE-stuff). I am currently using core-async with channels, before I only had items that I put on a channel which were batched by 10. Now I have to do some filtering too so it's like "Batch the items that have values that are equal by 10". 🙂

jarvinenemil08:10:29

Transaction report queue

schmee08:10:28

here’s a start of how to do it with Datomic/Datascript:

(def db2
  (d/create-conn
    {:item/colors {:db/cardinality :db.cardinality/many}
     :item/sizes {:db/cardinality :db.cardinality/many}}))

(def items
  [#:item{:identifier "1337" :colors #{:yellow :blue :brown} :sizes #{:s :m :l :xl}}
   #:item{:identifier "1338" :colors #{:yellow :brown} :sizes #{:s :m}}
   #:item{:identifier "1339 ":colors #{:blue} :sizes #{:xl}}])

(d/transact! db2 items)

(defn into-set [a b]
  (set [a b]))

(defn find-all-that-share-colors-and-sizes []
  (d/q '[:find [?t ...]
         :where [?e :item/colors ?c]
                [?e2 :item/colors ?c]
                [(not= ?e ?e2)]
                [?e :item/sizes ?s]
                [?e2 :item/sizes ?s]
                [(user/into-set ?e ?e2) ?t]]
       @db2))

user=> (find-all-that-share-colors-and-sizes)
[#{1 2} #{1 3}]

jarvinenemil09:10:39

Thank you I will use datomic, it looks really smooth. Very appreciated that you took your time to explain this and showed some nice examples @ 🙂

schmee10:10:09

anytime, happy to help! 🙂

stardiviner06:10:13

@cybersapiens97 I just watched it, but still don't know how to use the Ring sessions with clj-http.

cybersapiens9706:10:11

sorry @stardiviner i don't have experience with back end development. starting just now, wait until someone more experienced appears on the chat

orestis07:10:25

@stardiviner cookies are sessions, no?

orestis07:10:51

Are you trying say to login to a remote endpoint, then do some other calls?

stardiviner07:10:16

Yes, Python's requests.Session() objects did this.

stardiviner07:10:22

I want similar in Clojure

orestis07:10:29

Right, first of all Ring is a server thing AFAIK, it won’t help you here. Let me look at clj-http docs.

orestis07:10:41

CookieStore is what you should use.

orestis07:10:37

That is, assuming the remote server will send you some Auth token via a cookie.

orestis07:10:39

The idea being that cookies received by the server are stored in the store, then sent back on further requests. This is what browsers do.

stardiviner07:10:47

I see, let me have a look.

stardiviner07:10:50

@orestis This is exactly what I want. Thanks very much.

jaihindh.reddy13:10:12

What do you guys think about clj-http and http.async.client. Which one do you guys use, and why?

vale14:10:32

I use aleph

seancorfield15:10:04

@jaihindh.reddy We mostly use clj-http as it's been around longer and it is very robust and full-featured. We've started using http://www.http-kit.org/client.html recently for async HTTP handling (since we were already using http-kit as a server -- although we've since switched back to Jetty, to get better New Relic support).

mathpunk17:10:11

can I get some thoughts on what is "the right way" to write this? In this context, restore-commands is a collection of arrays containing tokens I would send to the shell---

mathpunk17:10:33

and db-helpers is a directory

mathpunk17:10:34

The data making those commands was slurped out of a bash script that is known to work, so I can apply sh to them and get a good result, if the pwd is correct

mathpunk17:10:06

but when I went to try a similar trick using with-sh-dir, I discover apply cannot take a macro

schmee18:10:13

you are now stuck in macro-land:

(defmacro foo [dbh tokens] `(with-sh-dir dbh [email protected]))

schmee18:10:01

@mathpunk ^

hiredman18:10:09

don't use with-sh-dir then

mathpunk18:10:42

Where "`" means, 'act like you're literally typing this,' and "[email protected]" means 'but evaluate this part to its value'

mathpunk18:10:49

@hiredman nono, I'm just new to it

mathpunk18:10:58

that looks easy enough

hiredman18:10:32

with-sh-dir just sets up a binding for a dynamic var, and you aren't changing its binding anywhere, so you don;t need it

mathpunk18:10:32

would you recommend a different way of accomplishing the task, "execute a command but as through from a particular directory"?

hiredman18:10:57

sh takes an argument

mathpunk18:10:33

oh! there's an optional :dir flag isn't there

mathpunk18:10:46

i forgot all about it

schmee18:10:43

all good in the hood then 😄

nickstares021:10:56

running lein repl I get Exception in thread "Thread-1" java.lang.IllegalAccessError: loose! does not exist. lein version gives Leiningen 2.8.1 on Java 1.8.0_171 Java HotSpot(TM) 64-Bit Server VM. Anyone know what may be the cause of this?

hiredman21:10:35

what is in your .lein/profiles.clj?

hiredman21:10:13

also do you get a full stacktrace?

nickstares021:10:48

Exception in thread "Thread-1" java.lang.IllegalAccessError: loose! does not exist
	at clojure.core$refer.invokeStatic(core.clj:4119)
	at clojure.core$refer.doInvoke(core.clj:4087)
	at clojure.lang.RestFn.applyTo(RestFn.java:139)
	at clojure.core$apply.invokeStatic(core.clj:648)
	at clojure.core$load_lib.invokeStatic(core.clj:5755)
	at clojure.core$load_lib.doInvoke(core.clj:5717)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:648)
	at clojure.core$load_libs.invokeStatic(core.clj:5774)
	at clojure.core$load_libs.doInvoke(core.clj:5758)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:648)
	at clojure.core$require.invokeStatic(core.clj:5796)
	at clojure.core$require.doInvoke(core.clj:5796)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at user$eval736.invokeStatic(NO_SOURCE_FILE:0)
	at user$eval736.invoke(NO_SOURCE_FILE)
	at clojure.lang.Compiler.eval(Compiler.java:6927)
	at clojure.lang.Compiler.eval(Compiler.java:6916)
	at clojure.lang.Compiler.eval(Compiler.java:6890)
	at clojure.core$eval.invokeStatic(core.clj:3105)
	at clojure.core$eval.invoke(core.clj:3101)
	at leiningen.core.eval$fn__4181.invokeStatic(eval.clj:342)
	at leiningen.core.eval$fn__4181.invoke(eval.clj:332)
	at clojure.lang.MultiFn.invoke(MultiFn.java:233)
	at leiningen.core.eval$eval_in_project.invokeStatic(eval.clj:366)
	at leiningen.core.eval$eval_in_project.invoke(eval.clj:356)
	at leiningen.repl$server$fn__5864.invoke(repl.clj:244)
	at clojure.lang.AFn.applyToHelper(AFn.java:152)
	at clojure.lang.AFn.applyTo(AFn.java:144)
	at clojure.core$apply.invokeStatic(core.clj:646)
	at clojure.core$with_bindings_STAR_.invokeStatic(core.clj:1881)
	at clojure.core$with_bindings_STAR_.doInvoke(core.clj:1881)
	at clojure.lang.RestFn.invoke(RestFn.java:425)
	at clojure.lang.AFn.applyToHelper(AFn.java:156)
	at clojure.lang.RestFn.applyTo(RestFn.java:132)
	at clojure.core$apply.invokeStatic(core.clj:650)
	at clojure.core$bound_fn_STAR_$fn__4671.doInvoke(core.clj:1911)
	at clojure.lang.RestFn.invoke(RestFn.java:397)
	at clojure.lang.AFn.run(AFn.java:22)
	at java.lang.Thread.run(Thread.java:748)
REPL server launch timed out.

nickstares021:10:33

profiles.clj:

{:user {:java-cmd     "/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/bin/java"
        :dependencies [[vvvvalvalval/scope-capture "0.3.1"]]
        :injections   [(require '[sc.api :as sc :refer [spy defsc brk loose!]])
                       (sc.api.logging/register-cs-logger
                        :sc.api.logging/log-spy-cs
                        (fn [cs]
                          nil))]}}

nickstares021:10:39

oh man, it must be that injection

hiredman21:10:39

the first thing I would do is remove any plugins/injects/middleware/etc, those will come from a project.clj, a profiles.clj, or a your .lein/profiles.clj

nickstares021:10:00

ok, thanks. removing that fixed the problem

hiredman21:10:26

this is the kind of thing that makes me recommend against ever using profiles.clj

dpsutton21:10:47

but i agree that the things in the profiles are just problems and its not obvious where to look

christian.gonzalez23:10:04

@hiredman I once spent 2 hours debugging a cryptic error protobuf message, only to find out that the cljfmt in my profiles.clj had an older version of protobuf as a dependency. It was a nightmare.

christian.gonzalez23:10:10

I also recommend not using profiles.clj