Fork me on GitHub
#beginners
<
2017-11-30
>
stvnmllr204:11:44

Is there some built in function or middleware to add no-cache headers for a ring app? a search didn't find anything

stvnmllr204:11:15

Well.. it is sending them now in FF and chrome. not Edge. So guess ignore this unless someone has some valuable info I should be aware of. Thanks!

stvnmllr204:11:35

Ok, so I read it wrong. Apparently ff and chrome send nocache headers in the request. IE does not. So i'll have to add the response headers to force it not to cache. So back to my original question. Any ring middleware for this yet? I'll keep looking, then make my own.

seancorfield06:11:38

@stvnmllr2 It doesn't seem like it warrants middleware for that? Are you saying you want all requests to respond with nocache headers? That's a one-line function... a middleware library would be overkill.

stvnmllr213:11:14

Thanks. I guess I'm still new enough that I'm missing something. I figured it was only one function to add the headers. But I have a ton of api endpoints in my compojure api. And I don't want to wrap each in a call to a function right? Would not middleware be the way to deal with this in one place?

seancorfield17:11:06

But middleware is just a function. And adding a cache control header is a single call inside that function. Easy to write. Not worth being a library unless it has a bunch of "knobs and dials" for configuration.

stvnmllr217:11:56

OH! because middleware has to be a library right? So I'd just use a function somewhere in here I think. (have to look up how again)

(def app-routes
  (routes
    (-> #'home-routes
        (wrap-routes middleware/wrap-csrf)
        (wrap-routes middleware/wrap-formats))
    (-> #'service-routes
        (wrap-routes middleware/wrap-auth))
    (route/not-found
      (:body
        (error-page {:status 404
                     :title "page not found"})))))

stvnmllr217:11:40

Wait, i might have been thinking of leiningen tasks needing to be a library. But yeah, I can just write a function and plug it into that service-routes form and think i'll be good. thx

stvnmllr218:11:49

I previously asked about middleware for not caching my rest api. @seancorfield was nice enough to reply with some info. I guess I assumed this was called middleware. But since it doesn't exist, I will share here. Even though I guess most everyone may have this. Or is there some other great way to deal with IE?

(defn wrap-no-cache
  [handler]
  (fn [request]
    (-> (handler request) 
        (header "Pragma" "no-cache") 
        (header "Cache-Control" "no-cache, no-store, must-revalidate") 
        (header "Expires" "0"))))

seancorfield18:11:34

That looks reasonable to me.

seancorfield18:11:43

It's all functions, all the way down 🙂

seancorfield06:11:49

ring.util.response/header or ring.util.response/update-header is all you need, unless I'm misunderstanding your question?

seancorfield06:11:16

This SO answer seems to be fairly comprehensive in terms of what headers to actually set https://stackoverflow.com/questions/49547/how-to-control-web-page-caching-across-all-browsers

max09:11:11

When I start a REPL, how come it doesn't already know the functions from my code? I'm starting it in the same directory as the source code.

antique03:12:49

If you want the lein repl to load a namespace by default add this to your project.clj :repl-options {:init-ns user} where user is the name of the namespace you want it to load

max09:11:31

I don't even know if I'm wording that question correctly. Total newb here 😬

danm10:11:11

Because when you load a REPL by default it's not in the namespace of your code

danm10:11:33

There are flags you can pass when starting the REPL to make it be in that namespace

danm10:11:54

But you can always just load your code with a (require ...) call

max10:11:38

oh I see. I read lein help repl but it didn't seem to explain those flags

max10:11:17

do you know where I could find the correct help file to learn about these flags?

danm11:11:24

I'm afraid I use boot, so I don't know much about how to do it via lein

danm11:11:44

I can't remember the flags off the top of my head, but the default namespace for a boot trigger repl is boot.user

stardiviner12:11:01

Is there any existed "search engine" library for Clojure? I want to build a simple meta search engine which combine some search engines like Google, Bing etc results.

stardiviner12:11:34

I did a search on http://clojars.org, only find one related library called "web-search", but it only use Google. I wish to create a meta search engine which combines difference sources results.

orestis13:11:40

What is idiomatic Clojure for the pattern “decorate a collection, find the max decorated value, return the original value”? So for example, I have a vector [:a :b :c :d] and a function f so that (mapv f [:a :b :c :d]) yields [6 9 8 7]. I want to combine those together so that I get :b.

