ruy 0.2.1 → 0.3.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 +4 -4
- data/LICENSE +21 -0
- data/README.md +197 -0
- data/lib/ruy.rb +2 -1
- data/lib/ruy/conditions.rb +3 -1
- data/lib/ruy/conditions/all.rb +2 -2
- data/lib/ruy/conditions/any.rb +2 -2
- data/lib/ruy/conditions/assert.rb +2 -2
- data/lib/ruy/conditions/between.rb +7 -6
- data/lib/ruy/conditions/cond.rb +3 -3
- data/lib/ruy/conditions/day_of_week.rb +6 -8
- data/lib/ruy/conditions/eq.rb +5 -6
- data/lib/ruy/conditions/except.rb +6 -6
- data/lib/ruy/conditions/greater_than.rb +30 -0
- data/lib/ruy/conditions/greater_than_or_equal.rb +5 -5
- data/lib/ruy/conditions/in.rb +31 -0
- data/lib/ruy/conditions/in_cyclic_order.rb +33 -0
- data/lib/ruy/conditions/include.rb +8 -8
- data/lib/ruy/conditions/included.rb +2 -2
- data/lib/ruy/conditions/less_than.rb +5 -5
- data/lib/ruy/conditions/less_than_or_equal.rb +4 -4
- data/lib/ruy/conditions/tz.rb +19 -18
- data/lib/ruy/context.rb +23 -5
- data/lib/ruy/dsl.rb +146 -0
- data/lib/ruy/outcome.rb +28 -3
- data/lib/ruy/rule.rb +11 -130
- data/lib/ruy/rule_set.rb +47 -23
- data/lib/ruy/time_pattern.rb +2 -2
- data/lib/ruy/utils.rb +2 -0
- data/lib/ruy/utils/naming.rb +53 -0
- data/lib/ruy/utils/printable.rb +36 -0
- metadata +32 -9
- data/lib/ruy/variable_context.rb +0 -24
@@ -5,18 +5,18 @@ module Ruy
|
|
5
5
|
class GreaterThanOrEqual < Ruy::Rule
|
6
6
|
attr_reader :attr, :value
|
7
7
|
|
8
|
-
# @param attr Context attribute's name
|
9
8
|
# @param value
|
9
|
+
# @param attr Context attribute's name
|
10
10
|
# @yield a block in the context of the current rule
|
11
|
-
def initialize(
|
11
|
+
def initialize(value, attr, &block)
|
12
12
|
super
|
13
|
-
@attr = attr
|
14
13
|
@value = value
|
14
|
+
@attr = attr
|
15
15
|
instance_exec(&block) if block_given?
|
16
16
|
end
|
17
17
|
|
18
|
-
def call(
|
19
|
-
|
18
|
+
def call(ctx)
|
19
|
+
@value <= ctx.resolve(@attr) && super
|
20
20
|
end
|
21
21
|
|
22
22
|
def ==(o)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Ruy
|
2
|
+
module Conditions
|
3
|
+
|
4
|
+
# Expects that a context attribute is included in a set of values.
|
5
|
+
class In < Ruy::Rule
|
6
|
+
attr_reader :attr, :values
|
7
|
+
|
8
|
+
# @param values Expected set of values
|
9
|
+
# @param attr Context attribute's name
|
10
|
+
# @yield a block in the context of the current rule
|
11
|
+
def initialize(values, attr, &block)
|
12
|
+
super
|
13
|
+
@values = values
|
14
|
+
@attr = attr
|
15
|
+
instance_exec(&block) if block_given?
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(var_ctx)
|
19
|
+
self.values.include?(var_ctx.resolve(self.attr)) && super
|
20
|
+
end
|
21
|
+
|
22
|
+
def ==(o)
|
23
|
+
o.kind_of?(In) &&
|
24
|
+
self.attr == o.attr &&
|
25
|
+
self.values == o.values &&
|
26
|
+
self.conditions == o.conditions
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Ruy
|
2
|
+
module Conditions
|
3
|
+
class InCyclicOrder < Ruy::Rule
|
4
|
+
attr_reader :from, :to, :attr
|
5
|
+
|
6
|
+
def initialize(from, to, attr, &block)
|
7
|
+
super
|
8
|
+
@from = from
|
9
|
+
@to = to
|
10
|
+
@attr = attr
|
11
|
+
instance_exec(&block) if block_given?
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(ctx)
|
15
|
+
value = ctx.resolve(@attr)
|
16
|
+
|
17
|
+
if @from > @to
|
18
|
+
(@from <= value || @to >= value) && super
|
19
|
+
else
|
20
|
+
(@from <= value && @to >= value) && super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def ==(o)
|
25
|
+
o.kind_of?(self.class) &&
|
26
|
+
@from == o.from &&
|
27
|
+
@to == o.to &&
|
28
|
+
@attr == o.attr &&
|
29
|
+
self.conditions == o.conditions
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,28 +1,28 @@
|
|
1
1
|
module Ruy
|
2
2
|
module Conditions
|
3
3
|
|
4
|
-
# Expects that a
|
4
|
+
# Expects that a value is included in a set of values from the context attribute.
|
5
5
|
class Include < Ruy::Rule
|
6
|
-
attr_reader :attr, :
|
6
|
+
attr_reader :attr, :value
|
7
7
|
|
8
|
+
# @param value Expected set of values
|
8
9
|
# @param attr Context attribute's name
|
9
|
-
# @param values Expected set of values
|
10
10
|
# @yield a block in the context of the current rule
|
11
|
-
def initialize(
|
11
|
+
def initialize(value, attr, &block)
|
12
12
|
super
|
13
|
+
@value = value
|
13
14
|
@attr = attr
|
14
|
-
@values = values
|
15
15
|
instance_exec(&block) if block_given?
|
16
16
|
end
|
17
17
|
|
18
|
-
def call(
|
19
|
-
self.
|
18
|
+
def call(ctx)
|
19
|
+
ctx.resolve(self.attr).include?(self.value) && super
|
20
20
|
end
|
21
21
|
|
22
22
|
def ==(o)
|
23
23
|
o.kind_of?(Include) &&
|
24
24
|
self.attr == o.attr &&
|
25
|
-
self.
|
25
|
+
self.value == o.value &&
|
26
26
|
self.conditions == o.conditions
|
27
27
|
end
|
28
28
|
end
|
@@ -5,16 +5,16 @@ module Ruy
|
|
5
5
|
class LessThan < Ruy::Rule
|
6
6
|
attr_reader :attr, :value
|
7
7
|
|
8
|
-
# @param attr Context attribute's name
|
9
8
|
# @param value
|
10
|
-
|
9
|
+
# @param attr Context attribute's name
|
10
|
+
def initialize(value, attr)
|
11
11
|
super
|
12
|
-
@attr = attr
|
13
12
|
@value = value
|
13
|
+
@attr = attr
|
14
14
|
end
|
15
15
|
|
16
|
-
def call(
|
17
|
-
|
16
|
+
def call(ctx)
|
17
|
+
@value > ctx.resolve(@attr)
|
18
18
|
end
|
19
19
|
|
20
20
|
def ==(o)
|
@@ -7,14 +7,14 @@ module Ruy
|
|
7
7
|
|
8
8
|
# @param attr Context attribute's name
|
9
9
|
# @param value
|
10
|
-
def initialize(
|
10
|
+
def initialize(value, attr)
|
11
11
|
super
|
12
|
-
@attr = attr
|
13
12
|
@value = value
|
13
|
+
@attr = attr
|
14
14
|
end
|
15
15
|
|
16
|
-
def call(
|
17
|
-
|
16
|
+
def call(ctx)
|
17
|
+
@value >= ctx.resolve(@attr)
|
18
18
|
end
|
19
19
|
|
20
20
|
def ==(o)
|
data/lib/ruy/conditions/tz.rb
CHANGED
@@ -2,9 +2,10 @@ module Ruy
|
|
2
2
|
module Conditions
|
3
3
|
|
4
4
|
# Evaluates a block using comparison matchers that receive time-related objects with time zone awareness.
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
5
|
+
#
|
6
|
+
# @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
9
|
class TZ < Ruy::Rule
|
9
10
|
# @param [String] tz_identifier String representing IANA's time zone identifier.
|
10
11
|
def initialize(tz_identifier)
|
@@ -12,25 +13,25 @@ module Ruy
|
|
12
13
|
@tz_identifier = tz_identifier
|
13
14
|
end
|
14
15
|
|
15
|
-
# @param [Ruy::VariableContext]
|
16
|
-
def call(
|
16
|
+
# @param [Ruy::VariableContext] ctx
|
17
|
+
def call(ctx)
|
17
18
|
@conditions.all? do |condition|
|
18
|
-
condition.call(
|
19
|
+
condition.call(ctx)
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
22
23
|
# Adds a DayOfWeek condition
|
23
24
|
#
|
24
25
|
# @param (see Ruy::Conditions::DayOfWeek#initialize)
|
25
|
-
def day_of_week(
|
26
|
-
@conditions << DayOfWeek.new(
|
26
|
+
def day_of_week(dow, attr)
|
27
|
+
@conditions << DayOfWeek.new(dow, attr, @tz_identifier)
|
27
28
|
end
|
28
29
|
|
29
30
|
# Intercepts an 'eq' call to the superclass and enhances its arguments
|
30
31
|
#
|
31
32
|
# @param (see Ruy::Conditions::Eq#initialize)
|
32
|
-
def eq(
|
33
|
-
super(
|
33
|
+
def eq(pattern, attr, &block)
|
34
|
+
super(TimePattern.new(pattern, @tz_identifier), attr, &block)
|
34
35
|
end
|
35
36
|
|
36
37
|
# TODO Add Greater condition call here
|
@@ -38,29 +39,29 @@ module Ruy
|
|
38
39
|
# Intercepts a 'greater_than_or_equal' call to the superclass and enhances its arguments
|
39
40
|
#
|
40
41
|
# @param (see Ruy::Conditions::GreaterThanOrEqual#initialize)
|
41
|
-
def greater_than_or_equal(
|
42
|
-
super(
|
42
|
+
def greater_than_or_equal(pattern, attr, &block)
|
43
|
+
super(TimePattern.new(pattern, @tz_identifier), attr, &block)
|
43
44
|
end
|
44
45
|
|
45
46
|
# Intercepts a 'less_than' call to the superclass and enhances its arguments
|
46
47
|
#
|
47
48
|
# @param (see Ruy::Conditions::LessThan#initialize)
|
48
|
-
def less_than(
|
49
|
-
super(
|
49
|
+
def less_than(pattern, attr, &block)
|
50
|
+
super(TimePattern.new(pattern, @tz_identifier), attr, &block)
|
50
51
|
end
|
51
52
|
|
52
53
|
# Intercepts a 'less_than_or_equal' call to the superclass and enhances its arguments
|
53
54
|
#
|
54
55
|
# @param (see Ruy::Conditions::LessThanOrEqual#initialize)
|
55
|
-
def less_than_or_equal(
|
56
|
-
super(
|
56
|
+
def less_than_or_equal(pattern, attr, &block)
|
57
|
+
super(TimePattern.new(pattern, @tz_identifier), attr, &block)
|
57
58
|
end
|
58
59
|
|
59
60
|
# Intercepts a 'between' call to the superclass and enhances its arguments
|
60
61
|
#
|
61
62
|
# @param (see Ruy::Conditions::Between#initialize)
|
62
|
-
def between(
|
63
|
-
super(
|
63
|
+
def between(from, to, attr, &block)
|
64
|
+
super(TimePattern.new(from, @tz_identifier), TimePattern.new(to, @tz_identifier), attr, &block)
|
64
65
|
end
|
65
66
|
|
66
67
|
end
|
data/lib/ruy/context.rb
CHANGED
@@ -1,10 +1,28 @@
|
|
1
1
|
module Ruy
|
2
|
-
|
3
|
-
|
4
|
-
ctx = Context.new
|
5
|
-
ctx.merge!(hash)
|
2
|
+
# Context that resolves lets and values access.
|
3
|
+
class Context
|
6
4
|
|
7
|
-
|
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
|
+
|
11
|
+
@resolved_lets = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
# Resolve the given attr from the lets or the context.
|
15
|
+
#
|
16
|
+
# @param [Symbol] attr
|
17
|
+
#
|
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])
|
23
|
+
else
|
24
|
+
@ctx.fetch(attr) { |key| @ctx[key.to_s] }
|
25
|
+
end
|
8
26
|
end
|
9
27
|
end
|
10
28
|
end
|
data/lib/ruy/dsl.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
module Ruy
|
2
|
+
module DSL
|
3
|
+
|
4
|
+
# Adds an All condition.
|
5
|
+
#
|
6
|
+
# @yield Evaluates the given block in the context of the new rule
|
7
|
+
def all(&block)
|
8
|
+
cond = Conditions::All.new
|
9
|
+
cond.instance_exec(&block)
|
10
|
+
|
11
|
+
self.conditions << cond
|
12
|
+
end
|
13
|
+
|
14
|
+
# Adds an Any condition.
|
15
|
+
#
|
16
|
+
# @yield Evaluates the given block in the context of the new rule
|
17
|
+
def any(&block)
|
18
|
+
cond = Conditions::Any.new
|
19
|
+
cond.instance_exec(&block)
|
20
|
+
|
21
|
+
self.conditions << cond
|
22
|
+
end
|
23
|
+
|
24
|
+
# Adds an Assert condition.
|
25
|
+
#
|
26
|
+
# @param (see Conditions::Assert#initialize)
|
27
|
+
def assert(attr)
|
28
|
+
self.conditions << Conditions::Assert.new(attr)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Adds a Between condition.
|
32
|
+
#
|
33
|
+
# @param (see Conditions::Between#initialize)
|
34
|
+
def between(from, to, attr, &block)
|
35
|
+
self.conditions << Conditions::Between.new(from, to, attr, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Adds an Eq condition.
|
39
|
+
#
|
40
|
+
# @param (see Conditions::Eq#initialize)
|
41
|
+
def eq(value, attr, &block)
|
42
|
+
self.conditions << Conditions::Eq.new(value, attr, &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Adds a Cond condition.
|
46
|
+
#
|
47
|
+
# @yield Evaluates the given block in the context of the new rule
|
48
|
+
def cond(&block)
|
49
|
+
cond = Conditions::Cond.new
|
50
|
+
cond.instance_exec(&block)
|
51
|
+
|
52
|
+
self.conditions << cond
|
53
|
+
end
|
54
|
+
|
55
|
+
# Adds an Except condition.
|
56
|
+
#
|
57
|
+
# @param (see Conditions::Except#initialize)
|
58
|
+
def except(value = nil, attr = nil, &block)
|
59
|
+
self.conditions << Conditions::Except.new(value, attr, &block)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Adds a GreaterThan condition.
|
63
|
+
#
|
64
|
+
# @param (see Conditions::GreaterThanOrEqual#initialize)
|
65
|
+
def greater_than(value, attr)
|
66
|
+
@conditions << Conditions::GreaterThan.new(value, attr)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Adds a GreaterThanOrEqual condition.
|
70
|
+
#
|
71
|
+
# @param (see Conditions::GreaterThanOrEqual#initialize)
|
72
|
+
def greater_than_or_equal(value, attr)
|
73
|
+
self.conditions << Conditions::GreaterThanOrEqual.new(value, attr)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Adds an In condition.
|
77
|
+
#
|
78
|
+
# @param (see Conditions::Include#initialize)
|
79
|
+
def in(values, attr, &block)
|
80
|
+
self.conditions << Conditions::In.new(values, attr, &block)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Adds a InCyclicOrder condition.
|
84
|
+
#
|
85
|
+
# @param (see Conditions::InCyclicOrder#initialize)
|
86
|
+
def in_cyclic_order(from, to, attr, &block)
|
87
|
+
@conditions << Conditions::InCyclicOrder.new(from, to, attr, &block)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Adds an Include condition.
|
91
|
+
#
|
92
|
+
# @param (see Conditions::Included#initialize)
|
93
|
+
def include(value, attr, &block)
|
94
|
+
self.conditions << Conditions::Include.new(value, attr, &block)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Adds a LessOrEqualThan condition.
|
98
|
+
#
|
99
|
+
# @param (see Conditions::LessOrEqualThan#initialize)
|
100
|
+
def less_than_or_equal(value, attr, &block)
|
101
|
+
self.conditions << Conditions::LessThanOrEqual.new(value, attr)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Adds a LessThan condition.
|
105
|
+
#
|
106
|
+
# @param (see Conditions::LessThan#initialize)
|
107
|
+
def less_than(value, attr)
|
108
|
+
self.conditions << Conditions::LessThan.new(value, attr)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Adds a TZ condition block
|
112
|
+
#
|
113
|
+
# @param [String] tz_identifier String representing IANA's
|
114
|
+
# time zone identifier. Defaults to UTC if none passed.
|
115
|
+
# @yield Evaluates the given block in the context of the TZ rule
|
116
|
+
def tz(tz_identifier = 'UTC', &block)
|
117
|
+
cond = Conditions::TZ.new(tz_identifier)
|
118
|
+
cond.instance_exec(&block)
|
119
|
+
|
120
|
+
self.conditions << cond
|
121
|
+
end
|
122
|
+
|
123
|
+
# @param [Integer] indentation Indentation level
|
124
|
+
def to_s(indentation = 0)
|
125
|
+
rule_name = Ruy::Utils::Naming.rule_name(self)
|
126
|
+
|
127
|
+
s = Ruy::Utils::Printable.indent(indentation) << rule_name
|
128
|
+
|
129
|
+
if self.params.any?
|
130
|
+
stringified_params = self.params.map(&:inspect)
|
131
|
+
|
132
|
+
s << ' ' << stringified_params.join(', ')
|
133
|
+
end
|
134
|
+
|
135
|
+
if self.conditions.empty?
|
136
|
+
s << "\n"
|
137
|
+
else
|
138
|
+
s << " do\n"
|
139
|
+
s << self.conditions.map { |c| c.to_s(indentation + 1) }.join()
|
140
|
+
s << Ruy::Utils::Printable.indent(indentation) + "end\n"
|
141
|
+
end
|
142
|
+
|
143
|
+
s
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|