This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-06-11
Channels
- # announcements (1)
- # architecture (23)
- # beginners (189)
- # boot (1)
- # calva (62)
- # clj-kondo (23)
- # cljs-dev (9)
- # clojure (336)
- # clojure-dev (11)
- # clojure-europe (2)
- # clojure-italy (17)
- # clojure-nl (25)
- # clojure-uk (53)
- # clojurescript (12)
- # core-async (29)
- # data-science (1)
- # emacs (6)
- # fulcro (23)
- # garden (3)
- # graphql (2)
- # jobs (1)
- # joker (7)
- # off-topic (17)
- # om (2)
- # qlkit (1)
- # reagent (15)
- # reitit (18)
- # rewrite-clj (7)
- # shadow-cljs (176)
- # sql (1)
- # test-check (4)
- # vim (32)
- # xtdb (30)
How would you name boolean defs? For functions that return booleans, we use bar?, but how would you name a let that will receive the result from bar? ?
@hp_pedrors I try to avoid code that let
-binds such results in general 🙂
You should be able to refactor your code to use the predicate in a conditional in just one place so you don't feel the need to let
-bind it.
It might leave me with a method that is something like
(defn update-position [params]
if (logic/position-valid? params)
(if (logic/position-empty? params)
(db/update (board/update-position params))
(return-error "Position not empty")
(return-error "Position not valid")))
Is "empty" a valid position?
Or maybe those are just badly-named predicates?
I would probably not use boolean predicates there. I would have a function that checked the position and returned a map with the position and a flag :invalid
, :empty
, :occupied
and then do
(let [{:keys [status position] (logic/check-position params)]
(case status
:invalid (return-error "Position not valid")
:occupied (return-error "Position not empty")
:empty (db/update (board/update-position position))))
So I have a board an I want to do 2 validations: first, if that position is inside the board (maybe inbouds is better than valid), and second if the position is empty.
I probably would not pass the whole params map to logic/check-position
either -- I'd extract the position from it first and just pass that I think.
Depending on what params
actually is, I'd probably separate the knowledge of that structure out of the pure logic/
stuff and put it somewhere else...? Or if params
is just a map with :x
, :y
for the position, I'd give it a better name 🙂
I have a controller which is doing all these requests to logic. Does it make sense that the controller know that a position can only be updated if it is empty + it is inbounds?
Do you mean a Ring handler? If so, yes, the handler should know how to get the position data out of the request, and then it could ask the logic
part for the status of that position (invalid, empty, occupied) and either present an error to the user or perform the update to put that new piece into the board at that position.
I'm not sure why you have logic/
and board/
tho' -- aren't check-position
and update-position
both just part of the game "model"?
I have a layer between the handler (which I have called service) and the logic, called controller. It is responsible for dealing with the db, and treating these errors, and so on. So basically the flow is the request gets in service-> controller -> logic to validate, and then controller->db is performed accordingly, returning if it was an ok operation.
And board/
?
Ah, OK. Makes sense.
So do you think it is ok for the controller to know which rules to check before updating a position?
Or should the knowing of the rule be inside the logic, and controller could just do logic/try-to-update
or something like that.
I can only say "It depends".
I probably wouldn't have a "controller" layer at all. I'd start out with everything in the handler and refactor if it seemed "too big". I'd probably refactor board-related stuff out of the handler into a something.board
namespace and the db-related stuff into something.storage
. I'd be using Component to manage start/stop lifecycles for anything resource-based, so I'd probably have an :application
key in my Ring request, added by middleware, and in the handler, I'd pull out the database Component to pass into the storage subsystem: (storage/update (-> req :application :database) (board/place-piece position player))
Depending on whether the server is for a single game (and can keep the board in memory) or multiple games, the handler might need to start by fetching the board from the DB?
But, overall, I think it's fine for a handler to contain: * Validate parameters (if needed) * Ask storage system for data (if needed) * Ask model to check business logic (if applicable) * Tell model to perform some business logic * Tell storage system to perform updates (if needed) * Set up error response or success response
If each of those is a single function call, that's about five lines.
If any of that gets more complex, refactor it into helper functions. Over time, if there seem to be a lot of helper functions, especially shared helper functions, refactor those into some other part of the model.
Here's an example handler from one of our apps
(defn update-search-criteria
"Update a member's My Match search criteria."
[req]
(let [result (api-search/validate-search-criteria req)]
(if-let [fail-codes (:fail-codes result)]
(api/invalid-argument req fail-codes)
(let [db-spec (-> req req/config :application :database :pooled-db)
user (-> req :params :user)]
(replace-search-criteria db-spec user (:discovery result))
(get-search-criteria req)))))
5-10 lines, validation and either return an error or call a helper (that interacts with the model and the storage), then return a (successful) result by calling another handler (which returns the member's current search criteria, so the updated results will be returned here).is cognitect.http-client
not meant to be used? I picked it up after looking at its use in the aws-api
project, but it seems lower level for an http-client
I have trouble reading this example of how it's used https://github.com/cognitect-labs/aws-api/blob/90f9726847977a3c785462b81202fed13525cb8f/src/cognitect/aws/client.clj#L63-L88
It's probably specialized for internal use in that app.
@johnjelinek If you're looking for a general purpose HTTP client library, clj-http
is probably the gold standard.
do you like it more than http-kit
(seems to have more stars in github)?
It has a lot more options than http-kit
. We use both at work. I like the simplicity of http-kit
and I like it's async behavior (and how easy it is to cancel an in-flight operation).
so, I should opt for http-kit
then?
We're probably moving toward using http-kit
more and more tho'. clj-http
drags in a lot of dependencies and we've also gotten stuck on 3.4.*
because there's a breaking change in 3.6.*
(at least, breaking for us).
You'll find it easier to get support/help from folks if you use clj-http
because it's much more popular. But you may find http-kit
simpler to use if it satisfies your needs. As with nearly everything in Clojure, there's no "one size fits all" and all library choices have trade offs.
I've started writing a project that reads a file in to a mutable Java Object and ultimately wants to alter it and write out a new file. Is there a standardized way to "immute" it? The only thing I can think to do is to create a representation of the object in clojure literals and then do a translation from file > Object > data > f(data) > f(Object) > f(file)
@mitchell_clojure Is there a reason not to just do file > data > f(data) > file' ?
Well, my initial thought is that someone has taken the time to create a translation layer to Java and that I might be able to leverage that rather than parsing the file myself
it really depends on how complicated the parsing is, and how "mutably" the java does it
but the interface contains basically only methods that will mutate the state of the Object once it is loaded in. I'm not a dev by trade, so this might not be feasible, but I would think there would be a way to call methods on Java Instances from Clojure that just gives you a new Instance of the Object with the changes you wanted
but either way that's the pattern - turn it into data (not objects), transform data, write out data
Thanks guys, the input is appreciated and super helpful as always
@mitchell_clojure That's really going to depend on the specifics of that Java class.
If the class really only exposes read from file, mutate stuff, write to file, then you probably have no way to get at the full representation as data?
Yeah, I have thought about it some more. I think that you can treat methods applied to copies as "functions" in most cases, but sometimes you don't have access to the underlying implementation of the Object
Is it a public class whose documentation you can point us at?
yeah it is powerpoint files as described here https://poi.apache.org/apidocs/4.1/
Oh... I'm so sorry 😐
yeah I think think that I might have to just do the legwork lol
I've only had to use POI a few times and it's always been unpleasant 😞
well, the bright side of it is that this is probably the first time where I have had a situation where I could start a project that is actually useful to some part of the community at large
just want to make sure I get the philosophy of it right, which as I see so far, is either creating a translation layer between a hiccup-like syntax to poi objects or going straight into parsing the file (which I think kind of defeats the clojure philosophy of letting the java people do the hard work)
Hey @mitchell_clojure you could use some inspiration from the various excel libraries that wrap POI. Searching for excel in clojars should give you a few hits.
I’d use a PowerPoint library for sure. Since one doesn’t exist, I’d start by just doing bare interop to cover my needs, then see if some abstraction forms.
Hello
I'm doing the exercise #2 of https://www.braveclojure.com/zombie-metaphysics and got stuck, my atom don't ever change and i have no idea why (are all futures realized, am i using swap!
right, etc, etc)
does anyone have a clue?
Pointers on how to improve the code are also welcomed =D
How do you know your atom never changes? Btw. you create a new atom for every "iteration".
I'd split it up more and fix the "update atom" function. Then you need to actually force the futures to execute and grab the value of the atom. I'd try something like this:
(defn- random-quote-words-counts []
(let [quote (get-random-quote)
values (count-words-on-quote quote)]
values))
(random-quote-words-counts)
;; => {"see" 1, "warm" 1, "it" 2, "is" 1, "like" 1, "you" 1, "friendship" 1, "can" 1, "that" 1, "brings" 1, "on" 1, "but" 1, "peeing" 1, "feeling" 1, "yourself" 1, "only" 1, "get" 1, "everyone" 1, "the" 1}
(defn update-words-counts [atomic-counts new-counts]
(merge-with + atomic-counts new-counts))
(update-words-counts (random-quote-words-counts) {"you" 10})
;; => {"more" 1, "going" 1, "doesn" 1, "garage" 1, "you" 12, "than" 1, "church" 1, "standing" 1, "makes" 1, "any" 1, "make" 1, "a" 3, "t" 1, "christian" 1, "car" 1, "to" 1, "in" 1}
(defn- update-atom
([atomic]
(future (let [words-counts (random-quote-words-counts)]
(swap! atomic update-words-counts words-counts)))))
(defn quote-word-count
([]
(quote-word-count 1))
([n]
(let [my-atom (atom {})
update (partial update-atom my-atom)
;; this is a lazy seq!
futures (take n (repeatedly update))]
(println @my-atom)
;; force futures and wait until they finishes
(run! deref futures)
(println @my-atom))))
(comment
(quote-word-count 5)
;;=> prints
;; {}
;; {happen 1, going 1, his 2, horn 1, s 1, science 1, right 2, made 1, of 1, two 2, annoying 2, not 1, birthday 1, is 2, people 2, you 2, fiction 1, closer 1, those 1, just 1, for 1, screen 1, read 2, my 1, chair 1, recipes 1, already 1, that 1, same 1, go 2, nothing 2, think 1, brakes 1, moved 1, have 3, couldn 1, so 4, interrupting 2, on 2, tvs 1, when 2, t 1, and 1, i 7, one 2, louder 1, big 1, husband 1, talking 2, re 2, well 1, repair 1, your 2, way 1, to 5, get 1, we 1, as 2, end 1, the 3, wanted 1, there 2}
)
Here's the diff: https://github.com/jumarko/clojure-experiments/commit/df59e9876d3b0b3da63c8159b1f98b391a689f61#diff-08543974d7ef27fee63825fb65373185 Note: I didn't really read the instruction or all the code. I've only tried to fix the "missing atom updates" stuff and refactor a bit. However, my "solution" may not be the proper one for the problem you're trying to solve
Based on your code I think I forgot to deref the futures, gonna try it Thanks so much, it gave me a direction
it worked 👏👏👏 🍺
thanks
is there emacs/cider command that does in-ns XY
from the current buffer namespace?
i find myself doing (in-ns NS1)
, and then later (in-ns 'NS2)
in repl especially in bigger projects when i am debugging or doing cross namespace stuff
Just trying to add a file to an existing project as a super noob here, got ....
Exception in thread "main" java.io.FileNotFoundException: Could not locate com/joe/jane/forms/mocro_test__init.class or com/joe/jane/forms/mocro_test.clj on classpath. Please check that namespaces with dashes use underscores in the Clojure file name.
I have a src/../macro.clj and test/../macro_test.clj so I'm not sure what I did wrong... I'm assuming something dumb.
ns from the test...
(ns com.joe.jane.forms.mocro-test
(:require
[clojure.test :refer :all]
[com.joe.jane.forms.macro :refer [=>]]))
what I would do is run a repl, and make sure that you can (require 'com.joe.jane.forms.mocro-test)
-- don't rely on starting jvm's over and over via lein test
you'll catch little errors as you work. Starting things via lein test might give you an error message/stacktrace (of dubious quality) but the error is "dead" -- can't introspect it, poke at it
lein test
's error reporting is not the best sometimes, and it's slow to start that process, which leads to a frustrating iteration cycle
So that require is giving me the filenotfound even though lein test works, am I missing a step?
Yeah, totally appreciating it/accepting/agreeing (tone is hard in text!) just new enough to not be succeeding at doing it >.<
it will take some time to get into the flow of that sort of iterative feedback, but once you get there, it will be a happy place
Got a link to "how to test in repl?" I'm googling with mediocre success
I like the talk that Stuart Halloway gave a couple years ago: https://vimeo.com/223309989
Is there a way to draw available namespaces at the repl? I'm pretty sure I'm just typing some little thing wrong. >.>
it kind of dives into more advanced usage, but just having a REPL open in another window and testing / messing with your code as you write it is way better than re-running lein test
over and over 😅
@suni_masuno did you write this namespace in a file yourself?
(ns com.joe.hane.forms.macro-test
(:require
[clojure.test :refer :all]
[com.joe.jane.forms.macro :refer [=>]]))
(deftest testing-testing
(testing "that I can test things"
(is (= 2 3))))
and
~/work/jane/test/clojure/com/joe/jane/forms/macro_test.clj
So those aren't the real names, though I'll believe another typo... but it works in lein which is weird to me
Apparently I'm just real reliant on tab completion in a cli
and so you’re doing lein repl
inside of the jane
dir and typing (require '[com.joe.jane.forms.macro-test])
?
I was in the wrong folder!
I knew it was something dumb... >.>
It's working #happycry
I have so much to learn about good workflows... -_-
Thank you so much!
How do I tell the repl to update with the latest code as I edit it?
if you’re using cursive or emacs I believe there’s keybindings for that (cursive is shift + alt + L)
Cursive and Emacs both have functions (with hotkeys) for reloading a namespace in a connected REPL
I can't find the cursive binding for that... any idea what it is?
I don’t use Cursive, but if you look up the keybindings in IntelliJ it should be something like “Load file in REPL”. anyone else know?
but if not you can right click, and look for the REPL submenu and it should tell you most of the hotkeys there
:thumbsup: So I'm using vim/terminals but a lot of my team mates were telling me that clojure couldn't do stuff. I am sharing the wisdom
hi, i'm trying to use honeysql and records. But honeysql doesn't take records for sset helper argument, cause it needs array-map. so is there any common method to covert record in array-map or i doing it wrong.
oops, forgive my question, it works
I don’t use Cursive, but if you look up the keybindings in IntelliJ it should be something like “Load file in REPL”. anyone else know?
Ok, so repl testing is apx 1000% better. Now I'm trying to go further, and I think I'm screwing up my quoting.
(defn
retest
[test-location]
(do
(require '[clojure.test :refer [run-tests]])
(require '[test-location] :reload)
(run-tests 'test-location)))
(retest com.joe.jane.forms.macro-test)
Does retest
need to be a macro or...? How do I even?(run-tests 'test-location)
will run any tests that live in (literally) the test-location
namespace (which probably doesn’t exist)
so I would change those two lines to:
(run-tests test-location)
(retest 'com.joe.jane.forms.macro-test)
although - another option might be, if you're working at the repl - open the file with the tests that you want to run, run the editor command to switch the repl to the current namespace and then evaluate (run-tests)
I suppose what I'm really trying to do is get down to (retest 'some-ns)
and it runs that test. So if I could wrap jumping to that ns and run-tests there that's just as good.
Next, still too naive, version of the first approach
(defmacro retest2 [test-location] `(do (require '[clojure.test :refer [run-tests]]) (require '[~test-location] :reload) (run-tests '~test-location)))
(and then my secret dream is to put retest on the User ns when I jump into my or my project's repl so I can use it always and forever)
How am I misunderstanding them?
(pretty sure I am, just wanting to learn to be better)
(require '[test-location] :reload)
won't expand to your test location but to the symbol test-location
so my next attempt...
(defn retest2 [test-location] (do (require '[clojure.test :refer [run-tests]]) (require [test-location] :reload) (run-tests test-location)))
invoked with (retest2 'some-ns)
got it!
Wooo!
Is there something like an RC for the repl that might let me put retest on my repl or my projects whenever it comes up?
Hi! I started learning clojure from haskell. I don't understand a lot of clojure "devops" cycle. How can I install package from github in lein project? And if I can install packages globally?
Could y please help or give a link at least
@altjsus In general, with Leiningen, you should rely on released packaged (which are on http://clojars.org or Maven Central) and you just put the artifact name and version in your :dependencies
vector.
The newer CLI tools (`clj`/`deps.edn`) are designed to let you depend directly on GitHub projects -- but they must be clj
-ready, i.e., they need a pom.xml
or deps.edn
file (there are exceptions to this but...).
So, if you wanted to use https://github.com/dakrone/clj-http you would add [clj-http "3.10.0"]
to your :dependencies
vector in project.clj
for Leiningen, or clj-http {:mvn.version "3.10.0"}
to your deps.edn
file for clj
.
@seancorfield And how are they come together? Woun't it break if I use deps.edn in lein project?
lein
requires project.clj
deps.edn
can be used with Leiningen but you need a plugin declared (in project.clj
) that understands how to read deps.edn
and add those dependencies. If you're just getting started with Clojure, I'd stick to "standard" stuff: lein
+ project.clj
is what most of the books/tutorials show.
If you want to learn about cli
/`deps.edn`, the official Clojure site has this guide https://clojure.org/guides/deps_and_cli
There is a lib I need for the project that only on Github..
Which lib is that?
I wanted to play with specific one.
Ok. I just test if it works and upload to clojure libs repo.
Hmm, that's never been released so you'll have to clone it and you can depend on it locally.
lein install
will put it into your local library cache.
Then you can depend on [airtable "0.1.0-SNAPSHOT"]
How do I install lib globally in clojure?
Not sure what you mean by that. JVM systems use a Maven cache (in .m2/repository
in your home directory).
Generally libraries have to be published to a public repository for you to use them.
You don't "install" a library really. You tell lein
/`clj`/etc that you "depend on" a specific library and version and it will fetch it for you automatically and cache it locally.
And if I want to use one in Repl?
Easiest way is to create a folder with project.clj
in, add the dependency, and then run lein repl
in that folder.
Or use clj
-- which lets you easily specify dependencies on the command line (see link above).
Thnx. I remembet that I wanted to start with clojure couple years ago but configuring and package management scared me that days.
it's weird at first, but it's actually much safer and saner compared to how most tooling does things.
you aren't forced to make all your code use one version - each project can use appropriate versions of a package independently
and if you download a repo, you won't have to wrestle with versioning issues just to work with that code- the lib versions are all specified and specific to that codebase
It's not so bad once you get started. I tend to recommend clj
/`deps.edn` to beginners these days and point them to that guide, but lein
is still a lot more popular because it's been around "forever". It's unfortunate that airtable-clj
never had a release (and maybe the author has abandoned it?).
You could use it via clj
as a GitHub dependency (via :git/url
and :sha
) but you'll need to tell clj
that it's really a deps.edn
project (specifying :manifest :deps
if I recall correctly) and you will need to specify that project's dependencies directly in your deps.edn
file since it doesn't actually have pom.xml
or deps.edn
in the repo 😐
@altjsus If you want to go down the clj
/`deps.edn` path with that library:
(! 699)-> cat deps.edn
{:deps {airtable-clj {:git/url ""
:sha "09721ba5a5ca1d9b60e3ccc087d2e7d453397ee6"
:deps/manifest :deps}
clj-http {:mvn/version "3.8.0"}
cheshire {:mvn/version "5.8.0"}
environ {:mvn/version "1.1.0"}}}
Tue Jun 11 12:21:07
(sean)-(jobs:0)-(~/clojure/airtable)
(! 700)-> clj
Clojure 1.10.1
user=> (require '[airtable-clj.core :as airtable] '[environ.core :refer [env]])
nil
user=>
:deps/manifest
tells clj
to treat the project as if it had an empty deps.edn
file (technically {}
). Then the three dependencies from its project.clj
have to be explicit in your deps.edn
file.
(since I don't have an API key etc, I can't test it any further than that -- looks like it expects you to set up environment variables containing the API key and the base ID)
@altjsus Another option (which works with either lein
or deps.edn
) is https://jitpack.io/.
Basically it pretends to be a normal remote Maven repository, but the identifiers (GAV) of the libraries you reference include sufficient information for jitpack to find them on GitHub, clone them, and build them on demand for you (and then cache the results for subsequent requests).
Here are instructions on how you’d use it to retrieve EvanHahn/airtable-clj
: https://jitpack.io/#EvanHahn/airtable-clj
has anyone used Chef for devops with leiningen in a CentOS machine? since leiningen has no installer