array_logic 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- == array_logic
1
+ = ArrayLogic
2
2
 
3
3
  A system that allows me to define the logic for comparing arrays of objects.
4
4
 
@@ -39,7 +39,7 @@ and *blockers*:
39
39
 
40
40
  See test/array_logic/rule_test for more examples
41
41
 
42
- === Combinations that match
42
+ == Combinations that match
43
43
 
44
44
  Two methods allow you to determine sample combinations that match the current
45
45
  rule.
@@ -57,3 +57,69 @@ and so would [1,2,3]. However, arrays that only contain 1 or 2 would not match
57
57
  Run example.rb to see some more examples
58
58
 
59
59
  ruby /lib/example.rb
60
+
61
+ == Functions
62
+
63
+ Version 0.2 introduces the concept of functions to ArrayLogic. The function
64
+ syntax is:
65
+
66
+ <function>(<object_method as symbol>) <operator> <number>
67
+
68
+ where:
69
+
70
+ [function] One of the functions listed below
71
+ [object_method] A method that can be called on all of the objects in the array
72
+ [operator] one of: <, <=, >, >=, ==
73
+ [number] a number to compare with the result
74
+
75
+ Using this array as an example:
76
+
77
+ answers = [Answer.find(1), Answer.find(5), Answer.find(6)]
78
+
79
+ === sum
80
+
81
+ Sums the values returned by the object_method and compares them with the number
82
+
83
+ rule = ArrayLogic::Rule.new 'sum(:id) == 12'
84
+ rule.match(answers) --> true
85
+
86
+ rule = ArrayLogic::Rule.new 'sum(:id) > 12'
87
+ rule.match(answers) --> false
88
+
89
+ rule = ArrayLogic::Rule.new 'sum(:id) >= 12'
90
+ rule.match(answers) --> true
91
+
92
+ === average
93
+
94
+ Averages the values returned by the object_method and compares them with the number
95
+
96
+ rule = ArrayLogic::Rule.new 'average(:id) == 4'
97
+ rule.match(answers) --> true
98
+
99
+ rule = ArrayLogic::Rule.new 'average(:id) < 4'
100
+ rule.match(answers) --> false
101
+
102
+ rule = ArrayLogic::Rule.new 'average(:id) <= 4'
103
+ rule.match(answers) --> true
104
+
105
+ === count
106
+
107
+ Counts the number of items not returning nil.
108
+
109
+ rule = ArrayLogic::Rule.new 'count(:id) == 3'
110
+ rule.match(answers) --> true
111
+
112
+ If answer has a method :is_odd? that returned nil if the :id is even:
113
+
114
+ rule = ArrayLogic::Rule.new 'count(:is_odd?) == 2'
115
+ rule.match(answers) --> true
116
+
117
+ === Combining functions with other rules
118
+
119
+ functions can be combined with other rules:
120
+
121
+ rule = ArrayLogic::Rule.new 'sum(:id) == 12 and a6'
122
+ rule.match(answers) --> true
123
+
124
+ rule = ArrayLogic::Rule.new '(sum(:id) == 12) and not a6'
125
+ rule.match(answers) --> false
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ Rake::RDocTask.new do |rdoc|
9
9
  files =['README.rdoc', 'MIT-LICENSE', 'lib/**/*.rb']
10
10
  rdoc.rdoc_files.add(files)
11
11
  rdoc.main = "README.rdoc" # page to start on
12
- rdoc.title = "Dibber Docs"
12
+ rdoc.title = "ArrayLogic Docs"
13
13
  rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
14
14
  rdoc.options << '--line-numbers'
15
15
  end
@@ -1,4 +1,3 @@
1
- require_relative('array_logic/thing')
2
1
  require_relative('array_logic/rule')
3
2
  module ArrayLogic
4
3
 
@@ -82,6 +82,7 @@ module ArrayLogic
82
82
  end
83
83
 
84
84
  def rule_processing_steps
85
+ replace_item(function_pattern, value_of_function_called_on_id_objects)
85
86
  add_space_around_puctuation_characters
86
87
  make_everything_lower_case
87
88
  replace_logic_words_with_operators
@@ -114,6 +115,27 @@ module ArrayLogic
114
115
  def ids_include_this_id
115
116
  lambda {|s| thing_ids.include?(s[/\d+/].to_i)}
116
117
  end
