ruy 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|