This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-09-25
Channels
- # asami (5)
- # babashka (38)
- # beginners (116)
- # calva (65)
- # chlorine-clover (4)
- # cider (8)
- # cljfx (6)
- # cljsrn (7)
- # clojure (18)
- # clojure-australia (1)
- # clojure-europe (49)
- # clojure-spec (6)
- # clojure-uk (4)
- # clojurescript (3)
- # clojureverse-ops (13)
- # core-async (2)
- # cryogen (1)
- # cursive (9)
- # datahike (32)
- # fulcro (3)
- # gratitude (17)
- # malli (4)
- # music (3)
- # nrepl (8)
- # off-topic (14)
- # releases (2)
- # rewrite-clj (3)
- # shadow-cljs (4)
- # vrac (1)
- # xtdb (4)
I'm trying to implement the repository pattern in Clojure but not sure how. Could someone please shed some light?
Clojure's hash-map together with get, assoc, dissoc and update functions can be used directly to mimic repository pattern. Do you need some specific logic that is not possible to express using it?
The repository pattern I'm referring to is the one which has to do with having an interface with all the possible persistence operations for a given entity. In Golang for example:
type UserRepository interface {
Create(user User)
Get(id int)
Update(id int, user User)
Delete(id int)
}
I might have 2 different implementations for the same repository interface, e.g. one which stores users in memory and another which uses a relational database.
(ns proj.persistence.user)
(defn create [db user]
...)
(defn get [db id]
...)
(defn update [db id user]
...)
(defn delete [db id]
...)
just a namespace that directly has the logic for performing this stuff given a relational database
and then from there if you want to truly have a part of the code where you don't want to pass the db connection
(defprotocol UserRespository
(create [_ user]
...)
(get [_ id]
...)
(update [_ id user]
...)
(delete [_ id]
...))
you can make a protocol and then an instance by using partial
on the namespace functions
(defn from-db [db]
(reify UserRespository
(create [_ user]
...)
(get [_ id]
...)
(update [_ id user]
...)
(delete [_ id]
...)))
(ns proj.persistence.user)
(defn create [db user]
...)
(defn get [db id]
...)
(defn update [db id user]
...)
(defn delete [db id]
...)
(ns proj.persistence.user)
(defprotocol UserRespository
(create* [_ user]
...)
(get* [_ id]
...)
(update* [_ id user]
...)
(delete* [_ id]
...))
(defn create [db user]
(create* db user))
(defn get [db id]
(get* db id))
(defn update [db id user]
(update* db id user))
(defn delete [db id]
(delete* db id))
(ns proj.persistence.user)
(defprotocol UserRespository
(create* [_ user])
(get* [_ id])
(update* [_ id user])
(delete* [_ id]))
(defn create [db user]
(create* db user))
(defn get [db id]
(get* db id))
(defn update [db id user]
(update* db id user))
(defn delete [db id]
(delete* db id))
(extend-protocol UserRepository
Datasource
...)
but the important thing, at least to me, is that you don't need to start making it extensible. Your public interface doesn't need to change (`(proj.persistence.user/get db 123)`) when you have that requirement
@U3JH98J4R Thanks you very much for the detailed explanation! I'll try to start simple without a protocol and see how it goes from there
Screw all that, just write normal functions inside a namespace. You don't need anything more.
hi! when I have a map with namespaced keys, is there some function that will change it into keys without namespace?
The newest alpha of Clojure has a function update-keys
(also easy to write one yourself), which combined with (comp keyword name)
will do what you want
(there's qualified-keyword?
if your map has keys that are not all keywords and you want to check before trying to change those)
or:
(reduce-kv
(fn [m k v] (assoc m (-> k name keyword) v))
{}
#:user{:name "John" :age 17})
I have written a function with if
which has 200 lines of code a year ago , As part of the enhancement now I have to put i if
else
condition in multiple lines and many in future, code is becoming ugly and not able to maintain uniformity in it, How can I handle such multiple if
condition better in clojure ? any suggestion ? Any design pattern suggestions ?
after adding new if else condition , need to write 10 lines in both the if and else block!
is there a trim-to-nil function in the core libs? (if string has only whitespace, then return nil otherwise trim and return the contents)
No. But you can check the result of clojure.string/trim
with clojure.string/blank?
I'm curious if other languages have a trim-to-nil and how it's used.
https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/StringUtils.html
You can define a function like
(let [s (str/trim ...)]
(when-not (str/blank? s)
s))
Won't blank check already have you covered there? Also, on older jdks strings have a isBlank method
You're right, I didn't remember that str/blank?
also returns true if a string contains only whitespace. Then you save potentially trimming a blank string by checking str/blank?
before trimming
Does anyone have any ideas on how to help me with this π¬ Posting here & on stack overflow to maximize potential solutions https://stackoverflow.com/questions/69328970/why-isnt-my-luminus-app-working-on-heroku
I have the same book but not there at chap9 yet. Do you have your database config defined anywhere for production build?
Top of page 342 says this "To actually run the app in production, all you need to do is replace dev-config.edn with the appropriate filename. By convention, prod-config.edn is usually used."
The [dev-|test-|prod-]config.edn are not in git by default but you need to create them.
If you create even an empty file prod-config.edn with just "{}" in it the error message will probably be different.
Wow thanks. I had no idea that [dev-|test-|prod-]config.edn are not in git, so I was really confused why those files weren't doing anything or being read. I'll try creating prod-config.edn now and update
Unfortunately I get the same error, strangely enough... @UP82LQR9N . I created prod-config.edn, then did git add ., git commit, git push and everything went okay on that end, but heroku run lein run migrate
gives the same error saying it can't find the config.edn file, and I see the same errors show up in the webpage :thinking_face:
Ok do you think I should try adding JVM_OPTS to the Procfile or to project.clj?
I notice it is in the book that there is JVM_OPTS in the procfile for Heroku so I will try that?
page 342 Now you can find the connection settings for the database on your Heroku dashboard. Heroku provides configuration via environment variables. It provides PORT and DATABASE_URL by default, and you can add any additional configuration to the environment via the Heroku dashboard. We're now ready to push our application to Heroku.
perhaps you can set JVM_OPTS there?
Ok I changed project.clj to include :project/prod {:jvm-opts ["-Dconf=prod-config.edn" ]
and I added $JVM_OPTS
to the Procfile
Unfortunately I don't understand the Heroku dashboard quite yet but I can definitely look into that too
Ok thanks. So in the book with Heroku deployment on p. 340 it has this for the procfile
web: java $JVM_OPTS -cp target/guestbook.jar \
clojure.main -m guestbook.core
I will try that. Right now I have the one that was automatically generated
web: java $JVM_OPTS -Dclojure.main.report=stderr -cp target/uberjar/guestbook.jar clojure.main -m guestbook.core
Ok thanks for helping me I really appreciate it
So for my Procfile, do you think it's good enough to have the $JVM_OPTS as it's written in the book on p. 340? Or should I add something about prod-config.edn?
Book page 340 in the section Heroku Deployment
Book version: B17.0βMarch 12, 2021
That might be my issue, is I don't have the final version... I forgot about that
This is what it says for the Procfile there
Ok I'll try this for my procfile. The other stuff was already there, except for -Dconf=prod-config.edn
web: java -Dconf=prod-config.edn -Dclojure.main.report=stderr -cp target/uberjar/guestbook.jar clojure.main -m guestbook.core
Same errors π
I wonder what else to try. Or maybe just change the name to config.edn instead of prod-config.edn? I'm not sure
I just found this and I'm going to read it really quick https://github.com/luminus-framework/luminus/issues/231
Here is the error:
Syntax error compiling at (/tmp/form-init6003546018927796534.clj:1:73).
could not find a non empty configuration file to load. looked in the classpath (as a "resource") and on a file system via "conf" system property
Here is my prod-config.edn:
{:prod true
:port (System/getenv "PORT")
:database-url (System/getenv "JDBC_DATABASE_URL")
}
In the luminus docs it says it can just be {:prod true}
but then someone yesterday here on slack suggested what I have now. But I can change back to {:prod true}
now and try that again...
So the person in the github issue said they changed a file in the env/prod/resources/config.edn, and now what they changed it to is the default. So I don't think that will help me. Unless maybe I should try removing that folder from the .gitignore so that it gets read?
Once the repository is created, we can test the application by running foreman start in the project's root directory. If the application starts up fine, then we're ready to deploy it to the cloud by running the following command:
When I run foreman start
I get this
15:19:47 web.1 | started with pid 85167
15:19:48 web.1 | Error: Could not find or load main class clojure.main
15:19:48 web.1 | Caused by: java.lang.ClassNotFoundException: clojure.main
15:19:49 web.1 | exited with code 1
15:19:49 system | sending SIGTERM to all processes
Maybe that is the root issue that it's not loading or finding the main class
I'm rebuilding right now!
I thought I did but better make sure
When I do git push heroku main
I see the resulting jar as target/uberjar/guestbook.jar
but when I open up the target folder, I don't see any uberjar folder...
I don't know where it is or how to check the contents. Sorry I'm such a newbie I feel like this stuff is way over my head even if it's really simple.
I just saw this from the github link I posted where people were having the same issue. I'm going to try it, even though it looks like I might have more errors regardless...
it seems that
heroku run lein run migrate
from the documentation is not going to work.
Migration could be done running the generated jar file:
heroku run java -cp target/uberjar/<app name>.jar clojure.main -m guestbook.core migrate
Wow ok so the migrations just worked
I'm going to try looking at the app in heroku now to see if it works
Holy smokes it works now!!! @UP82LQR9N thank you SO MUCH for all your help. I really, really appreciate it. I think it was changing the heroku migration command that worked for me, but all the other things might have contributed too, because I did run it with the updated Procfile and everything with the JVM_OPTS.
in the other github issue you linked i think the author didnt use heroku a lot and keep it up to date.
Ok yea that makes sense. I added the solution to my stack overflow post, so hopefully anyone else who tries will find the solution from Google. Or, maybe most people find that comment in the github post faster than I did π