schmee13:11:38

@orestis (max-key f coll)

orestis13:11:08

@schmee Thanks, I’ve seen that and even tried it but for some reason never realized that’s what it’s doing 🙂

schmee13:11:51

haha, there are a lot of function that do a lot of things in clojure, it’s not easy to keep track of them all 😄

orestis13:11:11

OK, another question; how can I produce a lazy sequence in the same style as Python’s generators? i.e., yield a value, do some computation, yield another, and so on?

orestis13:11:39

Seems like for is the mental equivalent…

orestis13:11:50

Reference: I’m working on past AoC problems, and as part of one I have to generate list of length N of non-negative numbers that must add up to K. For N=2, K=10, I can do: (for [i (range 11) j (range 11) :when (= 10 (+ i j))] [i j]) - but how I can do it for the general case for arbitrary N? Write a macro?

rauh14:11:56

@orestis A macro doesn't help you if you don't know the parameter at compile time. You'll need to use a different approach

uwo16:11:01

does with-redefs-fn see past function boundaries?

noisesmith17:11:49

it crosses function boundaries, and also thread boundaries

noisesmith17:11:19

in general, it’s preferable to rewrite code to abstract out things that you will need to redefine, rather than using with-redefs or with-redefs-fn because those functions cause global mutations on a vm wide basis which can lead to really weird and hard to fix problems

noisesmith17:11:41

it’s a lesser problem with tests, but eg. if you ever want to run tests in parallel, it’s still a good idea to avoid those things

noisesmith17:11:07

not to mention, abstracting out the modular things you would want to redefine is probably better design in most cases anyway

uwo21:11:48

thanks! good point about parallel test running

pablore17:11:45

Hello, I’m having problems running clojure.spec. When I do (s/def ::key val?) it fails to compile with this error:

Caused by: java.lang.IllegalStateException: Attempting to call unbound fn: #'clojure.core/ident?

pablore17:11:29

Fixed it! Had to change the clojure version from 1.8.0 to 1.9.0-alpha17

ghadi17:11:17

@orestis for generation: look at iterate and some of the collection transformations e.g. filter

ghadi17:11:30

clojure doesn't have arbitrary suspension like yield in python

ghadi17:11:14

but you don't need it for like 99% percent of usecases

orestis17:11:17

@rauh @ghadi Thanks. I guess with python I was used to “this is the next value, throw it over the wall”. Now I have to think a bit more 😉

ghadi17:11:31

clojure tends to favor functional transformations over potentially irregular control flow

smogstate17:11:08

i am trying to format my ClojureScript file

smogstate17:11:21

soz wrong channel 🙂

noisesmith18:11:23

@stvnmllr2 you could consider alternatively

(update (handler request) :headers
        assoc
        "Pragma" "no-cache"
        "Cache-Control" "no-cache, no-store, must-revalidate"
        "Expires" "0")
- it does the same thing, but more directly / readably IMHO

seancorfield18:11:21

I actually find the code with explicit calls to header more readable in terms of intent. YMMV.

noisesmith18:11:18

