Fork me on GitHub
#beginners
<
2016-01-31
>
chadhs03:01:16

any recommendations for a good place to start with building your first clojure web application?

jonahbenton04:01:55

@drewverlee: I haven't studied the source, but semantically, the "in-transaction" part of commute gets thrown away IF someone else updated the ref during or after the "in-transaction" execution of your function occurred. Think about it- in that case, your change was definitely lost. So commute will run it again. Alter provides "serialization" semantics, which is why there are rollbacks.

jonahbenton04:01:20

@drewverlee: re: the (dosync (future...))- yes- dosync and commute/alter have to be executed in the same thread

jonahbenton05:01:16

hi @fred, so, oooo is the top level function and it should receive a data structure like css1. clojure.string/join wants to receive a sequence which should be the transformation of the data structure into strings by the lower level functions, starting with ooo. using the map function to effect this transformation should work- e.g. (clojure.string/join "\n" (map ooo nestedmaps)) . similarly, ooo needs help to transform the block, which it can get from oo, again using map: (str selector " { " (map oo block) " } ")) you can repeat this process of having the higher level functions get help from the lower level functions using the map function to apply the lower level function to the appropriate portion of the data structure that the higher level function receives. now- this is definitely a tricky approach to try to compose all at once. doing this work at the repl in a "bottom up" fashion can be very helpful. e.g. start with (def p {:font-family "Times New Roman"}) and make a function to transform that data into the string you want. Then add a second or third key/value pair and use a collection operator like map to apply the transformation to all the key/value pairs. Then wrap a data structure around those and repeat the process. Being able to take this step by step through the repl is tremendously helpful because you can see the data at each step.

jonahbenton05:01:27

hope that's helpful, good luck

fred09:01:22

Many thanks @jonahbenton I guess if my approach is tricky I'm not one the best path to solve this in clojure 😅 I first solved it interactively like this by following the CSS logic (ruleset, selector, declaraction block, properties & values) :

(defn nmap->css [nmap]
  (clojure.string/join "\n" (map (fn [[k v]] (str k " { " (clojure.string/join "\n" (map (fn [[property value]] (str (name property) " : " (if (keyword? value) (name value) (str "\"" value "\"")) ";")) v)) " }")) nmap)))
and then I thought "what would be the right way and most elegant to decompose this in clojure? probably with comp and partial ... " my intuition here is that join kind of get in the way of showing me the true structure of the global function... many thanks for your advice simple_smile

Drew Verlee15:01:30

@jonahbenton: thanks again friend!

krchia15:01:59

hi, i’m not very sure what happens when you add a token to :body in the response hashmap when you do a POST for say, a successful login

krchia15:01:26

i tried navigating to another page after i did a login, but the :body doesn’t contain the encrypted token anymore

seriousbug15:01:15

The :body returned by a handler is what the server sends to the client, for example, a web page or a JSON response.

seriousbug15:01:39

And the body that is passed to the handler is what the client sends to the server

seriousbug15:01:59

The client needs to send the token with every connection, that doesn't happen automatically

seriousbug15:01:21

There are some wrappers to automatically manage sessions though, you might want to look into those

jonahbenton16:01:50

hey @fred- so let me suggest an alternate approach. what i meant by tricky is that this problem space- rendering a data structure into a stream, or taking a data structure and generating code- is a surprisingly deep and subtle one. here are a couple of examples of solutions: james reeve's hiccup, which renders data to html: https://github.com/weavejester/hiccup/blob/master/src/hiccup/compiler.clj and cheshire, which renders data to json: https://github.com/dakrone/cheshire/blob/master/src/cheshire/generate.clj this is not to discourage at all, just to provide some context- this is a great area in which to learn! a suggestion in terms of learning- instead of thinking about the problem from the code perspective- what's the structure of the code to work with this data- think about the problem from a data perspective- what data structure makes the code simple. for instance, try using vectors rather than maps, and try keeping all values as strings. e.g. [ :body [ [:background-color "#d0e4fe" ]] 😛 [[:font-family "Times New Roman"] [:font-size "20px"]]] and then look at the walk function to traverse the structure.

fred20:01:08

Hi @jonahbenton many thanks, trying to explore an html dsl is on my list definitely. If you tell me looking into hiccup is way to go that's what I'm gonna do 👍 anyway learning clojure is really an exiting journey 😋 cheers

seancorfield21:01:20

I'll offer up a +1 for Hiccup -- we use it extensively at World Singles.

Drew Verlee23:01:26

this exercise from clojure brave and true is breaking my circuit: > Create a function that uses futures to parallelize the task of downloading random quotes from http://www.braveclojure.com/random-quote using (slurp "http://www.braveclojure.com/random-quote"). The futures should update an atom that refers to a total word count for all quotes. The function will take the number of quotes to download as an argument and return the atom’s final value. Keep in mind that you’ll need to ensure that all futures have finished before returning the atom’s final value. Here’s how you would call it and an example result:

(quote-word-count 5)
; => {"ochre" 8, "smoothie" 2}
I'm not sure how to go about ensuring the threads i want to spawn to fetch fetch words + update counter have all finished. Assuming the happy path, where they always succeed how can i trigger checking the atom at the right point?

Drew Verlee23:01:06

I guess I might have to use a counter to keep track of how many have finished. Then watch the counter

Drew Verlee23:01:22

Im guessing the next chapter on core.async is going to give me a better answer.

mfikes23:01:22

@drewverlee: If you are solving it in Clojure, perhaps a refinement on your “waiting on counter” idea is to use Java’s CountDownLatch.

Drew Verlee23:01:33

thanks @mfikes, ill give it a look. Is there another way to do this besides a counter? I mean, at a high level I think something has to bee keep tracking threads and tracking that they finished. I haven't done any of this type of thing so i'm not sure if i'm using the wrong... err tool.

mfikes23:01:46

Thread has a join() method—that’s a pretty low-level construct. But, it wouldn’t work if the threads are returned to some pool.

mfikes23:01:40

You are using futures… hmm… maybe there is something you can do there.

mfikes23:01:22

If you deref you can get blocking behavior.

mfikes23:01:09

(As in, put all your futures in a collection, and then deref all of them serially.)

mfikes23:01:03

One advantage to that approach is you do everything in Clojure, FWIW.

Drew Verlee23:01:55

at this point in the book i have [futures, promises, delays, vars, atoms, refs]. I assume i'm supposed to start a thread for each request and update atom inside that thread (as the order shouldn't matter). The problem is knowing when i'm done. > (As in, put all your futures in a collection, and then deref all of them serially.) I don't follow 😕

mfikes23:01:48

Ah… no problem…. I can elaborate.

mfikes23:01:26

Let’s say you have this

(def f (future (Thread/sleep 10000) (println "done") 100))

mfikes23:01:52

Then if you do (deref f) (or, equivalently @f), you will see that it blocks until done.

mfikes23:01:02

Then extend that idea to collections of tasks

(def coll [(future (Thread/sleep 10000) (println "done") 100) (future (Thread/sleep 10000) (println "done") 100)])
which you can wait for all to be done via (doseq [f coll] @f)

mfikes23:01:45

In the above, you’d replace the thread sleeping and printing, with work and atom updating.

mfikes23:01:50

After the (doseq [f coll] @f) finishes, you can follow that with a call to deref the atom to get its value, and you’ll know that all the work is done prior to your atom deref.