ruy 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,27 +1,35 @@
1
1
  module Ruy
2
2
  module Conditions
3
3
 
4
- # Expects that a context attribute will be less than or equal to the given value.
4
+ # Expects that a context attribute will be less than or equal
5
+ # to the given value
6
+ #
5
7
  class LessThanOrEqual < Condition
6
- attr_reader :attr, :value
8
+ attr_reader :obj
7
9
 
8
- # @param attr Context attribute's name
9
- # @param value
10
- def initialize(value, attr)
10
+ # @param obj
11
+ # @param key
12
+ # @example check that :key is less than or equal to 5
13
+ # LessThanOrEqual.new(5, :key)
14
+ def initialize(obj, *key)
11
15
  super
12
- @value = value
13
- @attr = attr
14
- end
15
-
16
- def call(ctx)
17
- @value >= ctx.resolve(@attr)
16
+ @obj = obj
17
+ @key = key.first if key.any?
18
18
  end
19
19
 
20
20
  def ==(o)
21
21
  o.kind_of?(LessThanOrEqual) &&
22
- attr == o.attr &&
23
- value == o.value
22
+ o.obj == @obj &&
23
+ o.key == @key
24
+ end
25
+
26
+ protected
27
+
28
+ # @see Condition#evaluate
29
+ def evaluate(value)
30
+ @obj >= value
24
31
  end
32
+
25
33
  end
26
34
  end
27
35
  end
@@ -0,0 +1,32 @@
1
+ module Ruy
2
+ module Conditions
3
+
4
+ # Iterates over an Enumerable evaluating that some value
5
+ # matches the set of sub-conditions
6
+ #
7
+ class Some < CompoundCondition
8
+
9
+ # @param key
10
+ # @example check that at least a value from :key matches the sub-conditions
11
+ # Some.new(:key)
12
+ def initialize(*key)
13
+ super
14
+ @key = key.first if key.any?
15
+ end
16
+
17
+ # @see CompoundCondition#evaluate
18
+ def evaluate(enum)
19
+ enum.any? do |ctx|
20
+ new_ctx = Ruy::Context.new(ctx)
21
+
22
+ Ruy::Utils::Rules.evaluate_conditions(conditions, new_ctx)
23
+ end
24
+ end
25
+
26
+ def ==(o)
27
+ super &&
28
+ o.key == @key
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,67 +1,59 @@
1
1
  module Ruy
2
2
  module Conditions
3
3
 
4
- # Evaluates a block using comparison matchers that receive time-related objects with time zone awareness.
4
+ # Evaluates a block using comparison matchers
5
+ # that receive time-related objects with time zone awareness
5
6
  #
6
7
  # @note Does not support time zone-aware matchers inside sub-blocks.
7
- # A workaround for this is always surrounding your time zone-aware matchers by a 'tz' block even in sub-blocks
8
- # already surrounded by one.
8
+ # A workaround for this is always surrounding your
9
+ # time zone-aware matchers by a 'tz' block even in sub-blocks
10
+ # already surrounded by one.
9
11
  class TZ < CompoundCondition
10
- # @param [String] tz_identifier String representing IANA's time zone identifier.
12
+
13
+ # @param tz_identifier [String]
14
+ # String representing IANA's time zone identifier.
15
+ # @example evaluate all the sub-conditions under CST
16
+ # TZ.new('CST')
11
17
  def initialize(tz_identifier)
12
18
  super
13
19
  @tz_identifier = tz_identifier
14
20
  end
15
21
 
16
- # @param [Ruy::VariableContext] ctx
17
- def call(ctx)
18
- conditions.all? do |condition|
19
- condition.call(ctx)
20
- end
22
+ # @see Ruy::Conditions::DayOfWeek#initialize
23
+ def day_of_week(dow, key)
24
+ conditions << DayOfWeek.new(dow, key, @tz_identifier)
21
25
  end
22
26
 
23
- # Adds a DayOfWeek condition
24
- #
25
- # @param (see Ruy::Conditions::DayOfWeek#initialize)
26
- def day_of_week(dow, attr)
27
- conditions << DayOfWeek.new(dow, attr, @tz_identifier)
27
+ # @see Ruy::Conditions::Eq#initialize
28
+ def eq(pattern, *key)
29
+ super(TimePattern.new(pattern, @tz_identifier), *key)
28
30
  end
29
31
 
30
- # Intercepts an 'eq' call to the superclass and enhances its arguments
31
- #
32
- # @param (see Ruy::Conditions::Eq#initialize)
33
- def eq(pattern, attr, &block)
34
- super(TimePattern.new(pattern, @tz_identifier), attr, &block)
35
- end
36
32
 
