diff --git a/README.md b/README.md index cad534a..9f45215 100644 --- a/README.md +++ b/README.md @@ -1547,6 +1547,18 @@ schema.with_context(five: 15).(nil) # => Datacaster::ValidResult(nil) ``` +Method `has_key?` could be used to determine whether key is available in the context + +```ruby +schema = + Datacaster.schema do + check { context.has_key?(:five) } + end + +schema.with_context(five: 15).(nil) +# => Datacaster::ValidResult(nil) +``` + **Note** `context` can be accesed only in casters' blocks. It can't be used in schema definition itself: diff --git a/lib/datacaster/runtimes/user_context.rb b/lib/datacaster/runtimes/user_context.rb index e04f610..2fc1eb1 100644 --- a/lib/datacaster/runtimes/user_context.rb +++ b/lib/datacaster/runtimes/user_context.rb @@ -4,6 +4,10 @@ module Datacaster module Runtimes class UserContext < Base class ContextStruct + def self.context_has_key?(context, key) + context.respond_to?(:key?) && context.key?(key) || context.to_h.key?(key.to_sym) + end + def initialize(context, node) @context = context @node = node @@ -14,10 +18,7 @@ def method_missing(m, *args) return super end - key_present = @context.respond_to?(:key?) && @context.key?(m) || - @context.to_h.key?(m.to_sym) - - if key_present && args.empty? + if self.class.context_has_key?(@context, m) && args.empty? return @context[m] end @@ -31,6 +32,12 @@ def method_missing(m, *args) raise NoMethodError.new("Key #{m.inspect} is not found in the context") end end + + def has_key?(key) + self.class.context_has_key?(@context, key) || @node.class.send_to_parent(@node, :context).has_key?(key) + rescue NoMethodError + false + end end def initialize(parent, user_context) diff --git a/lib/datacaster/version.rb b/lib/datacaster/version.rb index a428d4a..1f62be2 100644 --- a/lib/datacaster/version.rb +++ b/lib/datacaster/version.rb @@ -1,3 +1,3 @@ module Datacaster - VERSION = "4.0.1" + VERSION = "4.1.0" end diff --git a/spec/user_context_spec.rb b/spec/user_context_spec.rb index c877415..b0cb2f0 100644 --- a/spec/user_context_spec.rb +++ b/spec/user_context_spec.rb @@ -79,4 +79,29 @@ expect(schema.with_context(d: 2, e: 3).({a: 1, b: {c: {d: 10}}, e: [3]}).to_dry_result).to eq Failure({b: {c: {d: ["is invalid"]}}}) end end + + describe "using #has_key?" do + it "works with deep nesting" do + schema = described_class.schema do + check { context.has_key?(:b) && context.has_key?(:c) && context.has_key?(:d) && !context.has_key?(:e) }. + with_context(b: 1). + with_context(c: 2). + with_context(d: 3) + end + + expect(schema.with_context(a: 1).(1).to_dry_result).to eq Success(1) + end + + it "works with complex schemas" do + schema = described_class.schema do + hash_schema( + a: check { context.has_key?(:a) && context.has_key?(:d) && context.has_key?(:e) && !context.has_key?(:f) }, + b: {c: {d: check { context.has_key?(:a) && context.has_key?(:d) && context.has_key?(:e) && !context.has_key?(:t) }}}, + e: [check { |v| context.has_key?(:a) && context.has_key?(:d) && context.has_key?(:e) && !context.has_key?(:p) }] + ).with_context(a: 1) + end + + expect(schema.with_context(d: 2, e: 3).({a: 1, b: {c: {d: 10}}, e: [3]}).to_dry_result).to eq Success({:a=>1, :b=>{:c=>{:d=>10}}, :e=>[3]}) + end + end end