This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-06-10
Channels
- # admin-announcements (2)
- # arachne (2)
- # beginners (53)
- # boot (52)
- # cider (7)
- # cljs-dev (61)
- # cljsrn (12)
- # clojure (61)
- # clojure-greece (22)
- # clojure-nl (16)
- # clojure-russia (103)
- # clojure-spec (84)
- # clojure-uk (15)
- # clojurescript (137)
- # community-development (14)
- # cursive (4)
- # datomic (14)
- # devcards (6)
- # euroclojure (3)
- # funcool (26)
- # hoplon (27)
- # jobs (4)
- # lambdaisland (1)
- # leiningen (1)
- # om (75)
- # onyx (77)
- # planck (15)
- # proton (2)
- # re-frame (23)
- # ring-swagger (9)
- # schema (1)
- # specter (95)
- # untangled (124)
- # yada (27)
Is there a straightforward to define a function spec and reuse with multiple fdef's?
@rickmoynihan @danielcompton : same here, also free type hinting, custom explain messages, performance and the list goes on. So far we have no reason to migrate really, we're waiting to see where clj.spec is going, it's probably a bit too early
Besides coercions, my top feature request to spec: helper-fn to create a spec from a vanilla clojure (function) var. It would understand the Clojure destructuring syntax. Something like this:
(require '[clojure.spec :as s])
(s/def ::age integer?)
(s/def ::fullname string?)
(s/def ::role keyword?)
(defn doit [{:keys [::fullname ::age ::role] :or {:boss ::role}}] [fullname age role])
(-> #'doit s/extract-spec s/describe)
; => (keys :req [:user/age :user/fullname] :opt [:user/role])
* the responsibility to extract (and use) the specs would be on the user - I would use these on the web-tier to auto-extract docs & do coercion in the web-api tier with our libs
* would not add new meta-data to vars (arguments are already in :arglists
)
* no need to describe the shape of the data twice (both for the function arguments & for it's spec)
* (optionally the :ret
and :fn
could be read from the Var metadata too)
Thoughts?I think this is rad, my only real question is why might we rely on spec to do it?
There are pretty good hooks to pull validation information out of a defined spec and an alternate defn like this should be a macro.
I don't work on spec so this is grain of salt stuff, but it seems like the opinion of spec is that people could conceivably bring their own sugar but under the hood there's a common language for validation.
@ikitommi: you could build that from what exists now. since it wouldn't generically apply, I don't think we would do that as part of core or anything. One thing that would help is a spec for destructuring, which I have and which will be released at some point in some form (details TBD still)
@alexmiller: spec for destucturing sounds cool. Did a dummy version of the extractor, will play more with it. Are there any caveats in playing with :arglists
?
there are a few cases where people have abused it a bit in what was put in it (data.generators is one that comes to mind) but generally should be fine
I think I would also consider allowing overrides via lookup in the registry - s/fdef
registers stuff there under the fn symbol and those can be obtained via s/fn-specs
so you have an existing registry for overrides of things you couldn't build automatically
also note that CLJ-1919 will add a new syntax for namespaced key destructuring.
your example there is not syntactically correct btw - the keys of :or
should always be unqualified symbols (matching the bindings that are created). there are some bugs in this area in current Clojure that will be fixed in CLJ-1919.
so that is, what you have there probably works now, but by accident not intent, and will change
what's the correct way to express to an fspec
that a function takes no arguments?
(s/cat)
I'd guess
maybe #{()}
would work too
alexmiller: there's no reason not to add specs to test.check is there?
assuming it accounts for older clojures
I guess this question is a superset of seancorfield's question on the ML, but also about test.check in particular since it's used in clojure.spec
I haven't moved forward with that since the first cut. Want to see more discussion on the ML first.
(sorry if I don't follow up for a few hours -- doors closing en route for a cat show!)
None other than that it then requires Clojure 1.9
Which requires test.check
okay, cool; I'll probably do a separate .specs
namespace like sean did
Anyone tried writing specs for stateful objects like database connections, or a Datomic database?
I can't think of a nice way to specify a function takes a db
, and some other args. To generate a db
I need a database connection that isn't available when I define my specs.
Imagine a trivial example like this:
(s/fdef load-entity
:args (s/cat :db ::d/db :tx ::entity-tx)
:ret ::entity)
(defn load-entity
[db tx]
(d/entity db [:entity/id (:entity/id tx)]))
I can't generate Datomic entities either. I need a DB, which needs a connection, which needs a URI.
Is there a way to say a spec can't be generated automatically so I can test other specs in this namespace maybe?
(s/def ::db
(s/with-gen #(instance? datomic.db.Db %)
(fn []
(gen/fmap (fn [facts] (-> (helpers/empty-db)
(helpers/transact facts)))
entities-generator))))
(s/def ::db
(s/with-gen #(instance? datomic.db.Db %)
(fn []
(gen/fmap (fn [facts] (-> (helpers/empty-db)
(helpers/transact facts)))
entities-generator))))
I've got basic predicate fns like these:
(defn db?
[x]
(instance? datomic.Database x))
(defn entity?
[x]
(instance? datomic.Entity x))
No, we just generate random URIs, we use in-memory databases for development / testing
That is global state. My Datomic connection is managed with components, and they're stopped/started around tests.
I could have two Datomic databases with separate connections. That wouldn't be possible with (helpers/empty-db)
.
actually, we do use mount for certain state, and rely on dynamic values using test/use-fixtures
your predicates look fine, but you won’t be able to use them for generating out-of-the-box, will need to use s/with-gen
https://clojurians.slack.com/archives/clojure-spec/p1465578891000846 I've been trying to figure this out also. My solutions have involved macros and sideband data. Not elegant at all. I also ignored generators.
seancorfield: were you thinking of having a conditional require
in the .jdbc
namespace? otherwise you'd have the problem of up-to-date users having to opt-in to the specs
when there are use cases for the specs besides explicit testing, e.g. clojure.repl/doc
I’ve been idly thinking about the problem of db args as well, though in the sql context. The problem seems the same for datomic and jdbc though: a spec saying the db
arg is e.g. a jdbc connection isn’t sufficient. You really want a spec that says this value is a jdbc connection to a database with at least a certain schema and maybe even a certain set of entities.
@donaldball: I'm using this at the mo:
(defn- with-datomic
[f]
(let [running (-> (config/read-config :test)
(assoc :uri (str "datomic:mem://" (UUID/randomUUID)))
map->Datomic
component/start)]
(try
(f running)
(finally
(component/stop running)))))
(defn entity?
[x]
(instance? datomic.Entity x))
(s/def ::d/entity
(s/with-gen
entity?
(fn [] (with-datomic (fn [{:keys [conn]}]
(gen/fmap
#(d/entity (d/db conn) %)
gen/int))))))
Don't love it if I'm honest. Creating a new connection for every test is pretty inefficient, but it works.
oh woah
I hadn't thought about this generator setup encouraging people to use stateful resources in their generators
@gfredericks: I'd say that the encouragement and bias towards namespace level hoisting (with s/def) takes away our ability to lexically scope and generally pass around explicit arguments. Diving too deeply into that statement takes you to mount vs component.
dominicm: the way I've used generators in the past is purely data-driven, so there's not even component-like stuff until the test starts running; but if you have a spec that's explicitly for a stateful thing, then you can't write a generator for it that way
my gut would be to try to keep doing the data-driven thing, and so not use generators for specs that describe stateful things
not sure how easy that is with datomic
a datomic entity is a map of attributes, is that right?
if that's the case you could make most of your specs just expect maps
and could do low-level testing with maps instead of entities
@gfredericks: It's definitely difficult to manage with generators. The discussion (in my opinion) transcends just testing. Doing explicit s/explain-data
on a runtime database is also a use-case.
that should work with map specs though I would think?
(s/explain-data {:email "
The unique email checker needs a database to make it's check. If I make it part of the map data (assoc m ::db (d/db conn))
, then when I check, the returned data gives me an incorrect path to the error position.
I have just got a macro working, which would take the error generated, and readjust the path for you. But it's still somewhat unnatural.
so your spec is making database queries?
I think specs should be pure functions
something that's not a pure function can be a plain ole test :)
I need to write a script to generate some Datomic seed data, and I'm experimenting with using spec to do so. Two questions:
1) One part of the seed data I need to write is the key/val that'll generate a temporary db/id. If I were hand writing the seed data, it would look like {:db/id #db/id[:db.part/user -1015948] ... }
. Can anyone help me understand how I would go about creating a spec for that?
2) ideally, it'd be nice to just spec that part of the schema, and then somehow use that to generate the seed data, but I'm not quite sure how go about that. Any hints?
(or at least that's the spec for the tagged literal itself. I'm not quite sure how to get from that to a spec that'll produce seed data as above. I guess I'll need to write a generator for it, but it's gonna take some experimentation for sure.
using spec just to generate data seems super weird, why wouldn't you use the generators from test.check or https://github.com/clojure/data.generators directly?