Fork me on GitHub
#clojure
<
2018-09-04
>
andrea.crotti10:09:24

does anyone knows of good examples of apps that support oauth2 authentication (for example with Github)

andrea.crotti10:09:11

I'm trying to add authentication to a project of mine and wanted to get some ideas for example about how to structure the database, how to use it with Ring etc etc

jmromrell13:09:28

I've been wrestling with how to implement a new Exception subclass using AOT compilation to make it accessible from both clojure and as a Java library. Am I overlooking something?

jmromrell13:09:34

(ns execution-tracing.execution-id-too-long-exception
  (:require [execution-tracing.core :refer [maximum-id-length]])
  (:gen-class
    :name executiontracing.ExecutionIDTooLongException
    :extends IllegalStateException
    :state "id"
    :init "init"
    :constructors {[String] []}
    :methods [[getMessage [] String]]))

(defn -init [id] [[] id])

(defn -getMessage [id]
  (str "Execution ID exceeds maximum length of " @maximum-id-length ":" id))

jmromrell13:09:22

I'm currently getting java.lang.ClassNotFoundException: executiontracing.ExecutionIDTooLongException, compiling:(core.clj:1:1) whenever I attempt to start a repl in this project.

Alex Miller (Clojure team)15:09:17

The best way to do this is to write it in Java

jmromrell15:09:52

Is it not possible to do via Clojure? 😕

bronsa15:09:04

it is possible but often not worth the hassle

lilactown15:09:18

does an ns declaration with (:gen-class) even generate a class while REPL-ing?

lilactown15:09:25

I thought it required AOT compilation

bronsa15:09:47

it doesn't generate a class while JITing indeed

bronsa15:09:56

that's why it's failing for @jmromrell

bronsa15:09:01

gen-class requires AOT

jmromrell15:09:14

So, assuming I am wanting to keep this library in Clojure, the solution is to publish a Java library containing only the exception I need, and then depend on that? Or is there a way I can directly package the Java .class or something with the clojure library?

bronsa15:09:47

you can use lein javac

bronsa15:09:00

and package the java file with your clojure lib

bronsa15:09:34

or, if possible, you should consider not subclassing exceptions and using ex-info instead

jmromrell15:09:46

All right, that isn't too ugly, at least. I might poke around and see if I can get the project to work as-is if I define the exception class in Clojure as well as using gen-class (potentially fix the current resolution error via clojure repl, while still exposing a definition that can be used by Java library consumers)

lilactown16:09:07

I have kind of a silly question. I recently started using datomic, and everything is wonderful… except that I end up doing stuff like this:

