how can I ns-resolve symbol
a spec sitting in a different namespace based on a given string?
can you more clearly state inputs and output?
I want something like:
(ns-resolve
'my-specs
(symbol "foo"))
but for a spec. How can I resolve ::my-specs/foo
when given two strings “my-specs” and “foo”resolve to what?
a spec object?
a fq keyword?
yeah, let’s say I want to validate a spec, but instead of spec I have its name as a string
my-specs
is an alias only meaningful in the context of a namespace. is that the current namespace or some other one?
so let’s say I have a bunch of fields that I extracted from e.g. SQL query, it’s a vector of ["foo" "bar"]
. I have a namespace my-specs
with two specs in it: (s/def ::foo string?)
and (s/def ::bar boolean?)
… now I want to validate data, or generate data, basically I need to “get” those specs
all that programmatically
do you know that all of these specs are in my-specs
?
sure… let’s assume they are 100% there
if so (s/get-spec (keyword "my-specs" "foo"))
should work?
OMG… there’s a literally a function called get-spec
… LOL
Thank you Alex… sorry for being so lame at explaining
np, just trying to impedance match :)
however… what if I want to check if the spec is indeed there? 🙂
well if you get nil, it's not there :)
you can also call (s/registry)
to just get the full map too
ah… okay… awesome… exactly what I needed. Thanks again!
@ag expectations.clojure.test
does a dynamic lookup like that to let you "expect" values conform to specs: https://github.com/clojure-expectations/clojure-test/blob/master/src/expectations/clojure/test.clj#L34-L40
(the dynamic require/resolve is so the code can run on Clojure 1.8)
Oh… that’s nice. I’ll give a gander as well. Thank you Sean!
hey friends… another dynamic lookup related question:
if I have a (s/keys)
spec and string representation of one of the fields how do I get-spec of that?
e.g: I have:
::foo-spec/foo
defined as:
(s/def ::foo
(s/keys :req-un [::bar-spec/bar]))
and in bar-spec ns I have:
(s/def ::bar (s/keys :req-un [::name]))
and I have strings “foo-spec”, “foo” and “bar.name” what’s the best way to get-spec of ::bar/name ?
How can I make it work for multiple levels of nesting?meaning I can get-spec/foo
but now I need to “analyze” its :bar
field, I have no idea where it sits, is there a way to find it out?
dynamically?
there are multiple independent questions here
re understanding the structure of a keys spec, you can use s/form to get a fully resolved form representing the spec (so you'd see (clojure.spec.alpha/keys :req-un [:bar-spec/name])
)
finding the subspecs inside that spec is a matter of fishing for it (made somewhat complicated by the and
/ or
support in s/keys). There are a variety of ways to tackle that, all a little meh, that's something we're looking at having better answers for in spec 2.
if you go that path, you have only fully-qualified subspecs, so you can just pass them to s/get-spec
and re nesting, there is no such thing as ::bar/name in this case - you've just smooshed together two independent things there
> you’ve just smooshed together two independent things there ehmm… I’m just trying to illustrate that I needed nested lookup…
just saying that the specs themselves are not "nested" and have no naming relationship. spec references are always via fully qualified keywords (even if you use autoresolved names to specify them)
yeah, it seems this isn’t as simple as I thought it would be… so basically I wanted to figure out specs for a vector of strings like:
["account.id" "account.contact.first-name" "account.contact.address.city"]
given that all specs are there with the relations set between themwhat do those strings mean?
those are nested keys in map data or something?
so there’s:
(s/def account (s/keys :req-un [::id ::contact])
(s/def contact (s/keys :req-un [::first-name ::address])
(s/def address (s/keys :req-un [::city])
and they all may be sitting in different namespaces
now without knowing the ns of city
or contact
but knowing where account
resides and given a string “account.contact.address.city” I want to get-spec of ::address/city
oh.. well, it gets a tad bit crazier when some fields are s/nilable
the whole point of having namespaces is being able to differentiate things. seems like you're working really hard to rebuild what that gives you.
the whole idea behind spec is that attributes are the main source of semantics and maps are weaker aggregations of those
you are working significantly at odds with that idea
So if you want a spec that describes a relational data, how would you do it?
generically, relational data is sets of maps
@ag it would probably help you to remember to use explicit qualifiers (on keywords) in your spec definition and stop using ::
at least until you get your head around this...
so the above snippet with account -> contact -> address is okay?
(s/def :person/account (s/keys :req-un [:account/id :account/contact])
(s/def :account/contact (s/keys :req-un [:contact/first-name :contact/address])
(s/def :contact/address (s/keys :req-un [:address/city])
for example ^That also gets you close to the "relational" model if you look at something like next.jdbc
querying a database since it will use qualified keywords for column names, based on the table they are in...
(although there you have to reify the primary keys so you'd most likely have :account/id
:account/contact_id
and the latter would be the FK into the :contact
table and its :contact/id
column etc)
If you did the join
with next.jdbc/execute!
you'd end up with a flat map containing
{:account/id 123, :contact/first-name "Ag", :address/city "Wherever"}
yeah, I see how this can simplify a few things.