ruy 0.3.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a3f59c87798c8d6d5aa92ca7639e4605f60c36c6
4
- data.tar.gz: 4d51c3dd0c5e0a9d7c4a8175caf38eccb19e7763
3
+ metadata.gz: 062667c3bb0ac6a9796f8bbda6dded7918ec9b11
4
+ data.tar.gz: bd51df8ff61cab98c007849cf4a1079dc8f83956
5
5
  SHA512:
6
- metadata.gz: 71e65bfa1ace338acdcefafbe9a234e5c415bdc1b8075c330fe4831e8f6cebfbd3f70a435656266c7604338c0180692f39af888d1bf7df6f64088233446b550b
7
- data.tar.gz: 8c8bb8c7db01b3254d83f53fd44a41dd9257fd2544929dc513360a27ad5805d38676e4568a9d868b6946efe525c7db4f0d101128463eab98a40cfe22f624f0f2
6
+ metadata.gz: e48d547cdb0e47a50ede40c794d3a90eb012ab27c34e31aba0d696ae3936bcbdd0a0501887ba269833135b61e70b34d684892ff84e1e698918f54c9d9c9c7a2e
7
+ data.tar.gz: 78a9ecccf5c5f9f0549f7c53fa2e3515d7d900afe4695c1b52f7e5b2a019c38158e385e2daa088a7c716a396d7c31c6e3b06f70185b9d9a42b0dc756450f921a
data/README.md CHANGED
@@ -4,7 +4,9 @@ Ruy is a library for defining a set of conditions and evaluating them against a
4
4
 
5
5
  ``` ruby
6
6
  # discount_day.rb
7
- gifter = Ruy::RuleSet.new
7
+ gifter = Ruy::Rule.new
8
+
9
+ gifter.set :name, 'Unforgettable Fridays'
8
10
 
9
11
  gifter.eq :friday, :day_of_week
10
12
 
@@ -21,7 +23,7 @@ gifter.outcome 3
21
23
  gifter.fallback 0
22
24
  ```
23
25
 
24
- RuleSets are evaluated against a context (the `Hash` being passed to `#call`) and return the first outcome that matches.
26
+ Rules are evaluated against a context (the `Hash` being passed to `#call`) and return the first outcome that matches.
25
27
 
26
28
  ``` ruby
27
29
  gifter.call(day_of_week: :friday, amount: 314)
@@ -49,6 +51,13 @@ gifter.call(day_of_week: :monday, amount: 124)
49
51
  # => 0
50
52
  ```
51
53
 
54
+ Retrieve rule attributes.
55
+ ``` ruby
56
+ gifter.get(:name)
57
+
58
+ # => 'Unforgettable Fridays'
59
+ ```
60
+
52
61
  ## Key concepts
53
62
 
54
63
  Ruy at its core is about evaluating a set of conditions against a context in order to return a result.
@@ -70,46 +79,28 @@ Available conditions:
70
79
  - greater_than *Tests that context value is greater than something*
71
80
  - greater_than_or_equal *Tests that context value is greater than or equal to something*
72
81
  - in *A context value must belong to a specified list of values*
73
- - in_cyclic_order *TBD*
82
+ - in_cyclic_order *Expects that a value is included in a cyclic order*
74
83
  - include *The context attribute must include a specified value*
75
84
  - less_than_or_equal *Tests that context value is less than or equal to something*
76
85
  - less_than *Tests that context value is less than something*
77
86
 
78
- Conditions can be nested. In such case, for the nesting condition to be met, the nested conditions must
79
- also be met.
80
-
81
- ``` ruby
82
- between 0, 1_000, :amount do
83
- eq :friday, :day_of_week
84
- end
85
- ```
86
-
87
- is equivalent to:
88
-
89
- ``` ruby
90
- all do
91
- between 0, 1_000, :amount
92
- eq :friday, :day_of_week
93
- end
94
- ```
87
+ ### Rules
95
88
 
96
- ### Rulsets
97
-
98
- A ruleset is a set of conditions that must suffice and returns a value resulting from either an
89
+ A Rule is a set of conditions that must suffice and returns a value resulting from either an
99
90
  outcome or a fallback.
100
91
 
101
92
  ### Contexts
102
93
 
103
- A context is a `Hash` from which values are fetched in order to evaluate a ruleset.
94
+ A context is a `Hash` from which values are fetched in order to evaluate a Rule.
104
95
 