(-> (d/q '[ ... query that should only return one value ... ] (d/db dconn))
    (first)
    (first))

lilactown16:09:20

is there some idiom for getting the value out of a query like that?

kirill.salykin16:09:16

ffirst or datomic Scalar value (ends with .)

👏 4
lilactown16:09:43

great! thanks 😄

eccentric J17:09:21

Hello there, I’m writing a for-fun app to send me slack cat gifs at a random time between 12pm and 5pm. In so far I’ve got a channel emitting the current date every second but I’m curious if there’s a better way to model this logic with core.async.

ghadi17:09:32

@jayzawrotny one method is having a function that output values at random times onto a channel:

(defn randomly [outc]
  (go-loop []
    (<! (timeout (rand-int PERIOD)))
   (>! outc :tick)
  (recur)))
Then you'd have a consumer of that channel that would only send a cat gif if the time was in bounds

eccentric J17:09:12

Ah that makes sense! Though I’m not sure I fully understand how your notion of periods\integers would work with that. Perhaps generate a random timestamp then calc the difference between that target and now to get the number of ms to set the timeout for?

ghadi17:09:16

the process is going to sleep for an indeterminate amount of time (0 < x < PERIOD)

ghadi17:09:37

no need for timestamp calculation, IMHO- but you can certainly calc to get more specific

eccentric J17:09:47

I think I see, however the value of PERIOD depends on when randomly func is called doesn’t it?

ghadi17:09:27

PERIOD should have been a parameter 😉 I was pseudocoding. randomly would only be called once and would run forever in the background

eccentric J17:09:25

So period would hypothetically be the number of ms between 18 - 24 hours from now?

ghadi17:09:32

(let [ticks (chan some-buffer)]
  (randomly (* 60 1000 10) ticks)
  (cat-gifs ticks))

ghadi17:09:53

period would be the max duration between cats

ghadi17:09:36

yeah it would be the number of millis between ticks, on average

ghadi17:09:43

"pulses" of the trigger

ghadi17:09:05

(there are other ways of approaching this problem)

eccentric J17:09:31

Cool, I gotcha. Agreed, I’m very interested in how other devs would solve this while keeping the trade-offs in mind 🙂

eccentric J17:09:44

I think yours is a very elegant solution but my understanding is that because it’s interval based the channel would need to be started within the range of the target time for it to hit the correct time window. Am I missing something?

ghadi17:09:56

in my book it is important to keep the stimuli (the pulses) and the action based on the stimulus (sending a cat gif) separate

ghadi17:09:44

I think you're missing that i'm suggesting deferring the determination of the correct time window to the consumer of all the pulses

ghadi17:09:03

ignore/drop a pulse if it's outside the time window

eccentric J17:09:45

Ohhh!!! I see now, yeah that could be way more robust!

ghadi17:09:45

having the separation between stimulus / action can help iterate on both sides of the problem

ghadi17:09:10

you could improve the consumption of the pulses... or produce more specific pulses

ghadi17:09:05

without core.async

eccentric J17:09:06

And the pulses are more general, in which a consumer can check the time against the target window (or w\e conditional) and send the next gif.

ghadi17:09:36

the pulse itself isn't meaningful, just a value :tick

eccentric J17:09:50

In which case, why randomize the pulses?

ghadi17:09:30

you don't have to. s/randomly/periodically

ghadi17:09:50

another example of creating a separation and iterating on both sides ^

eccentric J17:09:42

Thanks, I’m looking at it now. The randomness would ensure that the times a cat gif is sent isn’t too patterned and predictable.

eccentric J17:09:31

Ah yeah the queue scheduler could work too

eccentric J17:09:04

Is there a more clojure idiomatic way to work with the scheduler queue or would it be basic java interop with (shceduler\scheduleAtFixedRate time-test-fn, 0, PERIOD, scheduler/SECONDS)?

ghadi17:09:57

basic java interop

eccentric J18:09:01

The goal is really to learn with this project so may as well try a few of these designs and see what works the best. Thanks again for the help

kwladyka18:09:45

When / why use deps.edn? Can you recommend article to read which explain details in friendly way?

aarkerio18:09:51

as far as I know, CLI tools are more limited than lein/boot options.

noisesmith18:09:38

right, deps.edn cli is only for dep resolution, it doesn't do project or build features and doesn't have a plugin system (but people have tried to add extensions that build those features over in - they will never be part of the tool itself though)

kwladyka18:09:53

So why / when to use them?

noisesmith18:09:19

if all you need is to pull in dependencies and you don't need to build, deploy, package, add dev tooling, etc.

bja18:09:51

deps.edn is useful on the cli for test cases and sample code

noisesmith18:09:00

(or if you prefer the ad-hoc system being built around deps.edn rather than a unified tool)

bja18:09:09

No longer need English instructions or a full project.clj for a runnable example

kwladyka18:09:16

So… it sounds like it could be useful only for libraries? Like CLJJS wrappers

kwladyka18:09:40

I don’t feel it 🙂

kwladyka18:09:04

https://clojurescript.org/guides/webpack is it possible to not use deps.edn in this example? Instead use project.clj? I am trying to run it, but I have error:

aused by: clojure.lang.ExceptionInfo: No such namespace: reagent.core, could not locate reagent/core.cljs, reagent/core.cljc, or JavaScript source providing "reagent.core" in file ...

kwladyka18:09:29

well actually it happen during clj -m cljs.main -co build.edn -v -c which is something new for me

aarkerio18:09:29

if you are trying to do web development with Clojure(script), my recommendation is to start with Luminus

aarkerio18:09:47

it will give you a full app skeleton

kwladyka18:09:05

I want to use npm dependencies. Hmm probably this part I should discuss on #clojurescript 🙂

bhauman18:09:17

@kwladyka that’s what I was about to suggest

kwladyka18:09:08

I mean this part about not use deps.end, but project.clj instead

aarkerio18:09:21

I'm developing a Web app , this my project file, maybe could be useful: https://github.com/aarkerio/ZentaurLMS/blob/development/project.clj

bhauman18:09:51

@kwladyka so you can use project.clj

bhauman18:09:07

but you are going to have to create an alias

bhauman18:09:22

to run cljs.main

bhauman18:09:30

does that make sense?

bhauman18:09:48

project.clj will handle setting up your dependencies

kwladyka18:09:49

yes and no, I don’t get it why in this example Nolen use deps.edn

kwladyka18:09:40

clojurescript is obviously dependency when writing cljs code in project.clj / shadow-cljs.edn

bhauman18:09:41

@kwladyka :aliases {"cljs" ["run" "-m" "cljs.main" "-co" "build-edn" "-c"]}

bhauman18:09:29

@kwladyka I think you are missing some major things

kwladyka18:09:37

I am sure about it! 🙂

bhauman18:09:46

deps.edn specifies your dependencies

bhauman18:09:01

clojurescript is one of your dependencies in this case

bhauman18:09:32

the guide plainly specifies a deps.edn file

bhauman18:09:58

wether or not it should be their is a much bigger topic

bhauman18:09:18

if you have other dependencies they also have to be in the deps.edn as well

bhauman18:09:36

clj does not pick up your dependencies from your project.clj

kwladyka18:09:46

yes, but assumption is everybody will use project.clj / shadow-cljs.edn in project anyway. So why this deps.edn file? What a value for that? Is it necessary from technical point of view? Why not remove this deps.edn?

bhauman18:09:04

everyone does not use project.clj

lilactown18:09:08

people use deps.edn instead of project.clj @kwladyka

bhauman18:09:09

and shadow

lilactown18:09:16

deps.edn replaces project.clj

kwladyka18:09:47

why deps.edn should replace project.clj?

lilactown18:09:54

if you are using leiningen, then keep using project.clj If you are are using the new clj command-line tools, you would use deps.edn

bhauman18:09:54

you can have a fabulous fast clojurescript workflow with out leiningen

kwladyka18:09:26

hmm I think I really miss something in Clojure community about this deps.edn

kwladyka18:09:33

So while you use deps.edn how do you compile your code? by lein / shadow? What is the point then?

kwladyka18:09:26

Can you recommend something to read? I didn’t find anything which explain it for me.

ghadi18:09:35

https://github.com/clojure/tools.deps.alpha#rationale -- there are a few links there, I'd start with the Guide

ghadi18:09:50

one of the strategic moves that tools.deps + the clj tools unlock is Non-Artifact Based Development. Instead of having to bundle everything into a Jar / artifact and deploy that, then bump dependencies, you can refer to a git SHA instead

ghadi18:09:16

comparing lein vs deps.edn isn't apples to oranges, lein is like a swiss army knife. deps.edn Sets Up a JVM classpath and launches a JVM... that's it.

ghadi18:09:58

so -- clojurescript compilation isn't something that is in scope for clj/deps.edn, but setting up an environment where you could compile clojurescript definitely is

kwladyka18:09:56

Hmm so what was the intention to make deps.edn? git compatibility?

noisesmith18:09:23

to make sure people could just start up clojure 1.9 without a project manager

noisesmith18:09:30

since it depends on another jar

ghadi18:09:56

that, too.

ghadi18:09:14

lein's dependency / profile handling is... weird, to say the least

ghadi18:09:39

but yeah, moving beyond maven, clojure 1.9 requires multiple packages, etc.

kwladyka18:09:16

What about all lein plugins / solutions etc.? What do we have with deps.edn? Is it something what we should use in new project over lein?

ghadi18:09:32

Out_Of_Scope

kwladyka18:09:08

hmm and what with build.edn https://clojurescript.org/guides/webpack . I see it is supported by this new solution. So it also can compile code.

kwladyka18:09:50

*new solution clj instead lein

andy.fingerhut18:09:53

clj is not intended to be a replacement for Leiningen or Boot. I am not familiar with all of them, but people are developing programs that can create the dependencies part of a Leiningen project.clj file from a deps.edn file (corrections welcome on the details there, since I haven't used them) and maybe also the other direction. clj allows you to specify dependencies that Leiningen cannot, e.g. to specific versions of source code in a Github repo.

kwladyka19:09:50

Can I simplify it to this sentence? deps.edn is designed to manage dependencies and this is the right place for it. It should be there over lein / boot / shadow-cljs. But other things like how to compile the code etc. should be in lein / boot / shadow-cljs? The intention is to make Clojure dependency manager independent from other solutions. Split dependencies from tools / compile the code.

kwladyka19:09:03

Ok it is longer sentence than I expected 😉

kwladyka19:09:37

So should we prefer to use deps.edn over deps in lein?

seancorfield19:09:30

@kwladyka I'll also offer an observation that some Clojure shops (including where I work) had elected to put dependencies in external EDN files and use version overrides, via custom machinery prior to deps.edn / clj -- we happened to use Boot but I've heard of folks doing it with Leiningen as well (it was more common with Boot tho' due to how programmatic it is, compared to Leiningen).

seancorfield19:09:18

There are plugins/tasks for both Leiningen and Boot that let you use deps.edn (and tools.deps.alpha) to provide additional functionality on top of clj's core abilities.

seancorfield19:09:19

I think for any new project I was starting, I would go with deps.edn and clj plus associated tooling (like depstar maybe)... and only go to Boot (or Leiningen) for a task that was outside that ecosystem (although I can think of anything right now -- unless I still needed to conform to the deploy-to-Clojars model for some reason, rather than "encouraging" folks to use GitHub as a dependency).

andy.fingerhut19:09:36

@kwladyka I think people might hesitate to tell everyone else that they should prefer to use deps.edn over deps in lein. If someone is happily using Leiningen and it meets all of their needs with no changes, why would I presume to tell them they should change that?

lilactown19:09:29

deps.edn made a hell of a lot more sense when I started using Datomic ions

john19:09:45

@kwladyka also some more context, there's a "basic repl" provided by "cljs.main" described in the new cljs quickstart. This setup sits on top of the new tools.deps infrastructure. Most people will end up using figwheel.main or shadow, sitting on top of the new tools.deps infrastructure. However, when someone from the core team produces a new guide or tutorial, more often then not, you'll see them built on top of the simpler, generic "cljs.main" basic repl. This is so that the core team can focus on fixing problems related to core, rather than the (hopefully) thousands of tools that want to build on top of it.

john19:09:52

So, if you want to use the webpack tutorial, for instance, you're going to have to translate that to your tool of choice (which will hopefully be an expanding field of choices)

john19:09:31

(or not, some folks think too many choices is bad)

seancorfield19:09:23

I think we don't need any more "comprehensive" build tools -- Leiningen and Boot are more than enough -- but I think there's plenty of room for small/narrow, focused tooling, built on top of deps.edn etc. I think the new tools we see here https://github.com/clojure/tools.deps.alpha/wiki/Tools is encouraging.

👌 4
roklenarcic20:09:02

what would be an elegant way to split a vector into two vectors, one containing the even-index items other odd-index items?

nenadalm20:09:11

((juxt #(take-nth 2 %)
       #(take-nth 2 (rest %))) input)

nenadalm20:09:54

(sequences instead of vector)

nenadalm20:09:39

version with vectors:

(def input [1 2 3 4 5 6 8])

(defn every-secondv [coll]
  (vec (take-nth 2 coll)))

((juxt every-secondv
       (comp every-secondv rest)) input)

nenadalm20:09:55

version with transducers:

[(into [] (take-nth 2) input)
 (into [] (take-nth 2) (rest input))]

dpsutton20:09:33

i always forget about take-nth. this has my vote

ghadi21:09:38

juxt is overkill in this scenario

ghadi21:09:58

I find juxt is useful when you're composing the function and handing it elsewhere -- if the result of juxt gets immediately applied it almost seems like you're trying to use the juxt function simple_smile

nenadalm04:09:39

sure. I am just reading just juxt every day and cannot find good fit for it - so trying it everywhere I can 🙂

roklenarcic20:09:47

so far I have partition 2 followed by mapv first and mapv second.

john20:09:30

`(vals (group-by odd? (range 10)))` (I have a reading comprehension problem 😆)

dpsutton20:09:38

@roklenarcic be aware of how partition works when it can no longer fill up an entire partitioner

dpsutton20:09:05

(partition 2 [:a])

roklenarcic20:09:24

I check for that explicitly

roklenarcic20:09:15

another solution that doesn't involve making a new vector but rather sequences

roklenarcic20:09:46

is to use range with step of 2 and nth

kwladyka20:09:33

thank all of you for answers 🙂 🍻

tristefigure22:09:29

As anyone tried hyper-deep code-walking ? A typical code walking situation is that of a macro that accepts arbitrary code and performs transformations at arbitrary depth in its tree-like structure. Now imagine that additionally to this the macro figures out which functions are called, get their source code, deep-change it so as to define clones, and so on. Is this a thing in Lisp ?

noisesmith22:09:15

so treat every function as a macro?

noisesmith22:09:00

using tools.reader as a basis (either to use directly or fork of off) I am sure it could be done

andy.fingerhut22:09:50

The core.async library does transformations at arbitrary depths of the tree-like structure of code within single functions. I am pretty sure it does not do so across function boundaries.

noisesmith22:09:43

@andy.fingerhut does it do this by defining its channel ops as macros, or is there a deeper magic going on?

csm22:09:36

how does one produce a quoted form inside a macro? Specifically I’m trying to embed a Datomic query (`'[:find ?foo ...]`) in a macro, but I’m doing it wrong

csm22:09:58

hmm, (quote ~'form) worked. Is that correct?

noisesmith22:09:49

as a literal or something passed in?

csm23:09:59

a literal, literally [:find ?stuff ...]

noisesmith23:09:56

in that case (quote form) should suffice

csm23:09:29

didn’t seem to; the meta-vars ?stuff for datomic turned into the.ns/?stuff

noisesmith23:09:49

oh, inside backtick, that makes it harder, yes

noisesmith23:09:18

so yeah, yo want ~' on each of those symbol literals, or once on the whole form

csm23:09:31

hmm, so this works too: '~'[:find ?blah ..]

noisesmith23:09:40

right, 'foo and (quote foo) are always equivalent

noisesmith23:09:19

the reader does that transformation when consuming your code

john23:09:06

@noisesmith I think core.async is adding each form to a stack, sorta like a promise chain

noisesmith23:09:56

as far as I know it only does that for the channel ops though - but I guess that would mean that instead of expanding the forms it does a transform on that line and the ones following when it finds them

hiredman23:09:25

core.async's go macro operates like a compiler for a lisp, it is just that the source language and the target language are very similar. it does macro expansion (using the macro expander from tools.analyzer) and then code generation

hiredman23:09:00

the channel ops like !> and <! are special forms in the source language that the compiler knows how to generate code for

hiredman23:09:00

the macro expansion is fairly vanilla, and pretty much the least interesting part of the thing

john23:09:27

Is it every line or do you consecutive lines get bagged up until we reach a transforming function?

hiredman23:09:20

after the reader reads, you have datastructures, there are no lines anymore

john23:09:39

yeah, I mean consecutive forms/structures

hiredman23:09:17

the go macro only operates on the arguments to the go macro

andy.fingerhut23:09:59

And the arguments to the go macro can span pages of code, if one were so inclined to write code like that.

john23:09:06

So every form unwinds due to macro expansion, then they all become args to the go macro?

hiredman23:09:46

the go macro takes a & body

hiredman23:09:25

so it gets a datastructure (seq) of whatever datastructures the reader created

john23:09:40

I'm guess it collects forms until it hits a blocking op, then shelfs all collected forms in a closure, throws it on the stack, then starts collecting again

hiredman23:09:03

clojure macros are arbitrary functions, so they can do whatever even "hyper-deep code-walking" whatever that is, but macros are purely about transforming syntax. many people new to macros think "oh, I have this hand wavy thing I haven't defined, maybe macros will help". It never does. Macros can give you a nice syntax on top of whatever semantics, but you have to come up with and have a good set of semantics and know how those semantics are represented without macros first.

hiredman23:09:44

sort of, but actually all the code ends up a single closure in a case expression

hiredman23:09:46

the transform for the go macro is actually very similar to anf

dpsutton23:09:56

Tim has some amazing videos on YouTube about these macros