37
- # TODO Add Greater condition call here
33
+ # @see Ruy::Conditions::GreaterThanOrEqual#initialize
34
+ def greater_than(pattern, *key)
35
+ super(TimePattern.new(pattern, @tz_identifier), *key)
36
+ end
38
37
 
39
- # Intercepts a 'greater_than_or_equal' call to the superclass and enhances its arguments
40
- #
41
- # @param (see Ruy::Conditions::GreaterThanOrEqual#initialize)
42
- def greater_than_or_equal(pattern, attr, &block)
43
- super(TimePattern.new(pattern, @tz_identifier), attr, &block)
38
+ # @see Ruy::Conditions::GreaterThanOrEqual#initialize
39
+ def greater_than_or_equal(pattern, *key)
40
+ super(TimePattern.new(pattern, @tz_identifier), *key)
44
41
  end
45
42
 
46
- # Intercepts a 'less_than' call to the superclass and enhances its arguments
47
- #
48
- # @param (see Ruy::Conditions::LessThan#initialize)
49
- def less_than(pattern, attr, &block)
50
- super(TimePattern.new(pattern, @tz_identifier), attr, &block)
43
+ # @see Ruy::Conditions::LessThan#initialize
44
+ def less_than(pattern, *key)
45
+ super(TimePattern.new(pattern, @tz_identifier), *key)
51
46
  end
52
47
 
53
- # Intercepts a 'less_than_or_equal' call to the superclass and enhances its arguments
54
- #
55
- # @param (see Ruy::Conditions::LessThanOrEqual#initialize)
56
- def less_than_or_equal(pattern, attr, &block)
57
- super(TimePattern.new(pattern, @tz_identifier), attr, &block)
48
+ # @see Ruy::Conditions::LessThanOrEqual#initialize
49
+ def less_than_or_equal(pattern, *key)
50
+ super(TimePattern.new(pattern, @tz_identifier), *key)
58
51
  end
59
52
 
60
- # Intercepts a 'between' call to the superclass and enhances its arguments
61
- #
62
- # @param (see Ruy::Conditions::Between#initialize)
63
- def between(from, to, attr, &block)
64
- super(TimePattern.new(from, @tz_identifier), TimePattern.new(to, @tz_identifier), attr, &block)
53
+ # @see Ruy::Conditions::Between#initialize)
54
+ def between(from, to, *key)
55
+ super(TimePattern.new(from, @tz_identifier),
56
+ TimePattern.new(to, @tz_identifier), *key)
65
57
  end
66
58
 
67
59
  end
data/lib/ruy/context.rb CHANGED
@@ -1,28 +1,57 @@
1
1
  module Ruy
2
- # Context that resolves lets and values access.
2
+ class UnexpectedObjectError < StandardError; end
3
+
4
+ # Wraps an evaluation object and stores lazy variables.
5
+ # It supports hierarchical structures that can be navigated
6
+ # using the #[] method.
7
+ #
3
8
  class Context
4
9
 
5
- # @param [Hash] ctx
6
- # @param [Array] lets Names of the 'let' variables
7
- def initialize(ctx, lets = [])
8
- @ctx = ctx
9
- @lets = lets
10
+ attr_reader :obj
11
+ alias_method :object, :obj
10
12
 
13
+ # @param obj
14
+ # @param lets [Array<Symbol>]
15
+ # @example a Hash context
16
+ # Context.new({key: 5})
17
+ # @example a Hash context and expected lazy variables
18
+ # Context.new({key: 5}, [:lazy_a, :lazy_b])
19
+ # @example a Fixnum context
20
+ # Context.new(5)
21
+ def initialize(obj, lets = [])
22
+ @obj = obj
23
+ @lets = lets
11
24
  @resolved_lets = {}
12
25
  end
13
26
 
14
- # Resolve the given attr from the lets or the context.
27
+ # Returns true if the given key can be resolved in the current context
28
+ #
29
+ # @param key
30
+ #
31
+ # @return [Boolean]
32
+ def include?(key)
33
+ @obj.kind_of?(Hash) && @obj.include?(key)
34
+ end
35
+
36
+ # Resolves an attribute key to its corresponding value
15
37
  #
16
- # @param [Symbol] attr
38
+ # @param key
17
39
  #
