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
data/lib/ruy/outcome.rb
CHANGED
@@ -1,18 +1,43 @@
|
|
1
1
|
module Ruy
|
2
|
-
|
2
|
+
# Represents a possible result (outcome) of evaluating a rule set
|
3
|
+
#
|
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
|
+
class Outcome
|
7
|
+
include DSL
|
8
|
+
|
3
9
|
attr_reader :value
|
4
10
|
|
11
|
+
# @param value The value of this outcome
|
5
12
|
def initialize(value)
|
6
|
-
super
|
7
13
|
@value = value
|
14
|
+
@rule = Ruy::Rule.new
|
8
15
|
end
|
9
16
|
|
17
|
+
# Returns the value of this outcome is all of the conditions succeed.
|
18
|
+
#
|
19
|
+
# It also returns the value when there's no conditions.
|
20
|
+
#
|
21
|
+
# @param [Ruy::Context] ctx
|
22
|
+
#
|
23
|
+
# @return [Object]
|
24
|
+
# @return [nil] If some of the conditions does not succeeds
|
10
25
|
def call(ctx)
|
11
|
-
if
|
26
|
+
if @rule.call(ctx)
|
12
27
|
@value
|
13
28
|
end
|
14
29
|
end
|
15
30
|
|
31
|
+
# @return [Array<Ruy::Rule>]
|
32
|
+
def conditions
|
33
|
+
@rule.conditions
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Array] An array containing the value of this outcome
|
37
|
+
def params
|
38
|
+
[@value]
|
39
|
+
end
|
40
|
+
|
16
41
|
def ==(o)
|
17
42
|
o.kind_of?(Outcome) &&
|
18
43
|
value == o.value &&
|
data/lib/ruy/rule.rb
CHANGED
@@ -1,120 +1,16 @@
|
|
1
1
|
module Ruy
|
2
2
|
class Rule
|
3
|
+
include DSL
|
4
|
+
include Utils::Printable
|
5
|
+
|
3
6
|
attr_reader :conditions
|
4
|
-
attr_reader :vars
|
5
7
|
|
6
8
|
def initialize(*args)
|
7
|
-
@conditions = []
|
8
|
-
@vars = {}
|
9
9
|
@attrs = {}
|
10
|
+
@conditions = []
|
10
11
|
@params = args
|
11
12
|
end
|
12
13
|
|
13
|
-
# Adds an All condition.
|
14
|
-
#
|
15
|
-
# @yield Evaluates the given block in the context of the current rule
|
16
|
-
def all(&block)
|
17
|
-
cond = Conditions::All.new
|
18
|
-
cond.instance_exec(&block)
|
19
|
-
|
20
|
-
@conditions << cond
|
21
|
-
end
|
22
|
-
|
23
|
-
# Adds an Any condition.
|
24
|
-
#
|
25
|
-
# @yield Evaluates the given block in the context of the current rule
|
26
|
-
def any(&block)
|
27
|
-
cond = Conditions::Any.new
|
28
|
-
cond.instance_exec(&block)
|
29
|
-
|
30
|
-
@conditions << cond
|
31
|
-
end
|
32
|
-
|
33
|
-
# Adds an Assert condition.
|
34
|
-
#
|
35
|
-
# @param (see Conditions::Assert#initialize)
|
36
|
-
def assert(attr)
|
37
|
-
@conditions << Conditions::Assert.new(attr)
|
38
|
-
end
|
39
|
-
|
40
|
-
# Adds a Between condition.
|
41
|
-
#
|
42
|
-
# @param (see Conditions::Between#initialize)
|
43
|
-
def between(attr, from, to, &block)
|
44
|
-
@conditions << Conditions::Between.new(attr, from, to, &block)
|
45
|
-
end
|
46
|
-
|
47
|
-
# Adds an Eq condition.
|
48
|
-
#
|
49
|
-
# @param (see Conditions::Eq#initialize)
|
50
|
-
def eq(attr, value, &block)
|
51
|
-
@conditions << Conditions::Eq.new(attr, value, &block)
|
52
|
-
end
|
53
|
-
|
54
|
-
# Adds a Cond condition.
|
55
|
-
#
|
56
|
-
# @yield Evaluates the given block in the context of the current rule
|
57
|
-
def cond(&block)
|
58
|
-
cond = Conditions::Cond.new
|
59
|
-
cond.instance_exec(&block)
|
60
|
-
|
61
|
-
@conditions << cond
|
62
|
-
end
|
63
|
-
|
64
|
-
# Adds an Except condition.
|
65
|
-
#
|
66
|
-
# @param (see Conditions::Except#initialize)
|
67
|
-
def except(attr = nil, value = nil, &block)
|
68
|
-
@conditions << Conditions::Except.new(attr, value, &block)
|
69
|
-
end
|
70
|
-
|
71
|
-
# Adds a GreaterThanOrEqual condition.
|
72
|
-
#
|
73
|
-
# @param (see Conditions::GreaterThanOrEqual#initialize)
|
74
|
-
def greater_than_or_equal(attr, value)
|
75
|
-
@conditions << Conditions::GreaterThanOrEqual.new(attr, value)
|
76
|
-
end
|
77
|
-
|
78
|
-
# Adds an Include condition.
|
79
|
-
#
|
80
|
-
# @param (see Conditions::Include#initialize)
|
81
|
-
def include(attr, values, &block)
|
82
|
-
@conditions << Conditions::Include.new(attr, values, &block)
|
83
|
-
end
|
84
|
-
|
85
|
-
# Adds an Included condition.
|
86
|
-
#
|
87
|
-
# @param (see Conditions::Included#initialize)
|
88
|
-
def included(attr, value, &block)
|
89
|
-
@conditions << Conditions::Included.new(attr, value, &block)
|
90
|
-
end
|
91
|
-
|
92
|
-
# Adds a LessOrEqualThan condition.
|
93
|
-
#
|
94
|
-
# @param (see Conditions::LessOrEqualThan#initialize)
|
95
|
-
def less_than_or_equal(attr, value, &block)
|
96
|
-
@conditions << Conditions::LessThanOrEqual.new(attr, value)
|
97
|
-
end
|
98
|
-
|
99
|
-
# Adds a LessThan condition.
|
100
|
-
#
|
101
|
-
# @param (see Conditions::LessThan#initialize)
|
102
|
-
def less_than(attr, value)
|
103
|
-
@conditions << Conditions::LessThan.new(attr, value)
|
104
|
-
end
|
105
|
-
|
106
|
-
# Adds a TZ condition block
|
107
|
-
#
|
108
|
-
# @param [String] tz_identifier String representing IANA's
|
109
|
-
# time zone identifier. Defaults to UTC if none passed.
|
110
|
-
# @yield Evaluates the given block in the context of the TZ rule
|
111
|
-
def tz(tz_identifier = 'UTC', &block)
|
112
|
-
cond = Conditions::TZ.new(tz_identifier)
|
113
|
-
cond.instance_exec(&block)
|
114
|
-
|
115
|
-
@conditions << cond
|
116
|
-
end
|
117
|
-
|
118
14
|
# Gets attribute's value from the given name.
|
119
15
|
#
|
120
16
|
# @param name
|
@@ -132,23 +28,6 @@ module Ruy
|
|
132
28
|
@attrs[name] = value
|
133
29
|
end
|
134
30
|
|
135
|
-
# Defines a variable.
|
136
|
-
#
|
137
|
-
# If both value and block are given,
|
138
|
-
# only the block will be taken into account.
|
139
|
-
#
|
140
|
-
# @param name The name of the variable
|
141
|
-
# @param value The value of the variable
|
142
|
-
#
|
143
|
-
# @yield a block that will resolve the variable's value
|
144
|
-
def var(name, value = nil, &block)
|
145
|
-
if block_given?
|
146
|
-
@vars[name] = block
|
147
|
-
else
|
148
|
-
@vars[name] = lambda { value }
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
31
|
# Evaluates all conditions.
|
153
32
|
#
|
154
33
|
# @return [true] When all conditions succeeds
|
@@ -159,16 +38,18 @@ module Ruy
|
|
159
38
|
end
|
160
39
|
|
161
40
|
success.length == @conditions.length
|
162
|
-
rescue NoMethodError
|
163
|
-
false
|
164
41
|
end
|
165
42
|
|
166
43
|
def ==(o)
|
167
44
|
o.kind_of?(Rule) &&
|
168
|
-
conditions == o.conditions
|
169
|
-
vars.keys == o.vars.keys
|
45
|
+
conditions == o.conditions
|
170
46
|
end
|
171
47
|
|
48
|
+
# Getter method for rules params. It returns all the params without nil objects.
|
49
|
+
#
|
50
|
+
# @return [Array<Object>]
|
51
|
+
def params
|
52
|
+
@params.compact
|
53
|
+
end
|
172
54
|
end
|
173
|
-
|
174
55
|
end
|
data/lib/ruy/rule_set.rb
CHANGED
@@ -1,13 +1,23 @@
|
|
1
1
|
module Ruy
|
2
|
-
class RuleSet
|
2
|
+
class RuleSet
|
3
|
+
include DSL
|
4
|
+
|
5
|
+
attr_reader :lets
|
3
6
|
attr_reader :outcomes
|
4
|
-
attr_accessor :metadata
|
5
7
|
|
6
8
|
def initialize
|
7
9
|
super
|
10
|
+
|
11
|
+
@lets = []
|
8
12
|
@outcomes = []
|
13
|
+
|
9
14
|
@fallback = nil
|
10
|
-
|
15
|
+
|
16
|
+
@rule = Ruy::Rule.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def conditions
|
20
|
+
@rule.conditions
|
11
21
|
end
|
12
22
|
|
13
23
|
def outcome(value, &block)
|
@@ -20,30 +30,19 @@ module Ruy
|
|
20
30
|
@fallback = value
|
21
31
|
end
|
22
32
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
33
|
+
# @param [Hash] context
|
34
|
+
def call(context)
|
35
|
+
ctx = Context.new(context, @lets)
|
36
|
+
if @rule.call(ctx)
|
37
|
+
compute_outcome(ctx)
|
27
38
|
else
|
28
39
|
@fallback
|
29
40
|
end
|
30
41
|
end
|
31
42
|
|
32
|
-
def
|
33
|
-
@metadata[key]
|
34
|
-
end
|
35
|
-
|
36
|
-
def []=(key, value)
|
37
|
-
@metadata[key] = value
|
38
|
-
end
|
39
|
-
|
40
|
-
def apply?
|
41
|
-
@apply
|
42
|
-
end
|
43
|
-
|
44
|
-
def compute_outcome(var_ctx)
|
43
|
+
def compute_outcome(ctx)
|
45
44
|
@outcomes.each do |outcome|
|
46
|
-
result = outcome.call(
|
45
|
+
result = outcome.call(ctx)
|
47
46
|
unless result.nil?
|
48
47
|
return result
|
49
48
|
end
|
@@ -52,8 +51,33 @@ module Ruy
|
|
52
51
|
nil
|
53
52
|
end
|
54
53
|
|
55
|
-
|
56
|
-
|
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
|
57
81
|
end
|
58
82
|
|
59
83
|
end
|
data/lib/ruy/time_pattern.rb
CHANGED
@@ -32,7 +32,7 @@ module Ruy
|
|
32
32
|
@tz = TZInfo::Timezone.get(@time_zone || tz_identifier)
|
33
33
|
|
34
34
|
# Store a Time object with values based on the specified time zone
|
35
|
-
@local = Time.new(year || 0, month, day, hour, min, sec,
|
35
|
+
@local = Time.new(year || 0, month, day, hour, min, sec, 0)
|
36
36
|
|
37
37
|
# Store a Time object with values based on UTC
|
38
38
|
@utc = @tz.local_to_utc(@local)
|
@@ -101,7 +101,7 @@ module Ruy
|
|
101
101
|
#
|
102
102
|
# @return [String]
|
103
103
|
def inspect
|
104
|
-
@pattern
|
104
|
+
@pattern.inspect
|
105
105
|
end
|
106
106
|
|
107
107
|
# Overrides Ruby's method missing call to redirect calls
|
data/lib/ruy/utils.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module Ruy
|
2
|
+
module Utils
|
3
|
+
module Naming
|
4
|
+
|
5
|
+
# Returns the simple name of a given module.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# simple_module_name(Net::HTTP) # => 'HTTP'
|
9
|
+
#
|
10
|
+
# @param [Module] mod
|
11
|
+
#
|
12
|
+
# @return [String]
|
13
|
+
# @return [nil] if given module is an anonymous one.
|
14
|
+
def self.simple_module_name(mod)
|
15
|
+
name = mod.name
|
16
|
+
|
17
|
+
if name
|
18
|
+
name.split('::')[-1]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Converts a string into a snake case representation.
|
23
|
+
#
|
24
|
+
# It suppports these kinds of strings:
|
25
|
+
# - camel case
|
26
|
+
# - all caps
|
27
|
+
# - camel case mixed with all caps
|
28
|
+
#
|
29
|
+
# @param [String] s
|
30
|
+
#
|
31
|
+
# @return [String]
|
32
|
+
def self.snakecase(s)
|
33
|
+
s.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
34
|
+
.gsub(/([a-z\d])([A-Z])/,'\1_\2')
|
35
|
+
.downcase
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the name of a rule object.
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
# rule_name(Ruy::Eq.new) # => 'eq'
|
42
|
+
#
|
43
|
+
# @param [Ruy::Rule] rule
|
44
|
+
#
|
45
|
+
# @return [String]
|
46
|
+
def self.rule_name(rule)
|
47
|
+
module_name = simple_module_name(rule.class)
|
48
|
+
|
49
|
+
snakecase(module_name)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module Ruy
|
5
|
+
module Utils
|
6
|
+
|
7
|
+
module Printable
|
8
|
+
|
9
|
+
INDENTATION = ' ' * 2
|
10
|
+
|
11
|
+
def self.indent(level)
|
12
|
+
INDENTATION * level
|
13
|
+
end
|
14
|
+
|
15
|
+
# Adds inspect compatibility on ruby 1.9
|
16
|
+
#
|
17
|
+
# Ruby 1.9 changed the behaviour of #inspect, when not overriden, it uses #to_s method to
|
18
|
+
# generate the string.
|
19
|
+
#
|
20
|
+
# Since Ruby 2.0, the original behaviour was restored.
|
21
|
+
#
|
22
|
+
# @see https://bugs.ruby-lang.org/issues/4453
|
23
|
+
def inspect
|
24
|
+
PP.singleline_pp(self, StringIO.new).string
|
25
|
+
end
|
26
|
+
|
27
|
+
# Adds pretty print compatibility on ruby 1.9
|
28
|
+
#
|
29
|
+
# @see https://bugs.ruby-lang.org/issues/4453
|
30
|
+
def pretty_print(pp)
|
31
|
+
pp.pp_object(self)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
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.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Moove-IT
|
@@ -14,16 +14,16 @@ dependencies:
|
|
14
14
|
name: tzinfo
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '1'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,12 +38,29 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '3.1'
|
41
|
-
|
42
|
-
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: simplecov
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.10.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.10.0
|
55
|
+
description: A library for defining a set of conditions, matching them against an
|
56
|
+
input, and finally return an outcome.
|
57
|
+
email: ruy@moove-it.com
|
43
58
|
executables: []
|
44
59
|
extensions: []
|
45
60
|
extra_rdoc_files: []
|
46
61
|
files:
|
62
|
+
- LICENSE
|
63
|
+
- README.md
|
47
64
|
- lib/ruy.rb
|
48
65
|
- lib/ruy/conditions.rb
|
49
66
|
- lib/ruy/conditions/all.rb
|
@@ -54,19 +71,25 @@ files:
|
|
54
71
|
- lib/ruy/conditions/day_of_week.rb
|
55
72
|
- lib/ruy/conditions/eq.rb
|
56
73
|
- lib/ruy/conditions/except.rb
|
74
|
+
- lib/ruy/conditions/greater_than.rb
|
57
75
|
- lib/ruy/conditions/greater_than_or_equal.rb
|
76
|
+
- lib/ruy/conditions/in.rb
|
77
|
+
- lib/ruy/conditions/in_cyclic_order.rb
|
58
78
|
- lib/ruy/conditions/include.rb
|
59
79
|
- lib/ruy/conditions/included.rb
|
60
80
|
- lib/ruy/conditions/less_than.rb
|
61
81
|
- lib/ruy/conditions/less_than_or_equal.rb
|
62
82
|
- lib/ruy/conditions/tz.rb
|
63
83
|
- lib/ruy/context.rb
|
84
|
+
- lib/ruy/dsl.rb
|
64
85
|
- lib/ruy/outcome.rb
|
65
86
|
- lib/ruy/rule.rb
|
66
87
|
- lib/ruy/rule_set.rb
|
67
88
|
- lib/ruy/time_pattern.rb
|
68
|
-
- lib/ruy/
|
69
|
-
|
89
|
+
- lib/ruy/utils.rb
|
90
|
+
- lib/ruy/utils/naming.rb
|
91
|
+
- lib/ruy/utils/printable.rb
|
92
|
+
homepage: http://moove-it.github.io/ruy/
|
70
93
|
licenses:
|
71
94
|
- MIT
|
72
95
|
metadata: {}
|