Using Clojure multimethods defined across multiple namespaces - clojure

Although the below example seems a bit strange, it's because I'm trying to reduce a fairly large problem I've got at present to a minimal example. I'm struggling to work out how to call into multimethods when they're sitting behind a couple of abstraction layers and the defmulti and corresponding defmethods are defined in multiple namespaces. I really feel like I'm missing something obvious here...
Suppose I've got the following scenario:
I purchase stuff from a variety of suppliers, via their own proprietary interfaces
I want to implement a common interface to talk to each of those suppliers
I want to be able to purchase different items from different suppliers
Using Clojure, the recommended ways of implementing a common interface would be via protocols or multimethods. In this case, as I'm switching based on the value of the supplier, I think the best way to handle the situation I'm describing below is via multimethods (but I could be wrong).
My multimethod definitions would look something like this, which defines a common interface I want to use to talk to every supplier's APIs:
(ns myapp.suppliers.interface)
(defmulti purchase-item :supplier)
(defmulti get-item-price :supplier)
For each supplier, I probably want something like:
(ns myapp.suppliers.supplier1
(:require [myapp.suppliers.interface :as supplier-api]))
(defmethod purchase-item :supplier1 [item quantity] ...)
(defmethod get-item-price :supplier1 [item] ...)
and
(ns myapp.suppliers.supplier2
(:require [myapp.suppliers.interface :as supplier-api]))
(defmethod purchase-item :supplier2 [item quantity] ...)
(defmethod get-item-price :supplier2 [item] ...)
So far, no problem
Now to my code which calls these abstracted methods, which I assume looks something like:
(ns myapp.suppliers.api
(:require [myapp.suppliers.supplier1 :as supplier1]
[myapp.suppliers.supplier2 :as supplier2])
(defn buy-something
[supplier item quantity]
(purchase-item [supplier item quantity])
(defn price-something
[supplier item]
(get-item-price [supplier item])
This is starting to look a bit ... ugly. Every time I implement a new supplier's API, I'll need to change myapp.suppliers.api to :require that new supplier's methods and recompile.
Now I'm working at the next level up, and I want to buy a widget from supplier2.
(ns myapp.core
(:require [myapp.suppliers.api :as supplier])
(def buy-widget-from-supplier2
(buy-something :supplier2 widget 1)
This can't work, because :supplier2 hasn't been defined anywhere in this namespace.
Is there a more elegant way to write this code? In particular, in myapp.core, how can I buy-something from :supplier2?

Initial notes
It's hard to tell if you mixed up some things in the process of simplifying the example, or if they weren't quite right out of the gate. For an example of what I'm referring to, consider purchase-item, though the issues are similar for get-item-price:
The defmulti call is a single-argument function
The defmethod calls each take two arguments
The call in buy-something passes a vector to purchase-item, but looking up the :supplier keyword in a vector will always return nil
Your concerns
Every time I implement a new supplier's API, I'll need to change myapp.suppliers.api to :require that new supplier's methods and recompile.
If you require the myapp.suppliers.interface namespace myapp.suppliers.api, the problem can be avoided
This can't work, because :supplier2 hasn't been defined anywhere in this namespace.
Simply put, this will work. :)
Is there a more elegant way to write this code? In particular, in myapp.core, how can I buy-something from :supplier2?
Certainly, but this solution is going to make some assumption based on the ambiguities in the Initial notes.
Without straying too far from your original design, here's a fully-working example of how I interpret what you were trying to achieve:
myapp.suppliers.interface
(ns myapp.suppliers.interface)
(defmulti purchase-item (fn [supplier item quantity] supplier))
myapp.suppliers.supplier1
(ns myapp.suppliers.supplier1
(:require [myapp.suppliers.interface :as supplier-api]))
(defmethod supplier-api/purchase-item :supplier1 [supplier item quantity]
(format "Purchasing %dx %s from %s" quantity (str item) (str supplier)))
myapp.suppliers.supplier2
(ns myapp.suppliers.supplier2
(:require [myapp.suppliers.interface :as supplier-api]))
(defmethod supplier-api/purchase-item :supplier2 [supplier item quantity]
(format "Purchasing %dx %s from %s" quantity (str item) (str supplier)))
myapp.suppliers.api
(ns myapp.suppliers.api
(:require [myapp.suppliers.interface :as interface]))
(defn buy-something [supplier item quantity]
(interface/purchase-item supplier item quantity))
myapp.core
(ns myapp.core
(:require [myapp.suppliers.api :as supplier]))
(def widget {:id 1234 :name "Monchkin"})
(supplier/buy-something :supplier1 widget 15)
;;=> "Purchasing 15x {:id 1234, :name \"Monchkin\"} from :supplier1"
(supplier/buy-something :supplier2 widget 3)
;;=> "Purchasing 3x {:id 1234, :name \"Monchkin\"} from :supplier2"
As you can see, the supplier/buy-something calls propagate to the appropriate multimethod implementations. Hopefully this helps you get where you were trying to go.

Related

Using the hickory library, is it possible to use selectors in combination with zippers?

I'm new to Clojure, and hickory, and the idea of zippers.
What I want to do is, I want to use selectors to go to one location in an HTML document. And then, I want to be able to navigate from that location, up to a parent element, and then get 2nd sibling from that point.
Is this possible to do with hickory? From what I understand, it seems as though I only have the option of using selectors, or navigating the HTML in a zipper structure, but I can't figure out how to do both, or if that's even possible.
You could do something like this:
(:require
[hickory.select :as s]
[hickory.convert :as convert]
[clojure.zip :as z]
...
(let [html (convert/hiccup-to-hickory (list [:div
[:div {:class "didya"} "nevertheless"]]
[:div "possible"]
[:div "geometric"]))]
(-> (s/select-locs (s/class "didya") html)
(first)
(z/up)
(z/right)
(z/right)
(z/node)))
The forest library can do this easily. There is
a video from the last Clojure Conj
many examples also
docs are ongoing.

Clojure kebab case on selected keywords

I want to change certain key's in a large map in clojure.
These key's can be present at any level in the map but will always be within a required-key
I was looking at using camel-snake-kebab library but need it to change only a given set of keys in the required-key map. It doesn't matter if the change is made in json or the map
(def my-map {:allow_kebab_or-snake {:required-key {:must_be_kebab ""}}
:allow_kebab_or-snake2 {:optional-key {:required-key {:must_be_kebab ""}}}})
currently using /walk/postwalk-replace but fear it may change keys not nested within the :required-key map
(walk/postwalk-replace {:must_be_kebab :must-be-kebab} my-map))
ummmm.. could you clarify: do you want to change the keys of the map?! or their associated values?
off-topic: your map above is not correct (having two identical keys :allow_kebab_or_snake - i-m assuming you're just underlining the point and not showing the actual example :))
postwalk-replace WILL replace any occurrence of the key with the value.
so if you know the exact map struct you could first select your sub-struct with get-in and then use postwalk-replace :
(walk/postwalk-replace {:must_be_kebab :mus-be-kebab}
(get-in my-map [:allow_kebab_or_snake :required-key]))
But then you'll have to assoc this into your initial map.
You should also consider the walk function and construct your own particular algorithm if the interleaved DS is too complex.
Here is a solution. Since you need to control when the conversion does/doesn't occur, you can't just use postwalk. You need to implement your own recursion and change the context from non-convert -> convert when your condition is found.
(ns tst.clj.core
(:use clj.core clojure.test tupelo.test)
(:require
[clojure.string :as str]
[clojure.pprint :refer [pprint]]
[tupelo.core :as t]
[tupelo.string :as ts]
))
(t/refer-tupelo)
(t/print-versions)
(def my-map
{:allow_kebab_or-snake {:required-key {:must_be_kebab ""}}
:allow_kebab_or-snake2 {:optional-key {:required-key {:must_be_kebab ""}}}})
(defn children->kabob? [kw]
(= kw :required-key))
(defn proc-child-maps
[ctx map-arg]
(apply t/glue
(for [curr-key (keys map-arg)]
(let [curr-val (grab curr-key map-arg)
new-ctx (if (children->kabob? curr-key)
(assoc ctx :snake->kabob true)
ctx)
out-key (if (grab :snake->kabob ctx)
(ts/kw-snake->kabob curr-key)
curr-key)
out-val (if (map? curr-val)
(proc-child-maps new-ctx curr-val)
curr-val)]
{out-key out-val}))))
(defn nested-keys->snake
[arg]
(let [ctx {:snake->kabob false}]
(if (map? arg)
(proc-child-maps ctx arg)
arg)))
The final result is shown in the unit test:
(is= (nested-keys->snake my-map)
{:allow_kebab_or-snake
{:required-key
{:must-be-kebab ""}},
:allow_kebab_or-snake2
{:optional-key
{:required-key
{:must-be-kebab ""}}}} ))
For this solution I used some of the convenience functions in the Tupelo library.
Just a left of field suggestion which may or may not work. This is a problem that can come up when dealing with SQL databases because the '-' is seen as a reserved word and cannot be used in identifiers. However, it is common to use '-' in keywords when using clojure. Many abstraction layers used when working with SQL in clojure take maps as arguments/bindings for prepared statements etc.
Ideally, what is needed is another layer of abstraction which converts between kebab and snake case as needed depending on the direction you are going i.e. to sql or from sql. The advantage of this aproach is your not walking through maps making conversions - you do the conversion 'on the fly" when it is needed.
Have a look at https://pupeno.com/2015/10/23/automatically-converting-case-between-sql-and-clojure/

Clojure UUID - Having trouble creating IDs for defrecords

All I'm trying to do is create an auto-generated UUID for clojure defrecord's when they are created. I've tried the following:
(ns myns
(:require [clj-uuid :as uuid])
(defrecord Thing [thing-id name])
(defn create-thing
[name]
(map->Thing {:thing-id (uuid/v1)
:name name}))
Followed by:
(repeat 5 (create-thing "bob"))
But I get the same UUID created for every Thing I create. Help would be appreciated!
I'm suspicious about using a dedicated lib for this, given how easy it is to do via interop using the built in UUID class that comes with the jvm.
(ns myns
(:import (java.util UUID)))
(defrecord Thing [thing-id name])
(defn create-thing
[name]
(map->Thing {:thing-id (UUID/randomUUID)
:name name}))
;; using repeatedly instead of repeat generates new values,
;; instead of reusing the initial value
(repeatedly 5 #(create-thing "bob"))

Reusing a stub/redef across a speclj context

I'm writing tests for a Clojure app using Speclj. I'm accustomed in BDD to do things like this:
context "some context"
stub my-function :return true
it "has behavior one"
should true my-function
it "has behavior two"
should_not false my-function
But in Speclj I can't seem to find an example of how to share the stub across the characteristics, so I'm currently stuck writing code like this:
(describe "this"
(context "that"
(it "accepts nil"
(with-redefs [called-fn (constantly nil)]
(should= nil (my-fn nil)))))
(it "accepts 1"
(with-redefs [called-fn (constantly nil)]
(should= 100 (my-fn 1))))))
(I realize this is a somewhat contrived example and arguably those assertions could all go under one characteristic, but let's suppose for now that I have good reason to write the code like this.)
I want, however, to just have to stub called-fn once, but moving this up out of the its raises errors because the real called-fn gets called instead of my redef.
Is there a way to reuse redefs (or use Speclj stubs) in Speclj so I'm not stuck pushing them all down inside the characteristics?
You can use the around macro to accomplish this.
Here's an example spec:
(ns sample.core-spec
(:require [speclj.core :refer :all]
[sample.core :refer :all]))
(describe "a test"
(it "returns output from fn-a"
(should= 1 (fn-b)))
(describe "using with-redef"
(around [it] (with-redefs [fn-a (fn [] 2)] (it)))
(it "returns the output from redefined function"
(should= 2 (fn-b)))))
Source:
(ns sample.core)
(defn fn-a []
1)
(defn fn-b []
(fn-a))

Abstract functions or function interfaces in Clojure?

In a number of occasions I have a collection of functions that I'd like to implement in different ways. The most obvious example of this would be to abstract from specific databases. In an object-oriented language you would use an interface for this:
interface DB {
ResultSet query(String query);
void persist(Object o);
...
}
In speudo code I would want to do something like this:
(ns dbbackend)
(abstractfn query [q])
(abstractfn persist! [o])
And then implementations for each database:
(ns dbbackend.mysql :implements dbbackend)
(defn query [q] ...)
(defn persist! [o] ...)
It is not entirely clear to me what the best practice is to do something similar in a functional language, specifically Clojure. Should I use multi-methods for this?
Now that version 1.1 of Clojure has been released maybe it's time to take a look into the future.
Datatypes and protocols, which are currently only available in the new master branch on github , might be exactly what you are looking for.
(defprotocol DB
(query [backend query])
(persist [backend object]))
(deftype MySQLBackend []
DB
(query [query] ...)
(persist [object] ...))
For pre-protocol Clojure versions:
The interface:
(ns dbbackend)
(defmulti query
{:arglists '([c q])}
suitable-dispatch-fn)
(defmulti persist!
{:arglists '([c o])}
suitable-dispatch-fn)
The implementation:
(ns dbbackend.mysql
(:requires dbbackend))
(defmethod query com.mysql.jdbc.Connection
[c q]
...)
(defmethod persist! com.mysql.jdbc.Connection
[c o]
...)
The usage:
(ns user
(:require dbbackend dbbackend.mysql))
(def mysql-connection (connect-to-mysql))
(query mysql-connection some-query)
You can find a real-world example of this approach under the hood of ClojureQL.