18
- # @return [Object]
19
- # @return [nil] when attribute cannot be resolved
20
- def resolve(attr)
21
- if @lets.include?(attr)
22
- @resolved_lets[attr] ||= @ctx.instance_exec(&@ctx[attr])
40
+ # @return the value
41
+ # @return [nil] if key cannot be resolved
42
+ # @raise UnexpectedObjectError if object in context is not a Hash
43
+ def resolve(key)
44
+ if !@obj.kind_of?(Hash)
45
+ raise UnexpectedObjectError, 'Object in context is not a Hash instance'
46
+ end
47
+
48
+ # Lazy variables have precedence over context attributes
49
+ if @lets.include?(key)
50
+ @resolved_lets[key] ||= @obj.instance_exec(&@obj[key])
23
51
  else
24
- @ctx.fetch(attr) { |key| @ctx[key.to_s] }
52
+ @obj[key]
25
53
  end
26
54
  end
55
+
27
56
  end
28
57
  end
data/lib/ruy/dsl.rb CHANGED
@@ -24,29 +24,51 @@ module Ruy
24
24
  # Adds an Assert condition.
25
25
  #
26
26
  # @param (see Conditions::Assert#initialize)
27
- def assert(attr)
28
- self.conditions << Conditions::Assert.new(attr)
27
+ def assert(*attrs)
28
+ self.conditions << Conditions::Assert.new(*attrs)
29
29
  end
30
30
 
31
31
  # Adds a Between condition.
32
32
  #
33
33
  # @param (see Conditions::Between#initialize)
34
- def between(from, to, attr, &block)
35
- self.conditions << Conditions::Between.new(from, to, attr, &block)
34
+ def between(from, to, *attrs)
35
+ self.conditions << Conditions::Between.new(from, to, *attrs)
36
+ end
37
+
38
+ # Adds a Cond condition.
39
+ #
40
+ # @yield Evaluates the given block in the context of the new rule
41
+ def cond(&block)
42
+ cond = Conditions::Cond.new
43
+ cond.instance_exec(&block)
44
+
45
+ self.conditions << cond
46
+ end
47
+
48
+ # Adds a Dig condition.
49
+ #
50
+ # @param (see Conditions::Dig#initialize)
51
+ #
52
+ # @yield Evaluates the given block in the context of the new rule
53
+ def dig(*attrs, &block)
54
+ condition = Conditions::Dig.new(*attrs)
55
+ condition.instance_exec(&block)
56
+
57
+ self.conditions << condition
36
58
  end
37
59
 
38
60
  # Adds an Eq condition.
39
61
  #
40
62
  # @param (see Conditions::Eq#initialize)
41
- def eq(value, attr, &block)
42
- self.conditions << Conditions::Eq.new(value, attr, &block)
63
+ def eq(value, *attrs)
64
+ self.conditions << Conditions::Eq.new(value, *attrs)
43
65
  end
44
66
 
45
- # Adds a Cond condition.
67
+ # Adds an Every condition.
46
68
  #
47
- # @yield Evaluates the given block in the context of the new rule
48
- def cond(&block)
49
- cond = Conditions::Cond.new
69
+ # @param (see Conditions::Every#initialize)
70
+ def every(*attrs, &block)
71
+ cond = Conditions::Every.new(*attrs)
50
72
  cond.instance_exec(&block)
51
73
 
52
74
  self.conditions << cond
@@ -62,55 +84,65 @@ module Ruy
62
84
  # Adds a GreaterThan condition.
63
85
  #
64
86
  # @param (see Conditions::GreaterThanOrEqual#initialize)
65
- def greater_than(value, attr)
66
- self.conditions << Conditions::GreaterThan.new(value, attr)
87
+ def greater_than(value, *attrs)
88
+ self.conditions << Conditions::GreaterThan.new(value, *attrs)
67
89
  end
68
90
 
69
91
  # Adds a GreaterThanOrEqual condition.
70
92
  #
71
93
  # @param (see Conditions::GreaterThanOrEqual#initialize)
72
- def greater_than_or_equal(value, attr)
73
- self.conditions << Conditions::GreaterThanOrEqual.new(value, attr)
94
+ def greater_than_or_equal(value, *attrs)
95
+ self.conditions << Conditions::GreaterThanOrEqual.new(value, *attrs)
74
96
  end
75
97
 
76
98
  # Adds an In condition.
77
99
  #
78
100
  # @param (see Conditions::Include#initialize)
79
- def in(values, attr, &block)
80
- self.conditions << Conditions::In.new(values, attr, &block)
101
+ def in(values, *attrs)
102
+ self.conditions << Conditions::In.new(values, *attrs)
81
103
  end
