lib/speculation/spec_impl/hash_spec.rb in speculation-0.1.0 vs lib/speculation/spec_impl/hash_spec.rb in speculation-0.2.0

- old
+ new

@@ -1,12 +1,10 @@ # frozen_string_literal: true module Speculation - using Speculation::NamespacedSymbols.refine(self) - using Conj - # @private class HashSpec < SpecImpl + include NamespacedSymbols S = Speculation attr_reader :id def initialize(req, opt, req_un, opt_un) @@ -17,31 +15,32 @@ @opt_un = opt_un req_keys = req.flat_map(&method(:extract_keys)) req_un_specs = req_un.flat_map(&method(:extract_keys)) - unless (req_keys + req_un_specs + opt + opt_un).all? { |s| s.is_a?(Symbol) && s.namespace } + all_keys = req_keys + req_un_specs + opt + opt_un + unless all_keys.all? { |s| s.is_a?(Symbol) && NamespacedSymbols.namespace(s) } raise "all keys must be namespaced Symbols" end req_specs = req_keys + req_un_specs req_keys += req_un_specs.map(&method(:unqualify_key)) pred_exprs = [Utils.method(:hash?)] - pred_exprs.push(->(v) { parse_req(req, v, :itself.to_proc) == true }) if req.any? - pred_exprs.push(->(v) { parse_req(req_un, v, method(:unqualify_key)) == true }) if req_un.any? + pred_exprs.push(->(v) { parse_req(req, v, Utils.method(:itself)).empty? }) if req.any? + pred_exprs.push(->(v) { parse_req(req_un, v, method(:unqualify_key)).empty? }) if req_un.any? @req_keys = req_keys @req_specs = req_specs @opt_keys = opt + opt_un.map(&method(:unqualify_key)) @opt_specs = opt + opt_un @keys_pred = ->(v) { pred_exprs.all? { |p| p.call(v) } } @key_to_spec_map = Hash[req_keys.concat(@opt_keys).zip(req_specs.concat(@opt_specs))] end def conform(value) - return :invalid.ns unless @keys_pred.call(value) + return ns(S, :invalid) unless @keys_pred.call(value) reg = S.registry ret = value value.each do |key, v| @@ -51,11 +50,11 @@ next unless spec conformed_value = S.conform(spec, v) if S.invalid?(conformed_value) - return :invalid.ns + return ns(S, :invalid) else unless conformed_value.equal?(v) ret = ret.merge(key => conformed_value) end end @@ -64,42 +63,39 @@ ret end def explain(path, via, inn, value) unless Utils.hash?(value) - return [{ :path => path, :pred => :hash?, :val => value, :via => via, :in => inn }] + return [{ :path => path, :pred => [Utils.method(:hash?), [value]], :val => value, :via => via, :in => inn }] end problems = [] if @req.any? - valid_or_failure = parse_req(@req, value, :itself.to_proc) + failures = parse_req(@req, value, Utils.method(:itself)) - unless valid_or_failure == true - valid_or_failure.each do |failure_sexp| - pred = sexp_to_rb(failure_sexp) - problems << { :path => path, :pred => pred, :val => value, :via => via, :in => inn } - end + failures.each do |failure_sexp| + # eww + pred = [Utils.method(:key?), [sexp_to_rb(failure_sexp)]] + problems << { :path => path, :pred => pred, :val => value, :via => via, :in => inn } end end if @req_un.any? - valid_or_failure = parse_req(@req_un, value, method(:unqualify_key)) + failures = parse_req(@req_un, value, method(:unqualify_key)) - unless valid_or_failure == true - valid_or_failure.each do |failure_sexp| - pred = sexp_to_rb(failure_sexp) - problems << { :path => path, :pred => pred, :val => value, :via => via, :in => inn } - end + failures.each do |failure_sexp| + pred = [Utils.method(:key?), [sexp_to_rb(failure_sexp)]] + problems << { :path => path, :pred => pred, :val => value, :via => via, :in => inn } end end problems += value.flat_map { |(k, v)| next unless S.registry.key?(@key_to_spec_map[k]) unless S.pvalid?(@key_to_spec_map.fetch(k), v) - S.explain1(@key_to_spec_map.fetch(k), path.conj(k), via, inn.conj(k), v) + S.explain1(@key_to_spec_map.fetch(k), Utils.conj(path, k), via, Utils.conj(inn, k), v) end } problems.compact end @@ -113,25 +109,25 @@ rmap = S.inck(rmap, @id) reqs = @req_keys.zip(@req_specs). reduce({}) { |m, (k, s)| - m.merge(k => S.gensub(s, overrides, path.conj(k), rmap)) + m.merge(k => S.gensub(s, overrides, Utils.conj(path, k), rmap)) } opts = @opt_keys.zip(@opt_specs). reduce({}) { |m, (k, s)| if S.recur_limit?(rmap, @id, path, k) m else - m.merge(k => Gen.delay { S.gensub(s, overrides, path.conj(k), rmap) }) + m.merge(k => Gen.delay { S.gensub(s, overrides, Utils.conj(path, k), rmap) }) end } ->(rantly) do count = rantly.range(0, opts.count) - opts = opts.to_a.shuffle.take(count).to_h + opts = Hash[opts.to_a.shuffle.take(count)] reqs.merge(opts).each_with_object({}) { |(k, spec_gen), h| h[k] = spec_gen.call(rantly) } end @@ -146,21 +142,21 @@ rb_string << "(" unless level.zero? keys.each_with_index do |key, i| unless i.zero? - rb_string << " #{op.name} " + rb_string << " #{NamespacedSymbols.name(op)} " end - rb_string << sexp_to_rb(key, level + 1) + rb_string << sexp_to_rb(key, level + 1).to_s end rb_string << ")" unless level.zero? rb_string else - ":#{sexp}" + sexp end end def extract_keys(symbol_or_arr) if symbol_or_arr.is_a?(Array) @@ -169,45 +165,41 @@ symbol_or_arr end end def unqualify_key(x) - x.name.to_sym + NamespacedSymbols.name(x).to_sym end def parse_req(ks, v, f) key, *ks = ks ret = if key.is_a?(Array) op, *kks = key case op - when :or.ns - if kks.one? { |k| parse_req([k], v, f) == true } - true + when ns(S, :or) + if kks.one? { |k| parse_req([k], v, f).empty? } + [] else [key] end - when :and.ns - if kks.all? { |k| parse_req([k], v, f) == true } - true + when ns(S, :and) + if kks.all? { |k| parse_req([k], v, f).empty? } + [] else [key] end else raise "Expected or, and, got #{op}" end elsif v.key?(f.call(key)) - true + [] else [key] end if ks.any? - if ret == true - parse_req(ks, v, f) - else - ret + parse_req(ks, v, f) - end + ret + parse_req(ks, v, f) else ret end end end