Fork me on GitHub
#beginners
<
2019-11-19
>
dpsutton00:11:40

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

vaer-k00:11:03

Yes, I am. Wow thanks great tip!

cider 8
FiVo09:11:25

Is there an easy way to consume the whole expression with tools.reader when an reader error occurs?

FiVo09:11:41

(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]

andy.fingerhut10:11:59

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?

andy.fingerhut10:11:08

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?

FiVo10:11:25

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.

andy.fingerhut16:11:21

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?

andy.fingerhut16:11:52

I'm not saying what you are asking is impossible to do, just why tools.reader does not.

solf09:11:29

Isn't this supposed to work? ::foo/bar

solf10:11:13

Oh I think I get it, the ::foo/bar is only used when some required namespaces as been alias with :as foo, correct?

solf10:11:09

So if I want to create specs that describe nested objects, what should I use, something like (s/def ::foo-bar ...) ?

Alex Miller (Clojure team)12:11:48

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

Renam Savio14:11:10

hey, I'm new to pedestal and I'm trying to receive a POST request on pedestal server, passing json data onto the body

Renam Savio14:11:43

but the body's response is returning a jetty HTTP java object

Renam Savio14:11:57

how I can parse the return?

Renam Savio14:11:23

the object:

#object[org.eclipse.jetty.server.HttpInputOverHTTP 0x3a8cae6d "HttpInputOverHTTP@3a8cae6d[c=0,q=0,[0]=null,s=STREAM]"]"

mavbozo16:11:04

what's your service map looks like?

Renam Savio16:11:28

{: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 ""}

mavbozo16:11:26

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}})

mavbozo16:11:29

that's the generated service from the service map

mavbozo16:11:36

do you have some code defining the service like the sample I gave?

parker15:11:51

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.

mavbozo16:11:06

i'd like to put the pages on separate namespaces ex: ssg.pages

mavbozo16:11:19

and in ssg.sitemap, i'd put

mavbozo16:11:27

(def sitemap (mapv #(dissoc % :content) pages))

mavbozo16:11:45

nope, still cyclic deps

mavbozo16:11:49

sitemap <- pages templates <- sitemap content <- templates

mavbozo16:11:51

maybe you can separate the templates to 2 namespaces: templates.sitemap & templates.page

mavbozo16:11:52

sitemap <- pages templates.sitemap <- sitemap content <- templates.sitemap content <- templates.page

parker17:11:54

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

mavbozo17:11:37

it's not a bad idea to try different kind of things including eval when starting to learn clojure

mavbozo17:11:28

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

mavbozo17:11:47

you can see the context of the discussion here in the log https://clojurians-log.clojureverse.org/clojure-spec/2017-01-26

parker17:11:17

good info, thank you!!

Mario C.20:11:39

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 {}))))

noisesmith20:11:33

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

👍 4
Mario C.20:11:21

I am not sure why it was called start to begin with.

Mario C.20:11:59

Where (init) is a function that is doing what I am describing.

Mario C.20:11:00

In project.clj I have :main com.my.namespace.handler/start

Mario C.20:11:55

ill take a look

parker22:11:15

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?

Alex Miller (Clojure team)22:11:11

that ends up being the first segment of your project namespaces

Alex Miller (Clojure team)22:11:29

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)

Alex Miller (Clojure team)22:11:47

so Clojure's own namespaces are things like clojure.string (where "clojure" is the common root)

Alex Miller (Clojure team)22:11:25

if you just called it string, you'd be fighting with every other library and project that had a string namespace

hiredman22:11:45

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

hiredman22:11:30

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

seancorfield22:11:34

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.

seancorfield22:11:24

(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 😞 )

macgyver22:11:18

when you see one single quote in this context, what does that mean? (require '[clojure.repl :refer :all])

noisesmith22:11:38

single quote as a prefix always means "read but do not evaluate"

noisesmith22:11:54

that's equivalent to ['clojure.repl :refer :all]

noisesmith22:11:05

since that symbol is the only part that would change if evaluated

bfabry22:11:30

the reader translates '[foo bar bash] into (quote [foo bar bash]) the docs for what quote does should give you the rest

noisesmith22:11:32

the single quote would be an error in the :require clause of ns

macgyver22:11:18

ok great, thanks for the helpful fast replies!