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.
@@ -1,18 +1,43 @@
1
1
  module Ruy
2
- class Outcome < Rule
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 super(ctx)
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 &&
@@ -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
@@ -1,13 +1,23 @@
1
1
  module Ruy
2
- class RuleSet < Rule
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
- @metadata = {}
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
- def call(ctx)
24
- var_ctx = VariableContext.new(ctx, @vars)
25
- if @apply = super(var_ctx)
26
- compute_outcome(var_ctx)
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 [](key)
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(var_ctx)
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
- def method_missing(m, *args, &block)
56
- super
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
@@ -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, '+00:00')
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
@@ -0,0 +1,2 @@
1
+ require_relative 'utils/naming'
2
+ require_relative 'utils/printable'
@@ -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.2.1
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: '0'
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: '0'
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
- description:
42
- email:
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/variable_context.rb
69
- homepage: https://github.com/Moove-it/ruy
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: {}