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.
- checksums.yaml +13 -5
- data/README.md +37 -24
- data/lib/ruy/conditions.rb +3 -0
- data/lib/ruy/conditions/all.rb +2 -1
- data/lib/ruy/conditions/any.rb +7 -6
- data/lib/ruy/conditions/assert.rb +16 -11
- data/lib/ruy/conditions/between.rb +33 -19
- data/lib/ruy/conditions/compound_condition.rb +26 -6
- data/lib/ruy/conditions/cond.rb +11 -6
- data/lib/ruy/conditions/condition.rb +38 -0
- data/lib/ruy/conditions/day_of_week.rb +30 -20
- data/lib/ruy/conditions/dig.rb +42 -0
- data/lib/ruy/conditions/eq.rb +20 -13
- data/lib/ruy/conditions/every.rb +35 -0
- data/lib/ruy/conditions/except.rb +5 -12
- data/lib/ruy/conditions/greater_than.rb +20 -14
- data/lib/ruy/conditions/greater_than_or_equal.rb +21 -14
- data/lib/ruy/conditions/in.rb +19 -14
- data/lib/ruy/conditions/in_cyclic_order.rb +21 -11
- data/lib/ruy/conditions/include.rb +21 -14
- data/lib/ruy/conditions/less_than.rb +19 -13
- data/lib/ruy/conditions/less_than_or_equal.rb +21 -13
- data/lib/ruy/conditions/some.rb +32 -0
- data/lib/ruy/conditions/tz.rb +33 -41
- data/lib/ruy/context.rb +43 -14
- data/lib/ruy/dsl.rb +60 -28
- data/lib/ruy/outcome.rb +11 -11
- data/lib/ruy/rule.rb +26 -20
- data/lib/ruy/time_pattern.rb +6 -23
- data/lib/ruy/utils/naming.rb +3 -3
- data/lib/ruy/utils/rules.rb +3 -3
- metadata +12 -10
- data/lib/ruy/conditions/included.rb +0 -28
@@ -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
|
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 :
|
8
|
+
attr_reader :obj
|
7
9
|
|
8
|
-
# @param
|
9
|
-
# @param
|
10
|
-
|
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
|
-
@
|
13
|
-
@
|
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
|
-
|
23
|
-
|
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
|
data/lib/ruy/conditions/tz.rb
CHANGED
@@ -1,67 +1,59 @@
|
|
1
1
|
module Ruy
|
2
2
|
module Conditions
|
3
3
|
|
4
|
-
# Evaluates a block using comparison matchers
|
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
|
-
#
|
8
|
-
#
|
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
|
-
|
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
|
-
# @
|
17
|
-
def
|
18
|
-
conditions.
|
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
|
-
#
|
24
|
-
|
25
|
-
|
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
|
-
#
|
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
|
-
#
|
40
|
-
|
41
|
-
|
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
|
-
#
|
47
|
-
|
48
|
-
|
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
|
-
#
|
54
|
-
|
55
|
-
|
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
|
-
#
|
61
|
-
|
62
|
-
|
63
|
-
|
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
|
-
|
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
|
-
|
6
|
-
|
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
|
-
#
|
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
|
38
|
+
# @param key
|
17
39
|
#
|
18
|
-
# @return
|
19
|
-
# @return [nil]
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
@
|
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(
|
28
|
-
self.conditions << Conditions::Assert.new(
|
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,
|
35
|
-
self.conditions << Conditions::Between.new(from, to,
|
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,
|
42
|
-
self.conditions << Conditions::Eq.new(value,
|
63
|
+
def eq(value, *attrs)
|
64
|
+
self.conditions << Conditions::Eq.new(value, *attrs)
|
43
65
|
end
|
44
66
|
|
45
|
-
# Adds
|
67
|
+
# Adds an Every condition.
|
46
68
|
#
|
47
|
-
# @
|
48
|
-
def
|
49
|
-
cond = Conditions::
|
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,
|
66
|
-
self.conditions << Conditions::GreaterThan.new(value,
|
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,
|
73
|
-
self.conditions << Conditions::GreaterThanOrEqual.new(value,
|
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,
|
80
|
-
self.conditions << Conditions::In.new(values,
|
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,
|
87
|
-
self.conditions << Conditions::InCyclicOrder.new(from, to,
|
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,
|
94
|
-
self.conditions << Conditions::Include.new(value,
|
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,
|
101
|
-
self.conditions << Conditions::LessThanOrEqual.new(value,
|
129
|
+
def less_than_or_equal(value, *attrs)
|
130
|
+
self.conditions << Conditions::LessThanOrEqual.new(value, *attrs)
|
102
131
|
end
|
103
132
|
|
104
|
-
# Adds a
|
133
|
+
# Adds a Some condition.
|
105
134
|
#
|
106
|
-
# @param (see Conditions::
|
107
|
-
def
|
108
|
-
|
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]
|
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]
|
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
|
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
|
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]
|
23
|
+
# @param ctx [Ruy::Context]
|
22
24
|
#
|
23
|
-
# @return
|
24
|
-
# @return [nil]
|
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>]
|