This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-12-04
Channels
- # adventofcode (171)
- # beginners (160)
- # boot (13)
- # chestnut (2)
- # cider (6)
- # cljs-dev (15)
- # cljsjs (35)
- # cljsrn (1)
- # clojure (139)
- # clojure-argentina (3)
- # clojure-brasil (1)
- # clojure-greece (31)
- # clojure-italy (5)
- # clojure-russia (5)
- # clojure-spec (18)
- # clojure-uk (11)
- # clojurescript (42)
- # clojurex (6)
- # core-async (12)
- # cursive (14)
- # dirac (13)
- # emacs (13)
- # events (1)
- # fulcro (46)
- # graphql (7)
- # leiningen (10)
- # lumo (3)
- # mount (31)
- # off-topic (20)
- # onyx (30)
- # perun (4)
- # planck (47)
- # re-frame (28)
- # reagent (14)
- # ring (5)
- # shadow-cljs (3)
- # spacemacs (7)
- # specter (13)
- # timbre (3)
- # unrepl (65)
- # yada (8)
@drewverlee I’m in the same boat 😄 have been trying to build a simple rest api since friday. it’s very complex I must say
@bravilogy I just started and my needs are very basic. I'll probably spend longer reading about the ecosystem then it takes me to build one ... i hope. For what its worth, i like the approach Yada is taking, though im going to use compjure-api because my needs are so basic i might get more out of something less opinionated.
it works great, but I found that it doesn’t automatically return json unless you specify so in every single handler’s headers {"content-type" "application/json"}
I’m looking for some alternatives at the moment, so if you find anything good please do let me know
Im going to try https://github.com/metosin/compojure-api Which seems to have a bit more then compjure basic. It might let me re-use my specs, other wise i would probably use Yadda, or if i needed to do anything more then a single set of GET, PUT, POSTS!
@bravilogy The content type should be set by wrap-json-response
, which also converts the data structure to a JSON string.
@seancorfield unfortunately it doesn’t. I had to manually do it for every single handler. I do have wrap-json-response
in my middleware list, but it doesn’t seem to be doing anything. Actually what happens is that, once I visit the url that’s supposed to display json, it actually downloads the content to my machine in a text format
@bravilogy It works fine. I just tried it in the REPL.
Your handler is missing a call to ring.util.response/response
tho'
boot.user=> (defn get-response [] {:body {:result {:this "is" :a 42}}}) ; this just fakes the `client/get` call
#'boot.user/get-response
boot.user=> (require '[ring.middleware.json :refer [wrap-json-response]])
nil
boot.user=> (require '[ring.util.response :as resp])
nil
boot.user=> (defn handler [req] (resp/response (-> (get-response) :body :result)))
#'boot.user/handler
boot.user=> (def app (wrap-json-response handler))
#'boot.user/app
boot.user=> (app {})
{:status 200, :headers {"Content-Type" "application/json; charset=utf-8"}, :body "{\"this\":\"is\",\"a\":42}"}
boot.user=>
then perhaps chestnut’s template has a problem 😞 this is my handler
(defn open-orders [req]
(response {:data "hello world"}))
and this is the config:
(defn config []
{:http-port (Integer. (or (env :port) 8080))
:middleware [[wrap-defaults api-defaults]
wrap-json-response
wrap-reload]})
I think you need wrap-json-response
before wrap-defaults
Otherwise the wrap-content-type
in wrap-defaults
is going to set a default (and incorrect) Content-Type
header. And that will prevent wrap-json-response
from adding its application/json
type.
Middleware is very order-sensitive.
You should try it out in the REPL, like I did.
(and we had this convo before and I suggested reordering the middleware, didn't I? 🙂 )
Anyway, glad you have it working now...
They're just functions, composed around the handler.
Getting used to the REPL-driven workflow takes a while. It's a very different way to work from most languages 😐
:thumbsup::skin-tone-2:
You mean more learning resources?
I mostly learned from books, and trying stuff in the REPL -- and there are a lot more books around now.
First two books I bought were Clojure in Action and Joy of Clojure.
might give them a try, thanks. I’ve always been more of a video course + stackoverflow learner
Eric Normand has some great video tutorials on http://purelyfunctional.tv
Joy of Clojure has a 2nd ed out now -- and it's very much the "why" of Clojure. I'd say Clojure Applied is a good book for practical application building.
Also, Programming Clojure 3rd Ed is in "beta" https://pragprog.com/book/shcloj3/programming-clojure-third-edition -- that's a pretty comprehensive coverage of the language.
Im assuming live reloading is possible when working on a compjure-api app. This blog post seems to indicate as much https://www.anthony-galea.com/blog/post/getting-started-with-compojure-api/, however, if i edit my handlers and refresh the page it remains the same. Any suggestions on live reload of the api when working?
nevermind. I was refreshing the wrong project 🙂
@drewverlee Another useful trick it to use Vars for handler references -- #'
-- so that you can just eval a single handler into the REPL (to redefine the function) without having to reload anything else.
I don't use any of the reload middleware etc. I just eval each form -- each defn
-- as I edit it, so my running app has the updated functions.
How to generate all the possibilities for a 'combination' lock with three positions, where each can be from set 0->9 inclusive, perhaps using clojure.math.combinatorics? I look to do it with the permutations
function, but unfortunately it only takes one argument.
(for [x (range 10) y (range 10) z (range 10)
:let [value (str x y z)]]
value)
@bravilogy > I wish there were more resources for clojure Also there are excercises 4Clojure A resource to help fledgling clojurians learn the language through interactive problems. Clojure Katas A set of problems to help you get your hands dirty with Clojure. Wonderland Clojure Katas Clojure Katas inspired by Alice in Wonderland. Parens of the Dead A screencast series of zombie-themed games written with Clojure and ClojureScript. Clojure Koans (online) Exercises meant to initiate you to the mysteries of the Clojure language.
Thanks yes for
would be good. But I still would like to see another way, as I think it is a 'permutations' problem.
There's also 'Advent of Code'. You get too see how others tackle the problems. It just started (beginning Dec).
@cjmurphy (combo/selections (range 10) 3)
Hey there! I am pretty sure I've already seen it, but I can't remember: how to get from
'({:id 1 :text "Buy bread"} {:id 2 :text "Pay taxes"})
to
{
1 {:id 1 :text "Buy bread"}
2 {:id 2 :text "Pay taxes"}}
}
(reduce #(assoc %1 (:id %2) %2) {} '({:id 1 :text "Buy bread"} {:id 2 :text "Pay taxes"}))
?
@sundarj That ends up with
{
1 [{:id 1, :text "Buy bread"}]
2 [{:id 2, :text "Pay taxes"}]
}
so might need a bit of further fettling to remove the liststhe solution with reduce works perfectly ! thanks all
It might not be the best solution mind you 😉 I do tend to use reduce sometimes when something simpler would do
What is the preferred way of using Either monad in Clojure? I. e. is {:left nil :right value}
returned from functions a good code style?
There was just a discussion about that not long ago. I mentioned the some->
macro as a way to do “Either”-ish programming, but there’s also using reduce
with reduced
, as other people mentioned.
Clojure isn’t strongly typed, so you don’t need a formal result type like {:left nil :right value}
, you just write your code to handle failures appropriately.
@manutter51 some->
is nice but you can't find out what error happened
> (some-> {:a 1} :b inc)
nil
reduce
with reduced
code could be tricky to read.btw I can't read that discussion because workspace has passed the free plan's message limit
Yeah, I was trying to summarize as best I could remember, I figured it would have scrolled out of view.
One other approach I played with was Elixir/Erlang style results with argument passing. I think I saw a Clojure Conj talk about Union Types that was a similar approach
so basically return a vector with a status and data, kinda tuple-ish
[:ok some-data]
or [:error :some-error-code "Some Error Message"]
then use destructuring to split them out.
@manutter51 Thank you a lot! Tuple-ish seems to be a way to go.
You might want to check out https://github.com/manutter51/beanbag, that’s some old code I wrote playing around with this idea.
@manutter51 I wish it was part of Clojure core, but useful as is too. Thanks!
Somebody, please review two Clojure functions which use reduce
.
Is it a write-only code (large reduce
's are hard to read) or a usual Clojure code?
@joshkh Regex with lookahead would be faster to code but I wanted to stick to pure Clojure (I'm learning the language)
@rauh that helps, thank you! Than I could map
on partition-by
results.
But in some cases using reduce
will be inevitable.
i don't know much about transducers, but could you compose (map count) and (partition-by identity) to do the partitioning and counting in one go?
There are many ways to go on after you have the partition-by result. Also with transducers. Next I'd probably map a (juxt count first)
over it. See what that gives you
Amazing! What about decoding function (2nd)? I'll read about <https://eli.thegreenplace.net/2017/reducers-transducers-and-coreasync-in-clojure/|transducers> in the mean time.
@ghsgd2 Btw, the transducer example is definitely advanced. A more beginner friendly version which is easy to understand is probably:
(->> "aabbbcddef"
(partition-by identity)
(map (juxt count first))
(map (fn [[count char]] (if (< 1 count) (str count char) char)))
(apply str))
The reverse is very similar, just use a slightly different partition-by
function, after that you probably want to use the repeat
function and then you're done.
@ghsgd2 and if you did want to go in the regex direction this example might help. i wrote a little function to parse a string using a set of separators while ignore them if they appear within quotes:
(defn parse-identifiers
[s]
(let [matcher (re-matcher (re-pattern "[^(\\s|,;)\"']+|\"([^\"]*)\"|'([^']*)'") s)]
(->> matcher
(partial re-find)
repeatedly
(take-while some?)
(map (partial (comp last (partial take-while some?)))))))
it took a moment for me to understand that re-matcher is stateful. just something to keep in mind in case you come across it. 🙂
@joshkh Thanks, will use it occasionally. @rauh Wrote decoding function.
(->> "10ABZ3CD4E"
(partition-by #(Character/isDigit %))
(map #(if (Character/isDigit (first %))
(Integer. (apply str %))
[(first %) (for [z (rest %)] [1 z])]))
(flatten)
(partition-all 2)
(mapcat #(apply repeat %))
(apply str)))
lein with-profile dev run
and lein with-profile dev run
both pick up the respective config\[env]\config.edn
files, however i don't get any configuration baked in when i run lein with-profile dev uberjar
or lein with-profile prod uberjar
@joshkh just speculating for a moment - I would expect a tool yogthos makes to produce a jar that is usable in both staging and prod, so that if it passes the staging qa tests the identical jar is also ran in prod
that contraindicates baking config into the jar at build time
btw baking configuration into jars is against common DevOps principles
I would expect using a tool like env
to pull in the right config - or at least the name of the right config file
@ghsgd2 exactly yes
oh wait! - it looks like yogthos/config really does want to be used that way, mea culpa
that’s a weird decision IMHO
ha, slightly agreed. creating a {:profiles {:uberjar {:resource-paths ["config/prod"]}} fixed my problem but then i can't build separate dev and prod uberjars
@joshkh btw that’s now how you add profiles
lein with-profile foo bar
replaces the bar config with foo config
you probably want lein with-profile +foo bar
which merges the configs
i forgot how profile stacking goes but lein with-profile uberjar +dev
and lein with-profile uberjar +prod
would be nice
if yogthos/config works at all, (I would assume it would!) adding +dev or +prod should sort your issue - but also consider using config management that allows using one jar in multiple environments
aye, thanks for the advice. i usually manage the environments on a heroku / dokku platform with environment variables (which i think will override the baked in configuration file)
i like having "settings" configurations built in with the option of being overridden and excluding secrets and tokens.
(@noisesmith and thanks, lein with-profile +prod uberjar
did the trick)
:thumbsup: - I thought it would
speaking of config and environments, do you have any experience scaling up clojure apps on aws?
our ops guy sets up an nginx reverse proxy, and has chef rules to spin up the app by downloading an uberjar from s3 and running it
(along with setting env vars for config)
and he uses the chef UI to control servers
that way we can easily use any build - eg. quick rollback when something goes sour
or even mixed deploys as needed
is there any containerisation happening? i'm about to scale up a big project and was thinking of using ec2 and docker images containing my uberjars
we don’t need containers because we have no deps beside the vm itself and the uberjar
all of our components communicate via JWT which means all the components have to know the same secret. managing and rotating environment variables across multiple containers keeps me up at night. thanks for the info, i'll look into chef UI.
chef might not be the thing we would use if we were starting from scratch - it was grandfathered in from ages ago and got used because otherp arts of the system used it and it was what our ops guy knew
but if it does what you want that’s great 😄
for example elastic beanstalk is very easy to use and more streamlined if all you are doing is putting up jars
There's a guideline for Clojure to avoid having mutable state. However, Clojure core doesn't avoid it when it's convenient (see partition-by implementation, for example).
Does that mean that we should do the same? I'm thinking about using volatile!
to speed up coding (for example, at times I need to have reduce
function which takes previous element value into account).
@ghsgd2 best practice is to make things immutably first, then profile when it’s too slow, and then consider volatiles or even directly mutable data as needed in the bottlenecks
I wouldn’t just throw around optimizations before seeing a need and measuring
@ghsgd2 i think it's okay to work with mutable objects if you're careful. i have an in-browser file system that entirely makes use of (derive) to define file/folder hierarchy because it's much faster than the alternative. then again the folks in #clojure said i was crazy. 😉
@joshkh my main concern is that doing that first, before an immutable version, could be wasting your time (if the immutable version is fast enough, or if something else would be more likely to improve perf)
it’s not crazy to want things to perform well, at all
yes, totally agreed, especially if (like me) you come from an OO background and didn't start with best practices for optimising the immutable parts of clojure
i was so guilty of rebinding the same thing a million different ways in let statements (let [a 1 b 2 a (+ a b)]). that was a hard habit to break.
Is there a reason why (into {} '(2 1))
throws IllegalArgumentException Don't know how to create ISeq from: java.lang.Long
?
I think it’s probably expecting the last argument to be a map, or at least a list of tuples
> list of tuples
> (into {} '((:key 1)))
ClassCastException clojure.lang.Keyword cannot be cast to java.util.Map$Entry
No, that’s not getting it for me either
I even tried quoting the inner list, but no good
It works with vectors, but not seqs
(into {} [[:foo 1]])
=> {:foo 1}
(into {} ['(:key 1)])
ClassCastException clojure.lang.Keyword cannot be cast to java.util.Map$Entry
(into {} [[:foo 1]])
works indeed. Thank you!(into {} '([:foo 1]))
=> {:foo 1}
also worksA two-element vector can often stand in for a MapEntry, but not a two-element list.
Is there a repl function to see more about a specific type similar to (doc function-name)
?
I’m curious as to why lists can’t be cast to map entries. I’m guessing it has something to do with performance
There's specific code in some clojure.core functions that accept vectors with two elements where a MapEntry would otherwise be expected.
Map entries require direct access to the key and value slot. Lists would require traversing the key to get to the value
Hello Everyone. Are there any good resources to learn Clojure quickly and efficiently?
It depends a bit on what your background is.
@drewverlee front end development
(based on what you posted in #clojure another resource to look at is the Clojure Koans https://github.com/functional-koans/clojure-koans )
That's fun and interactive.
@faraj.9798 I would also recommend http://exercism.io/languages/clojure/about (after koans)