(defn headers [& kvs] (fn [response] (apply update response :headers assoc kvs))

derpocious18:11:07

Hey all, would love some feedback on these palindrome functions and tests. 🙂

derpocious18:11:10

http://app.klipse.tech/?cljs_in=%3B%3B%20Palidrone%20Functions%20by%20Jim%20Lynch%0A%0A(defn%20is-palindrome-brute-force%20%5Bs%5D%0A%20%20%3B%3B%20Checks%20whether%20a%20string%20is%20a%20Palindrome%20by%20comparing%20the%20string%20with%20the%20reverse%20of%20string.%0A%20%20(%3D%20s%20(apply%20str%20(reverse%20s))))%0A%0A%0A(defn%20is-palindrome-half-and-half%20%5Bs%5D%0A%20%20%3B%3B%20Checks%20whether%20a%20string%20is%20a%20Palindrome%20by%20comparing%20the%20first%20half%20with%20the%20reverse%20of%20the%20second%20half.%0A%20%20(if%20(%3D%20s%20nil)%0A%20%20%20%20false%0A%20%20(%3D%20(take%20%20(.floor%20js%2FMath%20(%2F%20(count%20(seq%20s))%202))%20(seq%20s))%0A%20%20%20%20%20(reverse%20(take-last%20(.floor%20js%2FMath%20(%2F%20(count%20(seq%20s))%202))%20(seq%20s))))))%0A%20%20%0A%20%20%0A(require%20'%5Bcljs.test%20%3Arefer%20%5Bis%20deftest%5D%5D)%0A%0A(deftest%20palindrome-6-letters%0A%20%20(is%20(%3D%20true%20(is-palindrome-brute-force%20%22lollol%22)))%0A%20%20(is%20(%3D%20true%20(is-palindrome-half-and-half%20%22lollol%22))))%0A%0A(deftest%20palindrome-7-letters%0A%20%20(is%20(%3D%20true%20(is-palindrome-brute-force%20%22racecar%22)))%0A%20%20(is%20(%3D%20true%20(is-palindrome-half-and-half%20%22racecar%22))))%0A%0A(deftest%20non-palindrome-6-letters%0A%20%20(is%20(%3D%20false%20(is-palindrome-brute-force%20%22leller%22)))%0A%20%20(is%20(%3D%20false%20(is-palindrome-half-and-half%20%22leller%22))))%0A%0A(deftest%20non-palindrome-7-letters%0A%20%20(is%20(%3D%20false%20(is-palindrome-brute-force%20%22whatsup%22)))%0A%20%20(is%20(%3D%20false%20(is-palindrome-half-and-half%20%22whatsup%22))))%0A%0A(deftest%20palindrome-single-letter%0A%20%20(is%20(%3D%20true%20(is-palindrome-brute-force%20%22a%22)))%0A%20%20(is%20(%3D%20false%20(is-palindrome-half-and-half%20%22a%22))))%0A%0A(deftest%20palindrome-nil%0A%20%20(is%20(%3D%20false%20(is-palindrome-brute-force%20nil)))%0A%20%20(is%20(%3D%20false%20(is-palindrome-half-and-half%20nil))))%0A%0A%0A(palindrome-6-letters)%0A(palindrome-7-letters)%0A(non-palindrome-6-letters)%0A(non-palindrome-7-letters)%0A(palindrome-single-letter)%0A(palindrome-nil)%0A%0A(println%20%22Tests%20finished%20running!%22)%0A%0A

derpocious18:11:05

Is the "brute-force" one better because it's shorter, or is the other one better because it's more efficient (and is it actually more efficient)?

seancorfield18:11:41

That link won't even open in my browser (and it's a really ugly link to paste into chat). Can you put the code in a Gist and provide the link to that instead? That way folks can comment on it, fork it, etc.

noisesmith18:11:22

@derpocious I bet the most performant thing would use (-> s (StringBuilder.) (.reverse) (str))

dpsutton19:11:05

its cljs i think.

noisesmith19:11:34

oh, I should have seen that, thanks

noisesmith19:11:35

@derpocious some things to try - comparing (seq s) to (reverse s) is less code, and likely faster than making a string in order to compare it, if you care about performance use let blocks to bind computations instead of doing them twice, clojure doesn’t optimize that sort of thing automatically

noisesmith19:11:58

(also using a let instead of doing ops inline twice is more readable, and will rarely be less performant)

derpocious19:11:10

true, good points!

seancorfield19:11:32

Bear in mind that (let [s ""] (= (seq s) (reverse s))) is false which seems wrong to me...

noisesmith19:11:10

oh that’s weird

seancorfield19:11:23

So I think you might want (= (seq s) (seq (reverse s))) so that an empty string (and nil) are both considered palindromes.

noisesmith19:11:37

@seancorfield good catch, I did not expect that!

seancorfield19:11:41

(seq "") => nil

noisesmith19:11:23

if we extended IReversible to String (which I bet is totally doable) then we’d likely get near optimum performance out of the seq / seq reverse method

noisesmith19:11:52

as it is, it appears cljs currently does an eager reduce to reverse a string into a sequence, which is potentially wasteful for this task

noisesmith19:11:24

though seq on String is reversible, so in this case (= (seq s) (seq (reverse (seq s)))) could potentially perform better? I’d just test it if criterium existed for cljs heh

derpocious19:11:29

