Fork me on GitHub
#clojure
<
2022-01-06
>
Dave Russell08:01:52

Heya folks! I need to monkey patch a function in code I don't control. I understand two methods to do this are to enter the namespace and redef it, or to intern the var. Is there a stable way to ensure that the intern/monkey patch is invoked after the lib loads?

Dave Russell08:01:36

For example, when executing tests on some code related to the monkey-patch, I need to ensure that the related monkey patch is applied after loading the dep and before running the test. So I can stick the monkey-patch in the namespace that's being tested, but it seems like there should be a nicer way to declare that "I depend on this monkey patch" so that the loader will handle this for me?

Dave Russell08:01:32

Ah I should clarify that I do have something working (doing in-ns and deffing it), but I'm looking to improve it 🙂 with-redefs is an option but I think I'd have to wrap it around my whole app and I'm not sure it solves the testing problem

Martynas Maciulevičius08:01:46

Then make your own version of that library that exposes what you want...?

Dave Russell08:01:20

Also a possibility 🙂 Would be nice if it was possible to do without though, since it's a 3 line change...

Martynas Maciulevičius08:01:40

Then you can copy the source code and do your change...

Martynas Maciulevičius08:01:50

But that's not very upgradeable

Dave Russell08:01:52

Yeah and it's an internal function so I'd rather not copy a bunch of source code to get "deep enough" to where I can patch what I need

Martynas Maciulevičius08:01:48

I needed something in golang for my master's. And there simply was no other way. I copied the sources and called it a day.

Martynas Maciulevičius08:01:45

In clojure people may have small functions which would allow to hack and compose everything yourself. But in golang they have large blobs with lots of ifs. And if I understand you have the latter.

Martynas Maciulevičius08:01:15

Also library authors in clojure sometimes make all internal variables private. Which blocks to do this monkey patching even if you want it.

Martynas Maciulevičius09:01:36

Anyway. You could use reflection and code generation. But it's too sophisticated. You could look how PowerMock and Mockito work. And try something of that sort (heavy reflection).

Martynas Maciulevičius09:01:14

Mockito can replace methods on a class. PowerMock can replace methods on a static final class (at least static for sure) and constructors, singletons.

Dave Russell09:01:06

> Also library authors in clojure sometimes make all internal variables private. Which blocks to do this monkey patching even if you want it. You should be able to reference the var directly even if it's private

Dave Russell09:01:45

Anyway, I guess my core issue is not how to do the monkey patching -- I have something that works. More how to ensure the monkey patch is applied consistently

Martynas Maciulevičius09:01:23

You could run it in your main method. Who knows.

Martynas Maciulevičius09:01:09

I think separate library is the most consistent and you will know that you do it every time.

Martynas Maciulevičius09:01:23

Also you could have a build process that checks the library for you

rutledgepaulv10:01:59

So long as you require the namespace containing the var you want to patch before you patch it you can be sure your patch is applied after the initial load of the namespace. It doesn't guarantee that the library (or another library) doesn't monkey patch it's own var after that, but that would be pretty unusual.

rutledgepaulv11:01:15

When I monkey patch, this is how I do it:

rutledgepaulv11:01:04

