Fork me on GitHub
#beginners
<
2017-04-05
>
seancorfield00:04:23

@rmuslimov Perhaps you can provide more details about your REPL and Java class problem here?

rmuslimov00:04:40

I’m using lein, here is my project.clj

rmuslimov00:04:44

(defproject clj-soap-srv "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url ""
  :license {:name "Eclipse Public License"
            :url ""}
  :dependencies [[org.clojure/clojure "1.9.0-alpha14"]
                 [com.stuartsierra/component "0.3.2"]
                 [ "2.0"]
                 [reloaded.repl "0.2.3"]]
  :main ^:skip-aot clj-soap-srv.core
  :target-path "target/%s"
  :source-paths ["src/clj"]
  :java-source-paths ["src/java"]
  :profiles {:dev {:source-paths ["dev"]}
             :user {:plugins [[refactor-nrepl "2.3.0-SNAPSHOT"]
                              [cider/cider-nrepl "0.15.0-SNAPSHOT"]]}
             :uberjar {:aot :all}})

rmuslimov00:04:54

for example I have class in src/java

package org.opentravel.ota._2003._05;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "successAndWarningsAndEchoData",
    "errors"
})
@XmlRootElement(name = "OTA_PingRS")
public class OTAPingRS {

rmuslimov00:04:18

And here is exception I’ve got

Caused by: java.lang.ClassNotFoundException: org.opentravel.ota._2003._05.OTAPingRQ, compiling:(clj_soap_srv/core.clj:1:1)

seancorfield00:04:40

You said it worked the first time you fired up the REPL and worked with some code, but not the second time you fired up the REPL?

seancorfield00:04:14

I’m thinking you’d need to do lein javac to get it to compile the Java code and then lein repl would find those compiled classes. But if you then did lein clean it would delete the classes — so if you did lein repl without first doing lein javac, you wouldn’t have those compiled classes available...

rmuslimov00:04:21

yea, I added

(:import            [org.opentravel.ota._2003._05 OTAPingRQ OTAPingRS]

rmuslimov00:04:34

lein javac fails with same exception

rmuslimov00:04:56

[[email protected] clj-soap-srv]$ lein javac
(WARNING: user-level profile defined in project files.)
Exception in thread "main" java.lang.ExceptionInInitializerError
	at clojure.main.<clinit>(main.java:20)
Caused by: java.lang.ClassNotFoundException: org.opentravel.ota._2003._05.OTAPingRQ, compiling:(clj_soap_srv/core.clj:1:1)
....

rmuslimov00:04:10

I believe lein repl calls lein javac inside of it

seancorfield00:04:09

(I think you would need :prep-tasks [”javac”] for that to happen but I’m not certain — it’s been a long time since I last used Leiningen — I switched to Boot about 18 months ago 🙂 )

rmuslimov00:04:12

ok, you’re right that I have to call lein javac first

rmuslimov00:04:51

yea, but javac still have same problem

seancorfield00:04:16

I’m puzzled as to why lein javac even tries to load the Clojure code...

rmuslimov00:04:55

so, idea of javac pick all code from java-source-paths and compile it

rmuslimov00:04:08

may be I have plugins that doing something badly

rmuslimov00:04:20

I will try to switch them off

seancorfield00:04:01

Could be — ~/.lein/profiles.clj is often the source of problems when several plugins have been added to it and then you go to work on a new project...

rmuslimov00:04:08

unfortunately, didn’t help

rmuslimov00:04:21

I’ve deleted profiles and plugins in project

rmuslimov00:04:37

after googling with proper words, you’ve said

rmuslimov01:04:21

yea, profile :dev

rmuslimov01:04:28

brings clojure code

seancorfield01:04:32

I just checked and with a fresh project, I added a simple Java class and in the simple Clojure namespace imported it, when I run lein repl, it compiles the Java to .class files as expected and they’re available directly.

seancorfield01:04:53

Ah, there you go — that’s probably why Leiningen gives you that warning: (WARNING: user-level profile defined in project files.)

seancorfield01:04:37

I would think you’d want those plugins loaded in the dev profile, not the user profile?

rmuslimov01:04:50

that’s because I added cider plugin

rmuslimov01:04:04

to show other devs some stuff with reloading

rmuslimov01:04:35

so, I do some stuff in dev/user.clj

rmuslimov01:04:39

with reloaded.repl

seancorfield01:04:41

I think “best practice” is to add that sort of stuff to the dev profile…?

rmuslimov01:04:04

and it turns to problem

rmuslimov01:04:30

(ns user
  (:require [com.stuartsierra.component :as component]
            [clj-soap-srv.core :refer [enabled-endpoints]]
            [clj-soap-srv.utils :refer [new-jaxws-server]]
            [clojure.tools.namespace.repl :refer [refresh]]
            reloaded.repl))

(defn dev-system []
  (component/system-map
   :jax-ws (new-jaxws-server enabled-endpoints)))

(defn reload-system!
  "Useful when in`lein repl`."
  []
  (reloaded.repl/suspend)
  (refresh)
  (reloaded.repl/resume))

(reloaded.repl/set-init! #'dev-system)

rmuslimov01:04:46

and if I change profile it works great

seancorfield01:04:11

(do you need/want reloaded.repl as a mainline dependency? shouldn’t that also be in the dev profile?)

seancorfield01:04:41

Glad you have it figured out! Leiningen profiles can be a pain when you’re first getting a dev workflow figured out (in my opinion).

rmuslimov01:04:57

so when I start lein javac from CLI, it uses profile

rmuslimov01:04:01

I will read about it

rmuslimov01:04:12

uses :dev profile

seancorfield01:04:02

you can always tell Leiningen to use a particular profile for any given task lein with-profile xyz sometask

seancorfield01:04:24

and you can run multiple tasks with “do”: lein do task1, task2

seancorfield01:04:49

(I think Boot makes all this stuff easier, but Leiningen is the older and more popular build tool)

dviramontes02:04:38

hi, can anyone point me to a good tutorial / guide on doing file io in an ring/compojure handler (async) I have code like this:

(defn handle-text-2-speech [req]
  (let [{:keys [content-type audio-stream]}
        (polly/synthesize-speech
          :text "An API for speech synthesis via AWS Polly"
          :output-format "mp3"
          :voice-id "Ivy")
        input-stream (.getWrappedInputStream audio-stream)
        filename "resources/public/tmp/speech.mp3"
        _ (io/copy input-stream (io/file filename))]
    (io/file filename)))
the file gets copied to the location on disk but obviously when i return (io/file filename) it doesnt have the contents

dviramontes02:04:04

i need to block / await for the file to be copied but having a little trouble wrapping my head around it

dviramontes02:04:02

if that something i would do in a thread ? with like (future …) ?

noisesmith02:04:35

@dviramontes 1) the io/copy doesn't need to be in the let block, defn has an implicit do

noisesmith02:04:46

2) io/copy doesn't return until the file is on disk

noisesmith02:04:11

3) just calling io/file doesn't give you contents, it just gives you a handle that would find the contents if you read from it