118
+
119
+ def function_pattern
120
+ /(#{array_functions.keys.join('|')})\(\s*\:(\w+[\?\!]?)\s*\)\s*((==|[\<\>]=?)\s*\d+(\.\d+)?)/
121
+ end
122
+
123
+ def array_functions
124
+ {
125
+ :sum => 'inject(:+)',
126
+ :average => 'reduce(:+) / size.to_f',
127
+ :count => 'compact.size'
128
+ }
129
+ end
130
+
131
+ def value_of_function_called_on_id_objects
132
+ lambda do |string|
133
+ all, array_function, function, operator = function_pattern.match(string).to_a
134
+ values = things.collect &function.to_sym
135
+ result = values.instance_eval(array_functions[array_function.to_sym])
136
+ "( #{result} #{operator} )"
137
+ end
138
+ end
117
139
 
118
140
  def processed_rule
119
141
  @processed_rule ||= rule.clone
@@ -171,11 +193,12 @@ module ArrayLogic
171
193
  {
172
194
  :brackets => ['\(', '\)'],
173
195
  :in_pattern => ['\d+\s+in'],
174
- :ids => ['\w\d+'],
196
+ :ids => [thing_id_pattern],
175
197
  :logic_words => %w{and or not},
176
198
  :logic_chrs => ['&&', '\|\|', '!'],
177
199
  :commas => ['\,'],
178
200
  :white_space => ['\s'],
201
+ :function_pattern => [function_pattern]
179
202
  }
180
203
  end
181
204
 
@@ -1,10 +1,14 @@
1
1
  module ArrayLogic
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
4
4
 
5
5
  # History
6
6
  # =======
7
7
  #
8
+ # Version 0.2.0
9
+ # =============
10
+ # Adds functions: sum, count and average
11
+ #
8
12
  # Version 0.1.2
9
13
  # =============
10
14
  # Refactor to tidy up code and make private, rule methods that don't need exposing
@@ -144,7 +144,115 @@ module ArrayLogic
144
144
  assert_thing_match([1, 2, 3], @rule)
145
145
  assert_no_thing_match([3], @rule)
146
146
  assert_thing_match([4], @rule)
147
- end
147
+ end
148
+
149
+ def test_cost
150
+ assert_equal(2, Thing.new(1).cost)
151
+ end
152
+
153
+ def test_sum
154
+ @rule.rule = 'sum(:cost) == 2'
155
+ assert_no_thing_match([1, 2], @rule)
156
+ assert_no_thing_match([1, 2, 3], @rule)
157
+ assert_thing_match([3], @rule)
158
+ assert_thing_match([4], @rule)
159
+ end
160
+
161
+ def test_sum_greater_than
162
+ @rule.rule = 'sum(:cost) > 2'
163
+ assert_thing_match([1, 2], @rule)
164
+ assert_thing_match([1, 2, 3], @rule)
165
+ assert_no_thing_match([3], @rule)
166
+ assert_no_thing_match([4], @rule)
167
+ end
168
+
169
+ def test_sum_less_than
170
+ @rule.rule = 'sum(:cost) < 3'
171
+ assert_no_thing_match([1, 2], @rule)
172
+ assert_no_thing_match([1, 2, 3], @rule)
173
+ assert_thing_match([3], @rule)
174
+ assert_thing_match([4], @rule)
175
+ end
176
+
177
+ def test_average
178
+ @rule.rule = 'average(:id) == 2'
179
+ assert_no_thing_match([1, 2], @rule)
180
+ assert_thing_match([1, 2, 3], @rule)
181
+ assert_no_thing_match([3], @rule)
182
+ assert_no_thing_match([4], @rule)
183
+ end
184
+
185
+ def test_average_with_decimal
186
+ @rule.rule = 'average(:id) == 1.5'
187
+ assert_thing_match([1, 2], @rule)
188
+ assert_no_thing_match([1, 2, 3], @rule)
189
+ assert_no_thing_match([3], @rule)
190
+ assert_no_thing_match([4], @rule)
191
+ end
192
+
193
+ def test_average_greater_than
194
+ @rule.rule = 'average(:id) > 2'
195
+ assert_no_thing_match([1, 2], @rule)
196
+ assert_no_thing_match([1, 2, 3], @rule)
197
+ assert_thing_match([3], @rule)
198
+ assert_thing_match([4], @rule)
199
+ end
200
+
201
+ def test_average_less_than
202
+ @rule.rule = 'average(:id) <= 2'
203
+ assert_thing_match([1, 2], @rule)
204
+ assert_thing_match([1, 2, 3], @rule)
205
+ assert_no_thing_match([3], @rule)
206
+ assert_no_thing_match([4], @rule)
207
+ end
208
+
209
+ def test_count
210
+ @rule.rule = 'count(:id) == 2'
211
+ assert_thing_match([1, 2], @rule)
212
+ assert_no_thing_match([1, 2, 3], @rule)
213
+ assert_no_thing_match([3], @rule)
214
+ assert_no_thing_match([4], @rule)
215
+ end
216
+
217
+ def test_count_greater_than
218
+ @rule.rule = 'count(:id) > 2'
219
+ assert_no_thing_match([1, 2], @rule)
220
+ assert_thing_match([1, 2, 3], @rule)
221
+ assert_no_thing_match([3], @rule)
222
+ assert_no_thing_match([4], @rule)
223
+ end
224
+
225
+ def test_count_less_than
226
+ @rule.rule = 'count(:id) <= 2'
227
+ assert_thing_match([1, 2], @rule)
228
+ assert_no_thing_match([1, 2, 3], @rule)
229
+ assert_thing_match([3], @rule)
230
+ assert_thing_match([4], @rule)
231
+ end
232
+
233
+ def test_count_with_method_that_can_return_nil
234
+ @rule.rule = 'count(:id_odd?) == 1'
235
+ assert_thing_match([1, 2], @rule)
236
+ assert_no_thing_match([1, 2, 3], @rule)
237
+ assert_thing_match([3], @rule)
238
+ assert_no_thing_match([4], @rule)
239
+ end
240
+
241
+ def test_function_with_other_rule
242
+ @rule.rule = 'sum(:id) >= 3 and t3'
243
+ assert_no_thing_match([1, 2], @rule)
244
+ assert_thing_match([1, 2, 3], @rule)
245
+ assert_thing_match([3], @rule)
246
+ assert_no_thing_match([4], @rule)
247
+ end
248
+
249
+ def test_function_with_other_rule_with_brackets
250
+ @rule.rule = '(sum(:id) >= 3) and t3'
251
+ assert_no_thing_match([1, 2], @rule)
252
+ assert_thing_match([1, 2, 3], @rule)
253
+ assert_thing_match([3], @rule)
254
+ assert_no_thing_match([4], @rule)
255
+ end
148
256
 
149
257
  def test_match_without_rule
150
258
  assert_raise RuntimeError do
@@ -1,5 +1,6 @@
1
1
  require 'test/unit'
2
2
  require_relative '../../lib/array_logic'
3
+ require_relative 'thing'
3
4
 
4
5
  module ArrayLogic
5
6
  class TestCase < Test::Unit::TestCase
@@ -8,6 +8,14 @@ module ArrayLogic
8
8
  @id = number
9
9
  end
10
10
 
11
+ def cost
12
+ 2
13
+ end
14
+
15
+ def id_odd?
16
+ return true if id % 2 != 0
17
+ end
18
+
11
19
  def self.make(number)
12
20
  things = Hash.new
13
21
  (1..number).each{|n| things[n] = new(n)}
@@ -10,5 +10,15 @@ module ArrayLogic
10
10
  assert_equal((1..number).to_a, things.values.collect(&:id))
11
11
  assert_equal((1..number).to_a, things.keys)
12
12
  end
13
+
14
+ def test_cost
15
+ thing = Thing.new(1)
16
+ assert_equal(2, thing.cost)
17
+ end
18
+
19
+ def test_id_odd
20
+ things = Thing.make(4)
21
+ assert_equal([true, nil, true, nil], things.values.collect(&:id_odd?))
22
+ end
13
23
  end
14
24
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: array_logic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-22 00:00:00.000000000 Z
12
+ date: 2013-03-26 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Allow a user to define a set of rules, and then test to see if an array
15
15
  of object match those rules.
@@ -20,13 +20,13 @@ extensions: []
20
20
  extra_rdoc_files: []
21
21
  files:
22
22
  - lib/array_logic/version.rb
23
- - lib/array_logic/thing.rb
24
23
  - lib/array_logic/rule.rb
25
24
  - lib/array_logic.rb
26
25
  - lib/example.rb
27
26
  - MIT-LICENSE
28
27
  - Rakefile
29
28
  - README.rdoc
29
+ - test/array_logic/thing.rb
30
30
  - test/array_logic/test_case.rb
31
31
  - test/array_logic/rule_test.rb
32
32
  - test/array_logic/thing_test.rb
@@ -55,6 +55,7 @@ signing_key:
55
55
  specification_version: 3
56
56
  summary: Matches arrays of objects against logical rules.
57
57
  test_files:
58
+ - test/array_logic/thing.rb
58
59
  - test/array_logic/test_case.rb
59
60
  - test/array_logic/rule_test.rb
60
61
  - test/array_logic/thing_test.rb