Fork me on GitHub
#clojure
<
2019-01-22
>
hmaurer00:01:26

Hi! Quick question: how can I add a CSS class to an element based on a condition in Hiccup? I tried [:div.foo {:class (if error "has-error")}] but it sets (if error "has-error") as the class of the element ๐Ÿค” @weavejester

weavejester00:01:59

@hmaurer How are you using it?

weavejester00:01:04

user=> (use 'hiccup2.core)
nil
user=> (def error true)
#'user/error
user=> (str (html [:div.foo {:class (if error "has-error")}]))
"<div class=\"foo has-error\"></div>"
user=> (def error false)
#'user/error
user=> (str (html [:div.foo {:class (if error "has-error")}]))
"<div class=\"foo\"></div>"

weavejester00:01:19

It sounds like you're accidentally quoting your code.

hmaurer00:01:14

@weavejester I was not quoting it I think, but i was on v1.0.5 of Hiccup; perhaps that explains it? Trying v2 now ๐Ÿ™‚ Thanks for the prompt reply

weavejester00:01:26

It might have been an issue fixed since then.

hmaurer00:01:04

I just tried it on v2; doesnโ€™t seem to make a difference. Here is how I am using it: https://gist.github.com/hmaurer/5d52b8a5edb625b00e175b9a7404f904#file-render-fn-clj-L50

hmaurer00:01:51

and in the REPL:

user=> (use 'hiccup2.core)
nil
user=> (def error true)
#'user/error
user=> (str (html [:div.foo {:class (if error "has-error")}]))
"<div class=\"foo if error has-error\"></div>"

hmaurer00:01:24

hiccup                       {:mvn/version "2.0.0-alpha1"}

weavejester00:01:24

Ah, it's been fixed in master but not been released.

hmaurer01:01:34

@weavejester ah; I will just point my dependency to master then. Thanks! ๐Ÿ™‚

weavejester01:01:02

Not sure you can without a deps.edn in the repo?

weavejester01:01:08

I'll release a 2.0.0-alpha2

hmaurer01:01:38

Ah I donโ€™t know, first time using deps.edn. I just tried and you are right, I canโ€™t ๐Ÿ™„

seancorfield01:01:57

Given there are no transitive dependencies, you can use it from GitHub with deps.edn -- you have to specify the manifest type as :deps and it will silently ignore the lack of a deps.edn file in the repo.

weavejester01:01:27

Ah, today I learned something ๐Ÿ™‚

seancorfield01:01:20

clj -Sdeps '{:deps {hiccup {:git/url "" :sha "8021043ae0eb64d1a31d87c0fa6071f44bc1c7f3" :deps/manifest :deps}}}'

hmaurer01:01:52

@U04V70XH6 ah, TIL as well. Thanks!

weavejester01:01:13

2.0.0-alpha2 has just been uploaded to Clojars anyway, so you can do it either way.

hmaurer01:01:06

@weavejester Brilliant; just updated the deps; it fixed my issues. Thank you ๐Ÿ™‚ (for live support at 1am)

hmaurer16:01:30

By the way, was this feature simply not supported in v1? (1.0.5). (dynamic classes)

hmaurer17:01:44

@weavejester ^

weavejester18:01:33

No, looks like it was a bug that was only recently fixed. Specifically it's only an issue if you use the dot-notation and supply another class that's calculated dynamically.

hmaurer18:01:07

@weavejester ah, I see, thank you!

chrisulloa01:01:21

Is there anyway, using clojure.zip to replace a node with two new nodes?

(let [btree [45 [10] [57]]
      root-loc (zip/zipper vector? rest
                           (fn [[x _ _] children]
                             (vec (cons x children)))
                           btree)]
  (-> root-loc
      zip/down ;[10]
      (replace-with [11] [12]) ; instead of zip/replace
      zip/root))
;=> [45 [11] [12] [57]]
Using zip/replace I can only swap out in this example [10] with [[11] [12]], which is not what I want because I don't want to add depth.

chrisulloa01:01:17

I can't really see any other way around this besides going up and replacing the children in the parent node.

chrisulloa01:01:09

I see I could replace and possibly insert left or right, will try that next.

hiredman01:01:45

the way a zipper works, you can think of replace as a mapping operation, you are a particular node, an you map (constantly replacement-value) over the value of that node, what you are talking about would be mapcat

hiredman01:01:24

in theory maybe possible, but there are kind of edge cases, like if are visiting node N, and you replace it with nodes N1 and N2, which are node are now visiting? you can make a choice, but there is no single "right" answer there

chrisulloa01:01:55

Yeah maybe my particular case warrants a new replace with function, like you said no right answer

chrisulloa01:01:00

I really want to split a node in two

chrisulloa01:01:36

I don't really care which node i'm visiting because i zip the tree right after

hiredman01:01:40

I think you have to do it from the parent

chrisulloa01:01:47

Combining zip/replace with insert-right works... since i don't really care if it goes left or right in my case:

(defn replace-with
  [loc node1 node2]
    (let [[x {r :r :as path}] loc]
      (with-meta [node1 (assoc path :r (cons node2 r) :changed? true)] (meta loc))))

chrisulloa01:01:55

This seems to work

hiredman01:01:57

I mean, if you don't want to dig in to the internals of zippers and figure out how to mapcat

chrisulloa01:01:50

These are the originals:

(defn insert-right
  [loc item]
    (let [[node {r :r :as path}] loc]
      (if (nil? path)
        (throw (new Exception "Insert at top"))
        (with-meta [node (assoc path :r (cons item r) :changed? true)] (meta loc)))))

(defn replace
  [loc node]
    (let [[_ path] loc]
      (with-meta [node (assoc path :changed? true)] (meta loc))))

hiredman01:01:51

(which I guess you did)

chrisulloa01:01:26

I was just hoping I maybe missed something but it helped talking to you lol

chrisulloa01:01:01

The only reason I didn't want to do it from the parent is because I did not want to go through at worst n many children to find and replace it with two new children

roklenarcic09:01:55

What is the easiest way to achieve the following: I want to split the sequence after the element for which a predicate is true.

gon09:01:46

you can use split-with

roklenarcic09:01:32

split with will split before the element

roklenarcic09:01:16

Like suppose you have [1 11 8 -3 9 33] and you want [1 11 8 -3] and [9 33], based on predicate neg?.

val_waeselynck09:01:05

Maybe combine split-with with (partition 2 1 ...)

gon09:01:41

Something like (map #(apply concat %1) (partition-all 2 (partition-by neg? [1 11 8 -3 9 33 -5 6 7])))

roklenarcic09:01:07

yes that's what I ended up with

enforser15:01:58

looks like something that https://clojuredocs.org/clojure.core/split-with could be useful for

Empperi14:01:20

I'm facing a very weird problem with require. For some reason, when I'm saying (require my-ns-var :refresh) the changes are not applied to that namespace. I'm pretty confident my logic is solid but I'm just missing some intricate detail about require here. Just for reference: this is about reloading changed Garden CSS declarations within my REPL

Empperi14:01:59

Problem manifests itself when I have structure main style-ns / page style-ns / component style-ns <- change to last one

Empperi14:01:31

I'm requiring the files in correct order, meaning that I first require namespaces which have no dependencies and from there I progress towards the main style-ns

Empperi14:01:51

But for some strange reason those changes are not applied

Empperi14:01:37

If however I go and reload those namespace manually within REPL in that same order the changes are applied

alexmiller14:01:20

:refresh is not a thing - you want :reload

Empperi14:01:36

Ah sorry about that. Using :reload

Empperi14:01:03

So as much as I would have hoped that would have been my problem but alas it isn't ๐Ÿ˜ž

alexmiller14:01:07

you could also try :reload-all (which also reloads dependent namespaces)

Empperi14:01:24

Yeah, tried that. Only thing that changes is the CSS compilation time since requiring takes more time. No help

Empperi14:01:49

I'm just using normal Clojure vars here, nothing special really

alexmiller14:01:09

I donโ€™t know anything about garden, but state has to live somewhere, typically in a var, so Iโ€™d investigate where Garden state is getting cached

Empperi14:01:13

defs referring to other defs

Empperi14:01:34

Garden just takes a bunch of Clojure data structures in and spits out a string of CSS

Empperi14:01:57

I've debugged this so far that the actual data structures do not update

Empperi14:01:11

So the bug happens way before Garden steps into play

Empperi14:01:56

But apparently I'm not missing something special require magic here that I should be aware of

alexmiller14:01:07

not much other magic to know

Empperi14:01:35

Yeah. I actually also tried to use remove-ns before requiring but that proved to cause all kinds of nasty problems

alexmiller14:01:40

other places where state hides in the runtime are in multimethods (dispatch table) and protocols

Empperi14:01:09

I'm wondering if I'm hitting garbage collection somehow

Empperi14:01:28

Meaning, my old vars are left dangling and old references are still there for some reason

Empperi14:01:01

It's really strange since if I go and reload all namespace in order manually it works

Empperi14:01:07

But not if I do the same via require

Empperi14:01:36

(manually meaning via Cursive "load namespace")

Empperi14:01:32

Ahem... Ok. Got it working.

Empperi14:01:42

Now using :reload-all

Empperi14:01:46

I restarted my REPL...

Empperi14:01:04

Still confused what caused all this

Empperi14:01:15

I had tried restarting my REPL previously

Empperi14:01:50

I really need to dig in deeper into this at some point but I've wasted too much time already

Empperi14:01:59

Thanks for ideas!

magnusdk15:01:55

Hi! Iโ€™ve encountered an issue when a protocol is reloaded causing the implementing objects to no longer satisfies? the protocol. Example:

(defprotocol A (foo [_]))
(def a (reify A (foo [_])))

(prn (satisfies? A a))        ;; => true
(defprotocol A (foo [_]))
(prn (satisfies? A a))        ;; => false
Is there any way to specify that the protocol object should not be recreated when reloading namespaces? Sort of like defonce does

alexmiller15:01:18

In short, no

borkdude15:01:38

but you can wrap it in a defonce:

(defonce do-once (defprotocol ...))

magnusdk16:01:42

Thanks for your answers :slightly_smiling_face: It looks like this wasn't the cause of the problem after all, the protocol is only loaded once. The object that implements the protocol is loaded twice โ€“ why that would break things is very mysterious :man-shrugging: _edit: &lt;--- I now realize this doesn't make that much sense in the context of my example, sorry :sweat_smile:_

hiredman17:01:53

my guess would be you are aot compiling and somewhere you are importing the class created by a defrecord without requiring the namespace that defines it, so the import is loading the old aot compiled version

magnusdk17:01:27

I think you are onto something with regards to aot. I will post the minimum code that breaks lein uberjar in thread.

magnusdk17:01:50

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; In protocol namespace
(ns protocol)

(defprotocol A
  (foo [_]))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; In implementation namespace
(ns implementation
  (:require [protocol :as proto]))

(def a
  (reify proto/A
    (foo [_] ":)")))

(println "I get printed twice!")
(assert (satisfies? proto/A a) "I fail the second time")                        


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; In our core namespace
(ns core
  (:require [implementation]))

magnusdk17:01:42

But this works fine in a fresh project though, so it must be something that's configured in my company's project

hiredman17:01:28

what are you doing when it fails? running tests?

magnusdk17:01:39

Running lein uberjar (compiling)

hiredman17:01:42

if you see that printed twice, I would look for a require with :reload or :reload-all

magnusdk17:01:57

There are no :reloads or :reload-alls in our project

magnusdk17:01:19

Using defonce instead of def fixes it:

(defonce a
  (reify proto/A
    (foo [_] ":)")))

hiredman17:01:07

it gets rid of the symptom, but there is still something wrong somewhere

hiredman17:01:46

when was the last time you ran lein clean?

magnusdk17:01:03

Unsure, but this happens when building the app on AWS as well. I'll try to do a lein clean before uberjar locally just in case update: same error

hiredman17:01:33

do you have a user.clj or some plugin or something that loads your code?

magnusdk17:01:06

We do have user.clj and we use ring.middleware.reload/wrap-reload, but these should only be enabled in dev

hiredman17:01:52

I bet one of those is the issue

hiredman17:01:31

aot compilation must load the code, so if you load it first, then ask it to be compiled you will end up loading it twice (which breaks things)

magnusdk17:01:47

That makes a lot of sense. Thanks ๐Ÿ™‚ I will have to look more into this tomorrow as it is getting late.

magnusdk17:01:17

I have a good feeling about looking into the aot stuff, thanks again!

restenb20:01:35

hm. is there some kind of practical difference between seq and lazyseq that i'm missing?

restenb20:01:59

sorry, lazy-seq. to me it looks like they're basically equivalent

hiredman21:01:19

seq is for consuming seqs from things that are seq'able, lazy-seq is for constructing lazy seqs recursively

caleb.macdonaldblack23:01:25

Do any libraries exist where I can do collection transformations like (map, filter, reverse, rest, ect) but it keeps the type of collection

caleb.macdonaldblack23:01:15

I know about mapv and filterv but more would be awesome.

dpsutton23:01:37

that sounds like one of the core tenants of specter

caleb.macdonaldblack23:01:19

I was thinking more of a library with heaps of sequence functions that would instead return vectors for example.

noisesmith23:01:44

for clojure's built in datatypes other than seq, you can use (into (empty coll) (filter f) coll) etc.