dviramontes03:04:24

@noisesmith thanks, and how would you read from it ? slurp, with-open ?

noisesmith03:04:37

slurp is for text

noisesmith03:04:01

so I think you need with-open, and a byte-array or whatever it is that is going to hold the bytes that come from that file

noisesmith03:04:38

or if you want to send the bytes to the client, you can create an FileInputStream from the file and return that to ring

noisesmith03:04:53

(now that I think about it, that's probably what you want)

dviramontes03:04:42

Ok cool, thanks! I'll try it!

dviramontes05:04:30

@noisesmith got it to work using (io/input-stream audio-stream)

byron-woodfork16:04:45

You beat me to it 😃

zcassini18:04:20

(defn title-case [xs]
  (as-> xs xs
      (s/split xs #" ")
      (map s/capitalize xs)
      (s/join " " xs)))
is it bad practice to reuse xs as the variable name in this threading macro?

zcassini18:04:45

or in a loop recur

(defn doit [xs]
  (loop [xs xs
         acc 0]
     ....

noisesmith18:04:41

in the loop/recur case I'd be tempted to use a multi-arity function and just recur to the function itself

(defn doit
       ([xs] (doit xs 0))
      ([xs acc] ....))

zcassini18:04:21

i can see that, thanks

curlyfry18:04:51

@zcassini I have definitely seen several people binding the name in a loop/as-> to the same name and I don't think there's much sense in renaming just for renaming's sake. I don't think it's bad practice. However, like the other answers I usually try to find other solutions where I don't have to use loop/recur or as->

seancorfield18:04:28

Also, I would write that example as either

(defn title-case [s]
  (-> s
      (s/split #” ')
      (->> (map s/capitalize)
           (s/join “ “))))
or, more likely, as:
(defn title-case [s]
  (->> (s/split s #” “)
       (map s/capitalize)
       (s/join “ “)))
I would not use as-> here (I would only use as-> inside an already-threaded expression).

teodorlu19:04:02

@seancorfield I like how you avoided having to choose between -> and ->> by starting with (->> (s/split s #" "

seancorfield19:04:33

You can mix and match -> and ->> if you can start with -> (but not with ->>). I try to avoid as-> unless I really need something in the middle of a threaded expression that has an argument not in the first/last position.

zcassini19:04:08

so you think

(defn title-case [xs]
  (-> xs
      (s/split #" ")
      (->> (map s/capitalize))
      (->> (s/join " " ))))

noisesmith19:04:07

you only need one ->> block, the map and join can both be in it

seancorfield19:04:51

Yeah, as I said, “more likely” just the ->> form.

zcassini19:04:56

i did that, but the shape of the code look "worse?

seancorfield19:04:25

Subjective. I prefer it the shorter way with an actual expression in the first slot of ->>.

seancorfield19:04:57

I find this

(defn title-case [s]
  (->> (s/split s #" ")
       (map s/capitalize)
       (s/join " “)))
much easier to read than
(defn title-case [xs]
  (-> xs
      (s/split #" ")
      (->> (map s/capitalize))
      (->> (s/join " " ))))

seancorfield19:04:35

Having multiple ->> in there looks really confusing IMO.

zcassini19:04:30

I see what u are saying, using the result of the split string as the starting point

zcassini19:04:25

thx for your input all

seancorfield19:04:09

When the syntax is new and unfamiliar, it can be hard to decide what “looks best” and I know my preferred style has changed a lot over the last six years.

teodorlu19:04:33

@seancorfield Follow up about the use of ->> inside ->. Is the reason you can nest expressions of ->> inside -> some special macro magic? I didn't expect this to work:

(-> "hello there you person"
    (s/split #" ")
    (->> (map s/capitalize)
         (s/join " ")))
, thinking the map s/cap ... line should return a function that crashes the next line.

seancorfield19:04:02

Use macroexpand to see how -> and ->> behave… 🙂

seancorfield19:04:23

They both just rewrite expressions into the nested forms.

seancorfield19:04:41

So (-> a (b x) (c y)) produces (c (b a x) y)

seancorfield19:04:39

and (->> a (b x) (c y)) produces (c y (b x a))

seancorfield19:04:27

Thus (-> a (->> (b x))) => (->> a (b x)) => (b x a)

teodorlu19:04:05

So the macros are run outside-to-inside, and the -> rewrites the ->>?

zcassini19:04:13

defn title-case [xs] do you think there is something more idiomatic that xs for the argument name?

seancorfield19:04:31

and (-> a (->> (b x) (c y))) produces (c y (b x a))

seancorfield19:04:50

@zcassini since the argument is a string, I’d just use s

zcassini19:04:35

i guess that makes sense, cause its not quite a list of chars or at least it is a special case

seancorfield19:04:24

@teodorlu Yes, macros are “just” functions of code that produce code so the outer -> rewrites its arguments first, then there’s another pass since there are still macros present (`->>`) and so more macro-expansion happens.

teodorlu19:04:32

@seancorfield That makes a lot of sense. Thank you.

seancorfield19:04:43

The biggest difference between macros and (regular) functions is that the latter have their arguments evaluated before the call whereas the former do not.