105
96
  ### Lazy values
106
97
 
107
- Rulesets can define lazy values. The context must provide a proc which is evaluted only once the first time the value is needed. The result returned by the proc is memoized and used to evaluate subsequent conditions.
98
+ Rules can define lazy values. The context must provide a proc which is evaluted only once the first time the value is needed. The result returned by the proc is memoized and used to evaluate subsequent conditions.
108
99
 
109
100
 
110
101
  ``` ruby
111
102
  # premium_discount_day.rb
112
- gifter = Ruy::RuleSet.new
103
+ gifter = Ruy::Rule.new
113
104
 
114
105
  gifter.let :amounts_average # an expensive calculation
115
106
 
@@ -125,23 +116,23 @@ gifter.call(day_of_week: :friday, amounts_average: -> { Stats::Amounts.compute_a
125
116
  ```
126
117
  ### Outcomes
127
118
 
128
- An outcome is the result of a successful ruleset evaluation. An outcome can also have nested
119
+ An outcome is the result of a successful Rule evaluation. An outcome can also have nested
129
120
  conditions, in such case, if the conditions meet, the outcome value is returned.
130
121
 
131
- A RuleSet can have multiple outcomes, the first matching one is returned.
122
+ A Rule can have multiple outcomes, the first matching one is returned.
132
123
 
133
124
  ### Time Zone awareness
134
125
 
135
126
  When it comes to matching times in different time zones, Ruy is bundled with a built in `tz` block that will enable specific matchers to make time zone-aware comparisons.
136
127
 
137
128
  ```ruby
138
- ruleset = Ruy::RuleSet.new
129
+ rule = Ruy::Rule.new
139
130
 
140
- ruleset.tz 'America/New_York' do
131
+ rule.tz 'America/New_York' do
141
132
  eq '2015-01-01T00:00:00', :timestamp
142
133
  end
143
134
 
144
- ruleset.outcome 'Happy New Year, NYC!'
135
+ rule.outcome 'Happy New Year, NYC!'
145
136
  ```
146
137
 
147
138
  For example, if the timestamp provided in the context is a Ruby Time object in UTC (zero offset from UTC), `eq` as child of a `tz` block will take the time zone passed as argument to the block (`America/New_York`) to calculate the current offset and make the comparison.
@@ -157,9 +148,9 @@ Where the time zone identifier is optional, but if you specify it, will take pre
157
148
  Inside any `tz` block, there's a matcher to look for a specific day of the week in the time zone of the block.
158
149
 
159
150
  ```ruby
160
- ruleset = Ruy::RuleSet.new
151
+ rule = Ruy::Rule.new
161
152
 
162
- ruleset.any do
153
+ rule.any do
163
154
  tz 'America/New_York' do
164
155
  day_of_week :saturday, :timestamp
165
156
  end
@@ -169,7 +160,7 @@ ruleset.any do
169
160
  end
170
161
  end
171
162
 
172
- ruleset.outcome 'Have a nice weekend, NYC!'
163
+ rule.outcome 'Have a nice weekend, NYC!'
173
164
  ```
174
165
 
175
166
  This matcher supports both the `Symbol` and number syntax in the range `(0..6)` starting on Sunday.
@@ -183,15 +174,20 @@ You cannot use matchers inside nested blocks in a `tz` block expecting them to w
183
174
  A possible workaround for this is to use `tz` blocks inside the nested block in question:
184
175
 
185
176
  ```ruby
186
- ruleset = Ruy::RuleSet.new
187
- any do
177
+ rule = Ruy::Rule.new
178
+
179
+ rule.any do
188
180
  tz 'America/New_York' { eq '2015-01-01T00:00:00', :timestamp }
189
181
  tz 'America/New_York' { eq '2015-01-01T02:00:00zUTC', :timestamp }
190
182
  end
191
183
 
192
- ruleset.outcome 'Happy New Year, NYC!'
184
+ rule.outcome 'Happy New Year, NYC!'
193
185
  ```
194
186
 
195
187
  Support for time zone awareness in nested blocks inside `tz` blocks is planned. This workaround could stop working in future versions; use it at your own risk.
196
188
 
