ruy 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: {}