This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-11-19
Channels
- # aleph (8)
- # announcements (43)
- # babashka (43)
- # beginners (62)
- # calva (8)
- # cider (27)
- # clj-kondo (18)
- # cljs-dev (25)
- # cljsrn (16)
- # clojure (51)
- # clojure-europe (6)
- # clojure-nl (14)
- # clojure-spec (7)
- # clojure-uk (39)
- # clojurescript (17)
- # cursive (9)
- # datascript (12)
- # datomic (16)
- # events (1)
- # fulcro (7)
- # funcool (1)
- # graalvm (2)
- # graphql (5)
- # jobs (1)
- # juxt (6)
- # kaocha (9)
- # leiningen (11)
- # luminus (1)
- # malli (1)
- # off-topic (80)
- # other-languages (2)
- # overtone (3)
- # pedestal (5)
- # quil (1)
- # re-frame (6)
- # reagent (1)
- # reitit (4)
- # rewrite-clj (5)
- # shadow-cljs (207)
- # spacemacs (1)
- # specter (4)
- # sql (1)
- # vim (14)
- # xtdb (7)
Are you using CIDER by any chance? If so there’s a helpful menu at the repl you can see by hitting comma (,) and “require-repl-utils “ from there
Is there an easy way to consume the whole expression with tools.reader when an reader error occurs?
(require '[clojure.tools.reader :as r])
(require '[clojure.tools.reader.reader-types :as t])
(binding [r/*read-eval* false]
(loop [input (t/source-logging-push-back-reader "(+ #=(+ 1 2) 3) :foo")
res '[]]
(let [exp (try (r/read input false :end)
(catch clojure.lang.ExceptionInfo e :read-err))]
(if (= exp :end) res
(recur input (if (= exp :read-err) res (conj res exp)))))))
;; => [(+ 1 2) 3 :foo]
;; => want just [:foo]
I think that in general, reader errors can occur at any nested depth, and so how would the reader know that you wanted just [:foo] when an error occurs nested 7 levels down?
Your phrase "the whole expression" I do not even know has a precise definition, when errors occur. What is the whole expression for a string that does not contain an expression?
Okay I don't know the exact vocabulary for this case. I guess toplevel expression. What I want to express is that with *read-eval*
set to true, the first expression would have been (+ 3 3)
. So as the error occurs I would like the whole expression (+ #=(+ 1 2) 3)
to be "consumed" no matter at what nested depth the error occurs. I just wanted to know if there is an easy way to do this. I can of course start counting parenthesis .. etc. and then do the consuming part myself.
For arbitrary input strings, which tools.reader is built to try to handle as best it can, it does not know after the end of the characters #=(+ 1 2)
that there will be any remaining characters that will complete the top level expression, or not. What if there are 17 additional errors between there and what you as a person think is the end of the top level expression?
I'm not saying what you are asking is impossible to do, just why tools.reader does not.
Oh I think I get it, the ::foo/bar
is only used when some required namespaces as been alias with :as foo
, correct?
So if I want to create specs that describe nested objects, what should I use, something like (s/def ::foo-bar ...)
?
You can use a (dotted) qualifier, you just can’t use an alias that hasn’t been defined, so something like :foo/bar is fine
hey, I'm new to pedestal and I'm trying to receive a POST request on pedestal server, passing json data onto the body
but the body's response is returning a jetty HTTP java object
how I can parse the return?
the object:
#object[org.eclipse.jetty.server.HttpInputOverHTTP 0x3a8cae6d "HttpInputOverHTTP@3a8cae6d[c=0,q=0,[0]=null,s=STREAM]"]"
{:protocol "HTTP/1.1",
:async-supported? true,
:remote-addr "127.0.0.1",
:servlet-response
#object[org.eclipse.jetty.server.Response 0x739ecf39 "HTTP/1.1 200 \nDate: Tue, 19 Nov 2019 16:36:04 GMT\r\n\r\n"],
:servlet
#object[io.pedestal.http.servlet.FnServlet 0xf278491 "io.pedestal.http.servlet.FnServlet@f278491"],
:headers
{"host" "localhost:3000",
"user-agent" "PostmanRuntime/7.19.0",
"content-type" "application/json",
"content-length" "36",
"connection" "keep-alive",
"accept" "*/*",
"accept-encoding" "gzip, deflate",
"postman-token" "8087db8a-ac66-4f65-99c9-77b4dc79690d",
"cache-control" "no-cache"},
:server-port 3000,
:servlet-request
#object[org.eclipse.jetty.server.Request 0x199991d9 "Request(POST //localhost:3000/character1/add)@199991d9"],
:content-length 36,
:content-type "application/json",
:path-info "/character1/add",
:character-encoding "UTF-8",
:url-for #<Delay@76ec7f1f: :not-delivered>,
:uri "/character1/add",
:server-name "localhost",
:query-string nil,
:path-params [],
:body
#object[org.eclipse.jetty.server.HttpInputOverHTTP 0x3a8cae6d "HttpInputOverHTTP@3a8cae6d[c=0,q=0,[0]=null,s=STREAM]"],
:scheme :http,
:request-method :post,
:context-path ""}
that's the request map, I mean the service map like in this sample https://github.com/pedestal/pedestal/blob/master/samples/json-api/src/json_api/service.clj
(def service {:env :prod
;; You can bring your own non-default interceptors. Make
;; sure you include routing and set it up right for
;; dev-mode. If you do, many other keys for configuring
;; default interceptors will be ignored.
;; ::http/interceptors []
::http/routes routes
;; Uncomment next line to enable CORS support, add
;; string(s) specifying scheme, host and port for
;; allowed source(s):
;;
;; ""
;;
;;::http/allowed-origins [""]
;; Tune the Secure Headers
;; and specifically the Content Security Policy appropriate to your service/application
;; For more information, see:
;; See also:
;;::http/secure-headers {:content-security-policy-settings {:object-src "'none'"
;; :script-src "'unsafe-inline' 'unsafe-eval' 'strict-dynamic' https: http:"
;; :frame-ancestors "'none'"}}
;; Root for resource interceptor that is available by default.
::http/resource-path "/public"
;; Either :jetty, :immutant or :tomcat (see comments in project.clj)
;; This can also be your own chain provider/server-fn --
::http/type :jetty
;;::http/host "localhost"
::http/port 8080
;; Options to pass to the container (Jetty)
::http/container-options {:h2c? true
:h2? false
;:keystore "test/hp/keystore.jks"
;:key-password "password"
;:ssl-port 8443
:ssl? false}})
this?
Hey everyone, noob here working on a little static site builder for myself. The way I have it set up currently, I have a 'sitemap' file that looks like this:
(ns ssg.sitemap
(:require ssg.content :as content))
(def pages
[
{:url "/index.html"
:title "Home"
:nav true
:content content/home}
{:url "/about.html"
:title "About"
:nav true
:content content/about}
{:url "/404.html"
:title "404 Not Found"
:nav false
:content content/not-found}
])
Is there a way to dissoc the :content fields out of these maps, and then pass them downstream without the content ns dependency?
I want my templates to have access to the sitemap for building navbars and such, and my content pages need to have access to the templates so I can use them, and it's a cyclic dependency.maybe you can separate the templates to 2 namespaces: templates.sitemap & templates.page
sitemap <- pages templates.sitemap <- sitemap content <- templates.sitemap content <- templates.page
Is it a bad idea to 'quote the content values, and then eval them when they're needed to build the site? That seems to resolve the problem in a kinda elegant way
it's not a bad idea to try different kind of things including eval
when starting to learn clojure
i remember tbaldridge wrote something that enlightened me here in this slack channel couple of years ago
I say this because the "eval is evil" trope is mis-applied in Lisps. Eval is evil in a language like Javascript (where the phrase comes from) where eval means concat-ing strings together. In Clojure eval is akin to something like stored procs in SQL. It makes code injection really hard
you can see the context of the discussion here in the log https://clojurians-log.clojureverse.org/clojure-spec/2017-01-26
Whats the common approach to "initializing", as in loading up files, setting up DB connections, etc before the app starts? I have something that looks like this
(defn start
"Entry point for the server"
[& args]
(init)
(if (env :dev-mode)
(web/run-dmc app {:host (or (env :host) "localhost")})
(web/run app (undertow/options {}))))
a couple of small things - idiomatically -main
is considered the entry-point for a namespace (it's the thing that becomes the main method when you use gen-class, but it's useful to use this convention even without gen-class IMHO), and (or (env :foo) "bar")
can be replaced with (env :foo "bar")
- using a hash as a function already allows another argument to be used when the key isn't found
and a nice guide: https://cb.codes/a-tutorial-of-stuart-sierras-component-for-clojure/
So what's the story on the 'project-name' subfolder under src/ in the standard Leiningen template? Is there a reason you don't want your source files to live directly under src/? Is there a situation where you'd want that subdir to be named something else, or to have multiple ones?
that ends up being the first segment of your project namespaces
generally, you're going to be mixing your code with other libraries, etc so it is a good and common practice to prefix all of the namespaces in your project with a common namespace prefix, ideally something that you "own" (reverse domain name, company name, trademark, etc)
so Clojure's own namespaces are things like clojure.string
(where "clojure" is the common root)
if you just called it string
, you'd be fighting with every other library and project that had a string
namespace
namespace names and file nesting in directories correspond to each other, and it is impolite to use a short non-segmented name (segments are separated by '.', each namespace segment corresponds to a nested directory) because of the possibility of collisions. the compilation model, how clojure generates bytecode in classes and names those classes is also effected by this, in that a non-segmented namespacee names result in classes in the global package (or whatever java calls it) and that is mostly considered poor practice to do on the java side of things
different tools handle the new project names differently, but with lein if you say lein new foo
you get a foo.core namespace to start, but if you say lein new foo.bar/baz
you get a foo.bar.baz namespace to start
The clj-new
tool (for the new CLI/`deps.edn` tooling) won't let you specify a single segment project name. If you want foo.core
, you must explicitly ask for it, but the readme explains that you should generally use project names like username/myproject
to ensure that your namespaces follow good practices (`username.project` as the entry point). Where username
would be your GitHub user name, for example, or your company's name or reverse domain name etc.
(unfortunately, not all project templates accept the username/project
format, so you sometime have to use username.project
instead, although some templates don't even accept that format either 😞 )
when you see one single quote in this context, what does that mean? (require '[clojure.repl :refer :all])
single quote as a prefix always means "read but do not evaluate"
that's equivalent to ['clojure.repl :refer :all]
since that symbol is the only part that would change if evaluated
the reader translates '[foo bar bash]
into (quote [foo bar bash])
the docs for what quote does should give you the rest
the single quote would be an error in the :require
clause of ns