197
189
  Ruy depends on [TZInfo](http://tzinfo.github.io/ "TZ Info website") to calculate offsets using IANA's Time Zone Database. Check their website for information about time zone identifiers.
190
+
191
+ ### Documentation
192
+
193
+ [RubyDoc.info](http://www.rubydoc.info/github/moove-it/ruy)
data/lib/ruy.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require_relative 'ruy/utils'
2
2
  require_relative 'ruy/dsl'
3
3
  require_relative 'ruy/rule'
4
- require_relative 'ruy/rule_set'
5
4
  require_relative 'ruy/conditions'
6
5
  require_relative 'ruy/context'
7
6
  require_relative 'ruy/outcome'
@@ -1,3 +1,5 @@
1
+ require_relative 'conditions/condition'
2
+ require_relative 'conditions/compound_condition'
1
3
  require_relative 'conditions/all'
2
4
  require_relative 'conditions/any'
3
5
  require_relative 'conditions/assert'
@@ -2,12 +2,7 @@ module Ruy
2
2
  module Conditions
3
3
 
4
4
  # Expects that all rules will succeed.
5
- class All < Ruy::Rule
6
- def call(ctx)
7
- @conditions.all? do |condition|
8
- condition.call(ctx)
9
- end
10
- end
5
+ class All < CompoundCondition
11
6
  end
12
7
  end
13
8
  end
@@ -2,9 +2,9 @@ module Ruy
2
2
  module Conditions
3
3
 
4
4
  # Expects that at least one of the rules will succeed.
5
- class Any < Ruy::Rule
5
+ class Any < CompoundCondition
6
6
  def call(ctx)
7
- @conditions.any? do |condition|
7
+ conditions.any? do |condition|
8
8
  condition.call(ctx)
9
9
  end
10
10
  end
@@ -2,7 +2,7 @@ module Ruy
2
2
  module Conditions
3
3
 
4
4
  # Asserts that an attribute has a truth value.
5
- class Assert < Ruy::Rule
5
+ class Assert < Condition
6
6
  attr_reader :attr
7
7
 
8
8
  # @param attr Context attribute's name
@@ -4,7 +4,7 @@ module Ruy
4
4
  # Expects that a context value belongs to a given range.
5
5
  #
6
6
  # Comparison formula: from <= context[attr] <= to
7
- class Between < Ruy::Rule
7
+ class Between < Condition
8
8
  attr_reader :attr, :from, :to
9
9
 
10
10
  # @param from Range lower bound
@@ -17,20 +17,18 @@ module Ruy
17
17
  @from = from
18
18
  @to = to
19
19
  @attr = attr
20
- instance_exec(&block) if block_given?
21
20
  end
22
21
 
23
22
  def call(ctx)
24
23
  value = ctx.resolve(@attr)
25
- @from <= value && @to >= value && super
24
+ @from <= value && @to >= value
26
25
  end
27
26
 
28
27
  def ==(o)
29
28
  o.kind_of?(Between) &&
30
29
  @attr == o.attr &&
31
30
  @from == o.from &&
32
- @to == o.to &&
33
- self.conditions == o.conditions
31
+ @to == o.to
34
32
  end
35
33
  end
36
34
  end
@@ -0,0 +1,25 @@
1
+ module Ruy
2
+ module Conditions
3
+
4
+ class CompoundCondition < Condition
5
+
6
+ attr_reader :conditions
7
+
8
+ def initialize(*params)
9
+ super
10
+
11
+ @conditions = []
12
+ end
13
+
14
+ # Evaluates all conditions.
15
+ #
16
+ # @return [true] When all conditions succeeds
17
+ # @return [false] Otherwise
18
+ def call(ctx)
19
+ @conditions.all? do |condition|
20
+ condition.call(ctx)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,15 +1,15 @@
1
1
  module Ruy
2
2
  module Conditions
3
3
 
4
- # Expects a successful evaluation of a sub-pair of rules.
4
+ # Expects a successful evaluation of a sub-pair of conditions.
5
5
  #
6
- # Groups rules in slices of 2 rules. Then evalutes each slice until one of them succeds.
7
- # If there's an even number of rules, the last slice will only one rule.
6
+ # Groups rules in slices of 2 conditions. Then evalutes each slice until one of them succeds.
7
+ # If there's an even number of conditions, the last slice will have only one condition.
8
8
  #
9
9
  # Cond is handy for mocking if/else if/else constructs.
10
- class Cond < Ruy::Rule
10
+ class Cond < CompoundCondition
11
11
  def call(ctx)
12
- clauses = @conditions.each_slice(2)
12
+ clauses = conditions.each_slice(2)
13
13
 
14
14
  clauses.any? do |rule_1, rule_2|
15
15
  result = rule_1.call(ctx)
@@ -0,0 +1,15 @@
1
+ module Ruy
2
+ module Conditions
3
+
4
+ class Condition
5
+ include Ruy::DSL
6
+
7
+ attr_reader :params
8
+
9
+ def initialize(*params)
10
+ @params = params
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -5,7 +5,7 @@ module Ruy
5
5
  module Conditions
6
6
 
7
7
  # Expects that a Time object's date corresponds to a specified day of the week.
8
- class DayOfWeek < Ruy::Rule
8
+ class DayOfWeek < Condition
9
9
  DAYS_INTO_WEEK = %w(sunday monday tuesday wednesday thursday friday saturday)
10
10
  attr_reader :attr, :value, :tz_identifier
11
11
 
@@ -2,7 +2,7 @@ module Ruy
2
2
  module Conditions
3
3
 
4
4
  # Expects that a context attribute will be equal to a given value.
5
- class Eq < Ruy::Rule
5
+ class Eq < Condition
6
6
  attr_reader :attr, :value
7
7
 
8
8
  # @param value Expected value
@@ -6,37 +6,22 @@ module Ruy
6
6
  # When a sub-rule is given, Except will expect an unsuccessful evaluation of that sub-rule.
7
7
  # When a sub-rule is not given, Except will expect a context attribute is not equal to a given
8
8
  # value.
9
- class Except < Ruy::Rule
10
- attr_reader :attr, :value
9
+ class Except < CompoundCondition
11
10
 
12
11
  # @param value Non-expected value
13
12
  # @param attr Context attribute's name
14
13
  # @yield a block in the context of the current rule
15
- def initialize(value = nil, attr = nil, &block)
14
+ def initialize(&block)
16
15
  super
17
- @value = value
18
- @attr = attr
19
16
  instance_exec(&block) if block_given?
20
17
  end
21
18
 
22
19
  def call(ctx)
23
- result = true
24
-
25
- if @attr
26
- result &&= ctx.resolve(@attr) != @value
27
- end
28
-
29
- if self.conditions.any?
30
- result &&= !super(ctx)
31
- end
32
-
33
- result
20
+ !super
34
21
  end
35
22
 
36
23
  def ==(o)
37
24
  o.kind_of?(Except) &&
38
- @attr == o.attr &&
39
- @value == o.value &&
40
25
  @conditions == o.conditions
41
26
  end
42
27
  end
@@ -2,7 +2,7 @@ module Ruy
2
2
  module Conditions
3
3
 
4
4
  # Expects that a context attribute will be greater than the given value.
5
- class GreaterThan < Ruy::Rule
5
+ class GreaterThan < Condition
6
6
  attr_reader :attr, :value
7
7
 
8
8
  # @param value
@@ -12,18 +12,16 @@ module Ruy
12
12
  super
13
13
  @value = value
14
14
  @attr = attr
15
- instance_exec(&block) if block_given?
16
15
  end
17
16
 
18
17
  def call(var_ctx)
19
- @value < var_ctx.resolve(@attr) && super
18
+ @value < var_ctx.resolve(@attr)
20
19
  end
21
20
 
22
21
  def ==(o)
23
22
  o.kind_of?(GreaterThan) &&
24
23
  attr == o.attr &&
25
- value == o.value &&
26
- self.conditions == o.conditions
24
+ value == o.value
27
25
  end
28
26
  end
29
27
  end
@@ -2,7 +2,7 @@ module Ruy
2
2
  module Conditions
3
3
 
4
4
  # Expects that a context attribute will be greater than or equal to the given value.
5
- class GreaterThanOrEqual < Ruy::Rule
5
+ class GreaterThanOrEqual < Condition
6
6
  attr_reader :attr, :value
7
7
 
8
8
  # @param value
@@ -12,18 +12,16 @@ module Ruy
12
12
  super
13
13
  @value = value
14
14
  @attr = attr
15
- instance_exec(&block) if block_given?
16
15
  end
17
16
 
18
17
  def call(ctx)
19
- @value <= ctx.resolve(@attr) && super
18
+ @value <= ctx.resolve(@attr)
20
19
  end
21
20
 
22
21
  def ==(o)
23
22
  o.kind_of?(GreaterThanOrEqual) &&
24
23
  attr == o.attr &&
25
- value == o.value &&
26
- self.conditions == o.conditions
24
+ value == o.value
27
25
  end
28
26
  end
29
27
  end
@@ -2,7 +2,7 @@ module Ruy
2
2
  module Conditions
3
3
 
4
4
  # Expects that a context attribute is included in a set of values.
5
- class In < Ruy::Rule
5
+ class In < Condition
6
6
  attr_reader :attr, :values
7
7
 
8
8
  # @param values Expected set of values
@@ -12,18 +12,16 @@ module Ruy
12
12
  super
13
13
  @values = values
14
14
  @attr = attr
15
- instance_exec(&block) if block_given?
16
15
  end
17
16
 
18
17
  def call(var_ctx)
19
- self.values.include?(var_ctx.resolve(self.attr)) && super
18
+ self.values.include?(var_ctx.resolve(self.attr))
20
19
  end
21
20
 
22
21
  def ==(o)
23
22
  o.kind_of?(In) &&
24
23
  self.attr == o.attr &&
25
- self.values == o.values &&
26
- self.conditions == o.conditions
24
+ self.values == o.values
27
25
  end
28
26
  end
29
27
 
@@ -1,6 +1,6 @@
1
1
  module Ruy
2
2
  module Conditions
3
- class InCyclicOrder < Ruy::Rule
3
+ class InCyclicOrder < Condition
4
4
  attr_reader :from, :to, :attr
5
5
 
6
6
  def initialize(from, to, attr, &block)
@@ -8,16 +8,15 @@ module Ruy
8
8
  @from = from
9
9
  @to = to
10
10
  @attr = attr
11
- instance_exec(&block) if block_given?
12
11
  end
13
12
 
14
13
  def call(ctx)
15
14
  value = ctx.resolve(@attr)
16
15
 
17
16
  if @from > @to
18
- (@from <= value || @to >= value) && super
17
+ (@from <= value || @to >= value)
19
18
  else
20
- (@from <= value && @to >= value) && super
19
+ (@from <= value && @to >= value)
21
20
  end
22
21
  end
23
22
 
@@ -25,8 +24,7 @@ module Ruy
25
24
  o.kind_of?(self.class) &&
26
25
  @from == o.from &&
27
26
  @to == o.to &&
28
- @attr == o.attr &&
29
- self.conditions == o.conditions
27
+ @attr == o.attr
30
28
  end
31
29
  end
32
30
  end
@@ -2,7 +2,7 @@ module Ruy
2
2
  module Conditions
3
3
 
4
4
  # Expects that a value is included in a set of values from the context attribute.
5
- class Include < Ruy::Rule
5
+ class Include < Condition
6
6
  attr_reader :attr, :value
7
7
 
8
8
  # @param value Expected set of values
@@ -12,18 +12,16 @@ module Ruy
12
12
  super
13
13
  @value = value
14
14
  @attr = attr
15
- instance_exec(&block) if block_given?
16
15
  end
17
16
 
18
17
  def call(ctx)
19
- ctx.resolve(self.attr).include?(self.value) && super
18
+ ctx.resolve(self.attr).include?(self.value)
20
19
  end
21
20
 
22
21
  def ==(o)
23
22
  o.kind_of?(Include) &&
24
23
  self.attr == o.attr &&
25
- self.value == o.value &&
26
- self.conditions == o.conditions
24
+ self.value == o.value
27
25
  end
28
26
  end
29
27
  end
@@ -2,7 +2,7 @@ module Ruy
2
2
  module Conditions
3
3
 
4
4
  # Expects that a value is included in a set of values from the context attribute.
5
- class Included < Ruy::Rule
5
+ class Included < Condition
6
6
  attr_reader :attr, :value
7
7
 
8
8
  # @param attr Context attribute's name
@@ -12,18 +12,16 @@ module Ruy
12
12
  super
13
13
  @attr = attr
14
14
  @value = value
15
- instance_exec(&block) if block_given?
16
15
  end
17
16
 
18
17
  def call(ctx)
19
- ctx.resolve(self.attr).include?(self.value) && super
18
+ ctx.resolve(self.attr).include?(self.value)
20
19
  end
21
20
 
22
21
  def ==(o)
23
22
  o.kind_of?(Included) &&
24
23
  self.attr == o.attr &&
25
- self.value == o.value &&
26
- self.conditions == o.conditions
24
+ self.value == o.value
27
25
  end
28
26
  end
29
27
  end
@@ -2,13 +2,12 @@ module Ruy
2
2
  module Conditions
3
3
 
4
4
  # Expects that a context attribute will be less than given value.
5
- class LessThan < Ruy::Rule
5
+ class LessThan < Condition
6
6
  attr_reader :attr, :value
7
7
 
8
8
  # @param value
9
9
  # @param attr Context attribute's name
10
10
  def initialize(value, attr)
11
- super
12
11
  @value = value
13
12
  @attr = attr
14
13
  end
@@ -2,7 +2,7 @@ module Ruy
2
2
  module Conditions
3
3
 
4
4
  # Expects that a context attribute will be less than or equal to the given value.
5
- class LessThanOrEqual < Ruy::Rule
5
+ class LessThanOrEqual < Condition
6
6
  attr_reader :attr, :value
7
7
 
8
8
  # @param attr Context attribute's name
@@ -6,7 +6,7 @@ module Ruy
6
6
  # @note Does not support time zone-aware matchers inside sub-blocks.
7
7
  # A workaround for this is always surrounding your time zone-aware matchers by a 'tz' block even in sub-blocks
8
8
  # already surrounded by one.
9
- class TZ < Ruy::Rule
9
+ class TZ < CompoundCondition
10
10
  # @param [String] tz_identifier String representing IANA's time zone identifier.
11
11
  def initialize(tz_identifier)
12
12
  super
@@ -15,7 +15,7 @@ module Ruy
15
15
 
16
16
  # @param [Ruy::VariableContext] ctx
17
17
  def call(ctx)
18
- @conditions.all? do |condition|
18
+ conditions.all? do |condition|
19
19
  condition.call(ctx)
20
20
  end
21
21
  end
@@ -24,7 +24,7 @@ module Ruy
24
24
  #
25
25
  # @param (see Ruy::Conditions::DayOfWeek#initialize)
26
26
  def day_of_week(dow, attr)
27
- @conditions << DayOfWeek.new(dow, attr, @tz_identifier)
27
+ conditions << DayOfWeek.new(dow, attr, @tz_identifier)
28
28
  end
29
29
 
30
30
  # Intercepts an 'eq' call to the superclass and enhances its arguments
@@ -55,15 +55,15 @@ module Ruy
55
55
  # Adds an Except condition.
56
56
  #
57
57
  # @param (see Conditions::Except#initialize)
58
- def except(value = nil, attr = nil, &block)
59
- self.conditions << Conditions::Except.new(value, attr, &block)
58
+ def except(&block)
59
+ self.conditions << Conditions::Except.new(&block)
60
60
  end
61
61
 
62
62
  # Adds a GreaterThan condition.
63
63
  #
64
64
  # @param (see Conditions::GreaterThanOrEqual#initialize)
65
65
  def greater_than(value, attr)
66
- @conditions << Conditions::GreaterThan.new(value, attr)
66
+ self.conditions << Conditions::GreaterThan.new(value, attr)
67
67
  end
68
68
 
69
69
  # Adds a GreaterThanOrEqual condition.
@@ -84,7 +84,7 @@ module Ruy
84
84
  #
85
85
  # @param (see Conditions::InCyclicOrder#initialize)
86
86
  def in_cyclic_order(from, to, attr, &block)
87
- @conditions << Conditions::InCyclicOrder.new(from, to, attr, &block)
87
+ self.conditions << Conditions::InCyclicOrder.new(from, to, attr, &block)
88
88
  end
89
89
 
90
90
  # Adds an Include condition.
@@ -107,7 +107,7 @@ module Ruy
107
107
  def less_than(value, attr)
108
108
  self.conditions << Conditions::LessThan.new(value, attr)
109
109
  end
110
-
110
+
111
111
  # Adds a TZ condition block
112
112
  #
113
113
  # @param [String] tz_identifier String representing IANA's
@@ -132,7 +132,7 @@ module Ruy
132
132
  s << ' ' << stringified_params.join(', ')
133
133
  end
134
134
 
135
- if self.conditions.empty?
135
+ if !self.respond_to?(:conditions) || self.conditions.empty?
136
136
  s << "\n"
137
137
  else
138
138
  s << " do\n"
@@ -11,7 +11,7 @@ module Ruy
11
11
  # @param value The value of this outcome
12
12
  def initialize(value)
13
13
  @value = value
14
- @rule = Ruy::Rule.new
14
+ @root_condition = Ruy::Conditions::All.new
15
15
  end
16
16
 
17
17
  # Returns the value of this outcome is all of the conditions succeed.
@@ -23,14 +23,14 @@ module Ruy
23
23
  # @return [Object]
24
24
  # @return [nil] If some of the conditions does not succeeds
25
25
  def call(ctx)
26
- if @rule.call(ctx)
26
+ if @root_condition.call(ctx)
27
27
  @value
28
28
  end
29
29
  end
30
30
 
31
- # @return [Array<Ruy::Rule>]
31
+ # @return [Array<Ruy::Condition>]
32
32
  def conditions
33
- @rule.conditions
33
+ @root_condition.conditions
34
34
  end
35
35
 
36
36
  # @return [Array] An array containing the value of this outcome
@@ -4,11 +4,14 @@ module Ruy
4
4
  include Utils::Printable
5
5
 
6
6
  attr_reader :conditions
7
+ attr_reader :outcomes
7
8
 
8
- def initialize(*args)
9
+ def initialize
9
10
  @attrs = {}
10
11
  @conditions = []
11
- @params = args
12
+ @fallback = nil
13
+ @lets = []
14
+ @outcomes = []
12
15
  end
13
16
 
14
17
  # Gets attribute's value from the given name.
@@ -28,28 +31,65 @@ module Ruy
28
31
  @attrs[name] = value
29
32
  end
30
33
 
31
- # Evaluates all conditions.
34
+ # Evaluates rule conditions return the corresponding outcome or fallback depending on whether
35
+ # the conditions matched or not.
32
36
  #
33
- # @return [true] When all conditions succeeds
34
- # @return [false] Otherwise
35
- def call(ctx)
36
- success = @conditions.take_while do |condition|
37
- condition.call(ctx)
38
- end
37
+ # @param [Hash] context
38
+ #
39
+ # @return [Object] outcome or fallback
40
+ def call(context)
41
+ ctx = Context.new(context, @lets)
39
42
 
40
- success.length == @conditions.length
43
+ if Ruy::Utils::Rules.evaluate_conditions(@conditions, ctx)
44
+ Ruy::Utils::Rules.compute_outcome(@outcomes, ctx)
45
+
46
+ else
47
+ @fallback
48
+ end
41
49
  end
42
50
 
43
- def ==(o)
44
- o.kind_of?(Rule) &&
45
- conditions == o.conditions
51
+ # TODO document
52
+ def fallback(value)
53
+ @fallback = value
46
54
  end
47
55
 
48
- # Getter method for rules params. It returns all the params without nil objects.
56
+ # Defines a memoized value.
49
57
  #
50
- # @return [Array<Object>]
51
- def params
52
- @params.compact
58
+ # The value will be resolved upon the context during evaluation. Let is lazy evaluated, it is
59
+ # not evaluated until the first condition referencing it is invoked. Once evaluated, its value
60
+ # is stored, so subsequent invocations during the same evaluation will resolve again its value.
61
+ def let(name)
62
+ @lets << name
63
+ end
64
+
65
+ # TODO document
66
+ def outcome(value, &block)
67
+ outcome = Outcome.new(value)
68
+ outcome.instance_exec(&block) if block_given?
69
+ @outcomes << outcome
70
+ end
71
+
72
+ def to_s
73
+ s = @attrs.map { |name, value| "set #{name.inspect}, #{value.inspect}" }.join("\n")
74
+ s << "\n\n" if @attrs.any?
75
+
76
+ s << @lets.map { |name| "let #{name.inspect}" }.join("\n")
77
+ s << "\n\n" if @lets.any?
78
+
79
+ s << self.conditions.join("\n")
80
+
81
+ if @outcomes.any?
82
+ s << "\n" unless s == ''
83
+ s << @outcomes.join("\n")
84
+ end
85
+
86
+ if @fallback
87
+ s << "\n" unless s == ''
88
+ s << "fallback #{@fallback.inspect}\n"
89
+ end
90
+
91
+ s
53
92
  end
93
+
54
94
  end
55
95
  end
@@ -1,2 +1,3 @@
1
1
  require_relative 'utils/naming'
2
2
  require_relative 'utils/printable'
3
+ require_relative 'utils/rules'
@@ -0,0 +1,40 @@
1
+ module Ruy
2
+ module Utils
3
+ module Rules
4
+
5
+ # Evaluates conditions against the given context
6
+ #
7
+ # Returns true when all the conditions match, false otherwise.
8
+ #
9
+ # @param [Array<Condition>] conditions
10
+ # @param [Hash] ctx
11
+ #
12
+ # @return [Boolean]
13
+ def self.evaluate_conditions(conditions, ctx)
14
+ succeed_conditions = conditions.take_while do |condition|
15
+ condition.call(ctx)
16
+ end
17
+
18
+ succeed_conditions.length == conditions.length
19
+ end
20
+
21
+ # Returns the value of the first outcome that matches
22
+ #
23
+ # @param [Ruy::Context] ctx
24
+ #
25
+ # @return [Object] The value of the first matching outcome
26
+ # @return [nil] when no outcome matches
27
+ def self.compute_outcome(outcomes, ctx)
28
+ outcomes.each do |outcome|
29
+ result = outcome.call(ctx)
30
+ unless result.nil?
31
+ return result
32
+ end
33
+ end
34
+
35
+ nil
36
+ end
37
+
38
+ end
39
+ end
40
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Moove-IT
@@ -67,7 +67,9 @@ files:
67
67
  - lib/ruy/conditions/any.rb
68
68
  - lib/ruy/conditions/assert.rb
69
69
  - lib/ruy/conditions/between.rb
70
+ - lib/ruy/conditions/compound_condition.rb
70
71
  - lib/ruy/conditions/cond.rb
72
+ - lib/ruy/conditions/condition.rb
71
73
  - lib/ruy/conditions/day_of_week.rb
72
74
  - lib/ruy/conditions/eq.rb
73
75
  - lib/ruy/conditions/except.rb
@@ -84,11 +86,11 @@ files:
84
86
  - lib/ruy/dsl.rb
85
87
  - lib/ruy/outcome.rb
86
88
  - lib/ruy/rule.rb
87
- - lib/ruy/rule_set.rb
88
89
  - lib/ruy/time_pattern.rb
89
90
  - lib/ruy/utils.rb
90
91
  - lib/ruy/utils/naming.rb
91
92
  - lib/ruy/utils/printable.rb
93
+ - lib/ruy/utils/rules.rb
92
94
  homepage: http://moove-it.github.io/ruy/
93
95
  licenses:
94
96
  - MIT
@@ -1,85 +0,0 @@
1
- module Ruy
2
- class RuleSet
3
- include DSL
4
-
5
- attr_reader :lets
6
- attr_reader :outcomes
7
-
8
- def initialize
9
- super
10
-
11
- @lets = []
12
- @outcomes = []
13
-
14
- @fallback = nil
15
-
16
- @rule = Ruy::Rule.new
17
- end
18
-
19
- def conditions
20
- @rule.conditions
21
- end
22
-
23
- def outcome(value, &block)
24
- outcome = Outcome.new(value)
25
- outcome.instance_exec(&block) if block_given?
26
- @outcomes << outcome
27
- end
28
-
29
- def fallback(value)
30
- @fallback = value
31
- end
32
-
33
- # @param [Hash] context
34
- def call(context)
35
- ctx = Context.new(context, @lets)
36
- if @rule.call(ctx)
37
- compute_outcome(ctx)
38
- else
39
- @fallback
40
- end
41
- end
42
-
43
- def compute_outcome(ctx)
44
- @outcomes.each do |outcome|
45
- result = outcome.call(ctx)
46
- unless result.nil?
47
- return result
48
- end
49
- end
50
-
51
- nil
52
- end
53
-
54
- # Defines a memoized value.
55
- #
56
- # The value will be resolved upon the context during evaluation. Let is lazy evaluated, it is
57
- # not evaluated until the first condition referencing it is invoked. Once evaluated, it's value
58
- # is stored, so subsequent invocations during the same evaluation will resolve again its value.
59
- def let(name)
60
- @lets << name
61
- end
62
-
63
- def to_s
64
- s = lets.map { |name| "let #{name.inspect}" }.join("\n")
65
-
66
- s << "\n\n" unless s == ''
67
-
68
- s << self.conditions.join("\n")
69
-
70
- if @outcomes.any?
71
- s << "\n" unless s == ''
72
- s << @outcomes.join("\n")
73
- end
74
-
75
- if @fallback
76
- s << "\n" unless s == ''
77
- s << "fallback #{@fallback.inspect}\n"
78
- end
79
-
80
- s
81
- end
82
-
83
- end
84
-
85
- end