@noisesmith for me, (= (seq s) (seq (reverse s)))) is still returning false when s is empty string

noisesmith19:11:19

(ins)dev:cljs.user=> (= (seq "") (seq (reverse "")))
true

derpocious19:11:23

sorry, it's failing for me on palindrome-nil

derpocious19:11:51

we wanted (isPalindrome nil) to return false

noisesmith19:11:21

oh - then you need an extra check for that, I didn’t see that expectation

dpsutton19:11:26

if you drop the seq on the reverse call it will

dpsutton19:11:36

reverse returns a seq, an empty one on ""

noisesmith19:11:44

but then you are back to the first error - “” not being a palindrome

dpsutton19:11:45

then it will find nil != ()

derpocious19:11:20

this what I have now, only failing on the nil

noisesmith19:11:30

@derpocious the easy thing might be (and (string? %) …)

derpocious19:11:47

hmm how would you use that?

noisesmith19:11:12

(and (string? s) (= (seq s) (seq (reverse s))))

noisesmith19:11:03

or, if all you care about is the nil case, and not eg. lists of strings, just (and s ...)

derpocious19:11:03

ah ok, so then any time you pass a non-string into it then it just automatically returns false

derpocious19:11:44

I think that works. 🙂

derpocious19:11:55

And I found out how to use "run-all-tests" too!

noisesmith19:11:54

(ins)dev:cljs.user=> (time (dotimes [_ 1000] (= (seq "hello") (seq (reverse "hello")))))
"Elapsed time: 9.400000 msecs"
nil
(ins)dev:cljs.user=> (time (dotimes [_ 1000] (= (seq "hello") (seq (reverse (seq "hello"))))))
"Elapsed time: 6.090000 msecs"
nil

noisesmith19:11:06

the js JIT makes the numbers change (I think that’s what’s doing it) but with repeated usage the second seems to stay faster

noisesmith19:11:23

quite counterintuitive

derpocious19:11:04

(mind-blown)

noisesmith19:11:37

it’s about the IReversible thing - it allows short-circuiting instead of eagerly building a list

derpocious19:11:18

interesting...

derpocious19:11:32

Well here's my code now. Is it so beautiful? 🙂

seancorfield19:11:41

I would argue that nil is a palindrome in the same way that (and) is true.

seancorfield19:11:47

But if you want it to return true only for strings, why not (and (string? s) (= (seq s) (seq (reverse s)))) ?

seancorfield19:11:15

That was (palindrome? []) is also false...

seancorfield19:11:46

(I don't think anyone has mentioned yet that is-something is not very idiomatic Clojure -- something? is better)

seancorfield19:11:49

Hah, the conversation moved on and @noisesmith already said the same (re string?)! That'll teach me to scroll back and read everything properly! 🙂

derpocious19:11:08

yeah, I never understood why some functions ended in a question mark. Does is always take the place of "is"?

noisesmith19:11:24

it means the result of calling it is a boolean

seancorfield19:11:36

? indicates a predicate (that returns true or false specifically).

seancorfield19:11:24

If a function returns just truthy/falsey, then you wouldn't have a ? (in general -- based on several discussions I've seen on the Clojure mailing list over the years).

derpocious19:11:57

ok, that makes sense. So if I just name a function "palindrome?" it would be clear to people that it takes an input and returns true or false whether or not the input is a palindrome?

dpsutton19:11:58

(defn palindrome? "Returns true or false depending upon whether input is a palindrome. Nil is not considered a palindrome" [s] ...)

dpsutton19:11:04

make it clear 🙂

seancorfield19:11:40

@dpsutton "Returns true for a string that is a palindrome. Returns false for all other arguments." 🙂

dpsutton19:11:48

even better

dpsutton19:11:32

but @derpocious I'm a big fan of docstrings and well named functions. palindrome? does indicate to me that it returns true or false as we have discussed. And Sean's message there would clear up any corner issues I might have. Then there's always the source

derpocious20:11:24

true, great tips. Thanks a lot guys!

New To Clojure20:11:06

Hi, is there a good Clojure idioms page to know all idioms commonly used (I mean no just random 5 of them but most of them)?

derpocious20:11:46

It's tough to give an example of every idiom. It's more about what you are trying to do and whether your code is written in an idiomatic way, I think.