(defmacro defpatch
 "An anaphoric macro for patching existing functions. Original function is bound to the symbol 'this'.Safe to execute multiple times, 'this' will always refer to the original implementation and never the previously patched implementation."
  [symbol bindings & body]
  `(let [var# (var ~symbol)]
     (alter-var-root var#
       (letfn [(define# [orig#]
                 (let [~'this orig#]
                   (with-meta (fn ~bindings ~@body)
                              (merge (meta orig#) {::original orig#}))))]
         (fn [original#] (define# (or (some-> original# meta ::original) original#)))))
     var#))

Yehonathan Sharvit09:01:56

What's the best way to parse a string into an integer?

Martynas Maciulevičius09:01:52

You already have set your constraint. You want it to be an integer. So probably it would be java.lang.Integer that you want. There is a function in Java's stdlib for that. But even then. This may have been clojurescript question. And there is no easy way to parse exactly integer without a floating point part.

ajk09:01:54

Integer/parseInt or Long/parseLong , although IIRC parse-int is to be added in the latest and greatest version of Clojure?

Martynas Maciulevičius09:01:06

@U46C250QM yes, but look at his title: "Author of Data-Oriented programming" This is not the guy who would ask this question to get the answer 😄

ajk09:01:12

of course, but he asked a simple question with a simple answer 🙂 perhaps he intends to ask a different question

Yehonathan Sharvit10:01:15

I didn't get the joke about the title of my book:hugging_face:

Martynas Maciulevičius10:01:19

If you have that kind of a book then you can lookup the answer on your own. So probably the question was not what you were asking for.

Yehonathan Sharvit11:01:37

In my book I deal with "principles" not with low-level implementation details:joy:

😂 1
Yehonathan Sharvit11:01:56

The answer @U46C250QM provided is exactly what I was looking for.

Al Z. Heymer12:01:24

I would always prefer clojure.edn/read-string for any parsing purposes, but I guess it depends on the use case.

Martynas Maciulevičius12:01:18

Sometimes you want to fail fast and with an exception. But edn reader would also be fine if you'd check that you get a number.

Alex Miller (Clojure team)13:01:08

Reading will read the next token from a string, and is inherently a different semantic than parsing.

Al Z. Heymer14:01:26

You mean because

(clojure.edn/read-string "123 4") => 123
?

Alex Miller (Clojure team)14:01:11

yeah. (clojure.edn/read-string "1 fish 2 fish") ;; => 1

Alex Miller (Clojure team)14:01:46

or even "1.234" returning a double when you want to parse a long. parsing has extra expectations - you want to parse the entire input and return a value of the expected type. In Clojure 1.11, we'll have parse-long, parse-double, parse-boolean, and parse-uuid added

Alex Miller (Clojure team)14:01:16

reading is more about tokenization/lexing and reads the next valid token of any type

Yehonathan Sharvit15:01:48

So for now, the way to go is Long/parseLong ?

Alex Miller (Clojure team)15:01:34

yes, that would be my recommendation

👍 3
kirill.salykin16:01:38

Hi I am trying to use clj-async-profiler [https://github.com/clojure-goes-fast/clj-async-profiler] but it fails for me with

JDK9+: you must start the JVM with option -Djdk.attach.allowAttachSelf, otherwise the agent will not be able to dynamically attach to the running process. For Leiningen, add :jvm-opts [“-Djdk.attach.allowAttachSelf”] to project.clj. For Boot, start the process with environment variable BOOT_JVM_OPTIONS=“-Djdk.attach.allowAttachSelf”.`
I’ve added the required option to dev alias
:dev {:extra-paths [“env/dev/clj”]
          :extra-deps  {clj-commons/clj-yaml {:mvn/version “0.7.0"}
        criterium/criterium  {:mvn/version “0.4.6”}
        pogonos/pogonos      {:mvn/version “0.1.1"}
        ubergraph/ubergraph  {:mvn/version “0.8.2”}
        vlaaad/reveal        {:mvn/version “1.3.196"}
        com.clojure-goes-fast/clj-async-profiler {:mvn/version “0.5.1”}

         binaryage/devtools {:mvn/version “0.9.10"}}
         :jvm-opts [“-Djdk.attach.allowAttachSel” “-XX:+UnlockDiagnosticVMOptions” “-XX:+DebugNonSafepoints”]}
and REPL started with command (generated by cider jack-in):
/usr/local/bin/clojure -Sdeps ‘{:deps {nrepl/nrepl {:mvn/version “0.9.0"} cider/cider-nrepl {:mvn/version “0.27.4”}} :aliases {:cider/nrepl {:main-opts [“-m” “nrepl.cmdline” “--middleware” “[cider.nrepl/cider-middleware]“]}}}’ -Mdev:test:cider/nrepl

But same error happens. Please advice, what I am doing wrong?

Ben Sless16:01:42

You have a typo, missing the f in self. Is that in the source, too?

kirill.salykin16:01:30

yes, it seems in source as well

kirill.salykin16:01:23

yes, stupid typo

kirill.salykin16:01:27

thank you so much!

Ben Sless16:01:37

Glad I could help While you're profiling, make sure you have profiling events enabled if on Linux and that you have debug symbols installed

kirill.salykin16:01:55

thanks, but i am on macos same applies?

Ben Sless16:01:59

Partially, consult the docs, you still need debug symbols and a few flags for good results

GGfpc17:01:54

Does anyone here use ragtime for db migrations. Is there any way to force it to only run on one pod? We just had to deal with a conflict where all pods tried to write to the migrations table

ghadi17:01:54

Don't couple database migrations with process initialization. Make them part of your deploy pipeline, just prior to rolling out to pods

2
Jacob Rosenzweig17:01:14

Can anyone explain the `:-` syntax in compojure sweet api? E.g.

(def app
  (api
    (GET "/hello" []
      :query-params [name :- String]
      (ok {:message (str "Hello, " name)}))))

p-himik17:01:54

To be on the same page - it's not syntax per se, it's just a keyword in Clojure. I'm just guessing here, but it looks like it's used to specify the type of the name parameter.

Ben Sless17:01:22

Is it using plumatic/schema?

Cora (she/her)17:01:44

is that for type coercion?

Jacob Rosenzweig18:01:10

Thanks @U02N27RK69K. Do you know if the empty string in this ex is supposed to be a default value?

[name :- String ""]

Cora (she/her)18:01:04

I'm guessing a default value, yes, but you should try it out and see

Cora (she/her)18:01:16

stick a nonsense word in there and perform a request against it

Jacob Rosenzweig18:01:41

Wow this stuff is ancient.

dharrigan18:01:03

I would reocmmend looking at reitit instead. It's the sucessor to compojure-sweet

Jacob Rosenzweig18:01:14

Oh this is work, I don't get a choice. :^)

thompen21:01:04

Inside a unit test I want to verify that the response (a map) contains certain things, that are possibly deeply nested. Are there libraries that: • allow flexible projection of an existing map (something like the glom library for python) • assert equality between two maps in a test, and displays a visual diff of mismatches, preferably colorful

ghaskins21:01:10

I just use https://clojuredocs.org/clojure.data/diff and look for [nil? nil? dont-care] as passing

phronmophobic22:01:09

there are lots of options for examining nested data structures: • https://github.com/redplanetlabs/specterhttps://github.com/noprompt/meander • see the Data Transformation section: https://www.clojure-toolbox.com/ • lots more, but those are the ones that I've found useful. for diffing, there's also multiple options. I've used https://github.com/lambdaisland/deep-diff2

Cora (she/her)00:01:02

lein-difftest, too, if you use lein

thompen06:01:37

thank you all!