82
104
 
83
105
  # Adds a InCyclicOrder condition.
84
106
  #
85
107
  # @param (see Conditions::InCyclicOrder#initialize)
86
- def in_cyclic_order(from, to, attr, &block)
87
- self.conditions << Conditions::InCyclicOrder.new(from, to, attr, &block)
108
+ def in_cyclic_order(from, to, *attrs)
109
+ self.conditions << Conditions::InCyclicOrder.new(from, to, *attrs)
88
110
  end
89
111
 
90
112
  # Adds an Include condition.
91
113
  #
92
114
  # @param (see Conditions::Included#initialize)
93
- def include(value, attr, &block)
94
- self.conditions << Conditions::Include.new(value, attr, &block)
115
+ def include(value, *attrs)
116
+ self.conditions << Conditions::Include.new(value, *attrs)
117
+ end
118
+
119
+ # Adds a LessThan condition.
120
+ #
121
+ # @param (see Conditions::LessThan#initialize)
122
+ def less_than(value, *attrs)
123
+ self.conditions << Conditions::LessThan.new(value, *attrs)
95
124
  end
96
125
 
97
126
  # Adds a LessOrEqualThan condition.
98
127
  #
99
128
  # @param (see Conditions::LessOrEqualThan#initialize)
100
- def less_than_or_equal(value, attr, &block)
101
- self.conditions << Conditions::LessThanOrEqual.new(value, attr)
129
+ def less_than_or_equal(value, *attrs)
130
+ self.conditions << Conditions::LessThanOrEqual.new(value, *attrs)
102
131
  end
103
132
 
104
- # Adds a LessThan condition.
133
+ # Adds a Some condition.
105
134
  #
106
- # @param (see Conditions::LessThan#initialize)
107
- def less_than(value, attr)
108
- self.conditions << Conditions::LessThan.new(value, attr)
135
+ # @param (see Conditions::Some#initialize)
136
+ def some(*attrs, &block)
137
+ cond = Conditions::Some.new(*attrs)
138
+ cond.instance_exec(&block)
139
+
140
+ self.conditions << cond
109
141
  end
110
142
 
111
143
  # Adds a TZ condition block
112
144
  #
113
- # @param [String] tz_identifier String representing IANA's
145
+ # @param tz_identifier [String] String representing IANA's
114
146
  # time zone identifier. Defaults to UTC if none passed.
115
147
  # @yield Evaluates the given block in the context of the TZ rule
116
148
  def tz(tz_identifier = 'UTC', &block)
@@ -120,7 +152,7 @@ module Ruy
120
152
  self.conditions << cond
121
153
  end
122
154
 
123
- # @param [Integer] indentation Indentation level
155
+ # @param indentation [Integer] Indentation level
124
156
  def to_s(indentation = 0)
125
157
  rule_name = Ruy::Utils::Naming.rule_name(self)
126
158
 
data/lib/ruy/outcome.rb CHANGED
@@ -1,31 +1,31 @@
1
1
  module Ruy
2
2
  # Represents a possible result (outcome) of evaluating a rule set
3
+ # Additionally, an outcome can be composed of rules. In that case,
4
+ # the outcome will evaluate to its value
5
+ # if all of them are evaluated successfully.
3
6
  #
4
- # Additionally, an outcome can be composed of rules. In that case, the outcome will evaluate to
5
- # its value if all of them are evaluated successfully.
6
7
  class Outcome
7
8
  include DSL
8
9
 
9
10
  attr_reader :value
10
11
 
11
- # @param value The value of this outcome
12
+ # @param value
13
+ # @example will return true if evaluation passes
14
+ # Outcome.new(true)
12
15
  def initialize(value)
13
16
  @value = value
14
17
  @root_condition = Ruy::Conditions::All.new
15
18
  end
16
19
 
17
- # Returns the value of this outcome is all of the conditions succeed.
18
- #
20
+ # Returns the value of this outcome if all of the conditions succeed.
19
21
  # It also returns the value when there's no conditions.
20
22
  #
21
- # @param [Ruy::Context] ctx
23
+ # @param ctx [Ruy::Context]
22
24
  #
23
- # @return [Object]
24
- # @return [nil] If some of the conditions does not succeeds
25
+ # @return the value
26
+ # @return [nil] if conditions are not met
25
27
  def call(ctx)
26
- if @root_condition.call(ctx)
27
- @value
28
- end
28
+ @value if @root_condition.call(ctx)
29
29
  end
30
30
 
31
31
  # @return [Array<Ruy::Condition>]