filter_matcher 0.1.0 → 0.2.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.
data/README.md CHANGED
@@ -9,11 +9,13 @@ How to install?
9
9
  What it does?
10
10
  ==
11
11
 
12
- It filters a collection with defined filters. Launches the filters one by one on the collection until it finds a single result. When the result is found a final action is invoked.
12
+ It filters a collection with defined filters. Launches the filters one by one on the collection until it finds a result. When the result is found a final action is invoked.
13
13
 
14
14
  Example
15
15
  ==
16
16
 
17
+ This example describes SingleMatcher.
18
+
17
19
  Lets say we have following data about people:
18
20
 
19
21
  db = [
@@ -57,7 +59,7 @@ Now, in order to run them all on the collection, we run the matcher method:
57
59
  @input.each do |input|
58
60
  collection = @db
59
61
 
60
- matcher(collection) do |m|
62
+ matcher(collection, :single) do |m|
61
63
  m.filter do |col|
62
64
  col.select { |element| element[:name] == input[:name] }
63
65
  end
@@ -94,7 +96,7 @@ The expected result is the db matched like:
94
96
  {:id => 6, :name => "Dan", :age => 40, :homepage => "www.fakedanny.com", :matched => false}
95
97
  ]
96
98
 
97
- Full PeopleMatcher class example, that is responsible only for the matching is shown below:
99
+ Full PeopleMatcher class example, that is responsible only for the matching could look like that:
98
100
 
99
101
  class PeopleMatcher
100
102
  include FilterMatcher
@@ -133,9 +135,9 @@ Full PeopleMatcher class example, that is responsible only for the matching is s
133
135
  end
134
136
  end
135
137
 
136
- Visual example
138
+ Visual examples
137
139
  ==
138
- Filtering a collection
140
+ Filtering a collection with SingleMatcher
139
141
 
140
142
  [1,2,3] --first_filter--> [2,3] ---second_filter--> [2] -> match
141
143
 
@@ -147,6 +149,19 @@ Filtering a collection
147
149
 
148
150
  [1,2,3] --first_filter--> [1,2,3] --second_filter--> [2,3] --third_filter--> [2,3] -> no match
149
151
 
152
+ Filtering a collection with FirstFromTopMatcher
153
+
154
+ [1,2,3] --first_filter--> [2,3] ---second_filter--> [2] -> matches 2
155
+
156
+ [1,2,3] --first_filter--> [] --> [1,2,3] --second_filter--> [3] -> matches 3
157
+
158
+ [1,2,3] --first_filter--> [1,2] --second_filter--> [1,2] --third_filter--> [1, 2] -> matches 1
159
+
160
+ [1,2,3] --first_filter--> [] --> [1,2,3] --second_filter--> [] --> [1,2,3] --third_filter--> [] -> no match
161
+
162
+ [1,2,3] --first_filter--> [1,2,3] --second_filter--> [2,3] --third_filter--> [2,3] -> matches 2
163
+
164
+
150
165
  Filtering real data by SQL or JS
151
166
  ==
152
167
 
@@ -160,5 +175,5 @@ Did you find it usefull any how? Please let me know, and if you can want to impr
160
175
  Contributors
161
176
  ==
162
177
 
163
- [Albert Llop](https://github.com/mrsimo)
178
+ [Albert Llop](https://github.com/mrsimo),
164
179
  [Sebastian Röbke](https://github.com/boosty)
@@ -1,35 +1,94 @@
1
1
  module FilterMatcher
2
- class Filterer
3
- def initialize(collection)
2
+
3
+ class Matcher
4
+ class UnknownMatcherError < StandardError; end
5
+
6
+ MATCHERS = [:single, :first_from_top]
7
+
8
+ def initialize(collection, matcher_type)
9
+ unless MATCHERS.include?(matcher_type)
10
+ raise UnknownMatcherError,
11
+ "No matcher #{matcher_type} found, select one of these: #{MATCHERS.join(', ')}"
12
+ end
13
+
4
14
  @collection = collection
5
- @filters = []
15
+ @matcher = get_matcher(matcher_type)
6
16
  end
7
17
 
8
18
  def filter(&block)
9
- @filters << block
19
+ @matcher.filters << block
10
20
  end
11
21
 
12
22
  def match(&block)
13
23
  @match = block
14
24
  end
15
25
 
16
- def process
26
+ def run
27
+ @matcher.run(@collection, @match)
28
+ end
29
+
30
+ private
31
+
32
+ def get_matcher(symbol)
33
+ klass_name = symbol.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
34
+ klass_name << "Matcher"
35
+ klass = Object.const_get(klass_name)
36
+ klass.new
37
+ end
38
+ end
39
+
40
+ # matches only when filters can narrow collection to one element
41
+ class SingleMatcher
42
+ attr_accessor :filters
43
+
44
+ def initialize
45
+ @filters = []
46
+ end
47
+
48
+ def run(collection, match)
17
49
  @filters.each do |filter|
18
- filtered = filter.call(@collection)
19
- @collection = filtered.empty? ? @collection : filtered
50
+ filtered = filter.call(collection)
51
+ collection = filtered.empty? ? collection : filtered
52
+
53
+ if collection.size == 1
54
+ match.call(collection.first)
55
+ break
56
+ end
57
+ end
58
+ end
59
+ end
20
60
 
21
- if @collection.size == 1
22
- @match.call(@collection.first)
61
+ # matches the result from the top of the filtered collection
62
+ # does not match only when filtered didn't filtered anything
63
+ class FirstFromTopMatcher
64
+ attr_accessor :filters
65
+
66
+ def initialize
67
+ @filters = []
68
+ end
69
+
70
+ def run(collection, match)
71
+ last_filter_index = @filters.size - 1
72
+ collection_init_size = collection.size
73
+ @filters.each_with_index do |filter, index|
74
+ filtered = filter.call(collection)
75
+ collection = filtered.empty? ? collection : filtered
76
+
77
+ if (collection.size == 1) ||
78
+ ((last_filter_index == index) && (collection_init_size != collection.size))
79
+
80
+ match.call(collection.first)
23
81
  break
24
82
  end
25
83
  end
26
84
  end
27
85
  end
28
86
 
29
- def matcher(collection)
30
- filterer = Filterer.new(collection)
31
- yield filterer
32
87
 
33
- filterer.process
88
+ def matcher(collection, matcher_type)
89
+ matcher = Matcher.new(collection, matcher_type)
90
+ yield matcher
91
+
92
+ matcher.run
34
93
  end
35
94
  end
@@ -1,3 +1,3 @@
1
1
  module FilterMatcher
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -0,0 +1,101 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+
3
+ class FirstFromTopMatcherTest < Test::Unit::TestCase
4
+
5
+ def run_matcher(input)
6
+ input.each do |input|
7
+ collection = @db
8
+
9
+ matcher(collection, :first_from_top) do |m|
10
+ m.filter do |col|
11
+ col.select { |element| element[:name] == input[:name] }
12
+ end
13
+
14
+ m.filter do |col|
15
+ col.select { |element| element[:age] == input[:age]}
16
+ end
17
+
18
+ m.filter do |col|
19
+ col.select { |element| element[:homepage] == input[:homepage] }
20
+ end
21
+
22
+ m.match do |element|
23
+ element[:matched] = true
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ def setup
30
+ @db = [
31
+ { :id => 1, :name => "John", :age => 33, :homepage => "www.johny.com", :matched => false },
32
+ { :id => 2, :name => "Mike", :age => 30, :homepage => "www.mikes.com", :matched => false },
33
+ { :id => 3, :name => "Johny", :age => 25, :homepage => "www.johny.com", :matched => false },
34
+ { :id => 4, :name => "Mike", :age => 30, :homepage => "www.realmike.com", :matched => false },
35
+ { :id => 5, :name => "Dan", :age => 25, :homepage => "www.danny.com", :matched => false },
36
+ { :id => 6, :name => "Dan", :age => 40, :homepage => "www.fakedanny.com", :matched => false }
37
+ ]
38
+ end
39
+
40
+ def test_filters_by_name
41
+ input = [
42
+ { :name => "John" },
43
+ { :name => "Johny" }
44
+ ]
45
+
46
+ run_matcher(input)
47
+
48
+ assert_matched(@db, input, [1, 3])
49
+ end
50
+
51
+ def test_filters_by_name_and_then_by_age
52
+ input = [
53
+ { :name => "Dan", :age => 40 }
54
+ ]
55
+
56
+ run_matcher(input)
57
+
58
+ assert_matched(@db, input, [6])
59
+ end
60
+
61
+ def test_filters_by_name_and_then_by_age_and_then_by_homepage
62
+ input = [
63
+ { :name => "Mike", :age => 30, :homepage => "www.realmike.com" },
64
+ { :name => "Dan", :homepage => "www.danny.com" }
65
+ ]
66
+
67
+ run_matcher(input)
68
+
69
+ assert_matched(@db, input, [4, 5])
70
+ end
71
+
72
+ def test_filters_by_age_and_then_by_homepage_name_not_given
73
+ input = [
74
+ { :age => 25, :homepage => "www.johny.com" }
75
+ ]
76
+
77
+ run_matcher(input)
78
+
79
+ assert_matched(@db, input, [3])
80
+ end
81
+
82
+ def test_doesnt_match_if_nothing_matched
83
+ input = [
84
+ { :name => "George" }
85
+ ]
86
+
87
+ run_matcher(input)
88
+
89
+ assert_matched(@db, input, [])
90
+ end
91
+
92
+ def test_doesnt_match_if_many_matched
93
+ input = [
94
+ { :name => "Mike", :age => 30 }
95
+ ]
96
+
97
+ run_matcher(input)
98
+
99
+ assert_matched(@db, input, [2])
100
+ end
101
+ end
@@ -1,8 +1,31 @@
1
- require 'bundler/setup'
2
- require 'test/unit'
3
- require File.expand_path(File.dirname(__FILE__) + '/people_matcher')
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+
3
+ class FirstFromTopMatcherTest < Test::Unit::TestCase
4
+
5
+ def run_matcher(input)
6
+ input.each do |input|
7
+ collection = @db
8
+
9
+ matcher(collection, :single) do |m|
10
+ m.filter do |col|
11
+ col.select { |element| element[:name] == input[:name] }
12
+ end
13
+
14
+ m.filter do |col|
15
+ col.select { |element| element[:age] == input[:age]}
16
+ end
17
+
18
+ m.filter do |col|
19
+ col.select { |element| element[:homepage] == input[:homepage] }
20
+ end
21
+
22
+ m.match do |element|
23
+ element[:matched] = true
24
+ end
25
+ end
26
+ end
27
+ end
4
28
 
5
- class PeopleMatcherTest < Test::Unit::TestCase
6
29
  def setup
7
30
  @db = [
8
31
  { :id => 1, :name => "John", :age => 33, :homepage => "www.johny.com", :matched => false },
@@ -20,7 +43,9 @@ class PeopleMatcherTest < Test::Unit::TestCase
20
43
  { :name => "Johny" }
21
44
  ]
22
45
 
23
- assert_matched(input, [1, 3])
46
+ run_matcher(input)
47
+
48
+ assert_matched(@db, input, [1, 3])
24
49
  end
25
50
 
26
51
  def test_filters_by_name_and_then_by_age
@@ -28,7 +53,9 @@ class PeopleMatcherTest < Test::Unit::TestCase
28
53
  { :name => "Dan", :age => 40 }
29
54
  ]
30
55
 
31
- assert_matched(input, [6])
56
+ run_matcher(input)
57
+
58
+ assert_matched(@db, input, [6])
32
59
  end
33
60
 
34
61
  def test_filters_by_name_and_then_by_age_and_then_by_homepage
@@ -37,7 +64,9 @@ class PeopleMatcherTest < Test::Unit::TestCase
37
64
  { :name => "Dan", :homepage => "www.danny.com" }
38
65
  ]
39
66
 
40
- assert_matched(input, [4, 5])
67
+ run_matcher(input)
68
+
69
+ assert_matched(@db, input, [4, 5])
41
70
  end
42
71
 
43
72
  def test_filters_by_age_and_then_by_homepage_name_not_given
@@ -45,7 +74,9 @@ class PeopleMatcherTest < Test::Unit::TestCase
45
74
  { :age => 25, :homepage => "www.johny.com" }
46
75
  ]
47
76
 
48
- assert_matched(input, [3])
77
+ run_matcher(input)
78
+
79
+ assert_matched(@db, input, [3])
49
80
  end
50
81
 
51
82
  def test_doesnt_match_if_nothing_matched
@@ -53,7 +84,9 @@ class PeopleMatcherTest < Test::Unit::TestCase
53
84
  { :name => "George" }
54
85
  ]
55
86
 
56
- assert_matched(input, [])
87
+ run_matcher(input)
88
+
89
+ assert_matched(@db, input, [])
57
90
  end
58
91
 
59
92
  def test_doesnt_match_if_many_matched
@@ -61,21 +94,8 @@ class PeopleMatcherTest < Test::Unit::TestCase
61
94
  { :name => "Mike", :age => 30 }
62
95
  ]
63
96
 
64
- assert_matched(input, [])
65
- end
66
-
67
- private
97
+ run_matcher(input)
68
98
 
69
- def assert_matched(input, matched)
70
- matcher = PeopleMatcher.new(@db, input)
71
- matcher.match
72
-
73
- matcher.db.select {|x| matched.include?(x[:id]) }.each do |entry|
74
- assert(entry[:matched], "Matched expected: #{entry.inspect}")
75
- end
76
-
77
- matcher.db.reject {|x| matched.include?(x[:id]) }.each do |entry|
78
- assert(!entry[:matched], "Not matched expected: #{entry.inspect}")
79
- end
99
+ assert_matched(@db, input, [])
80
100
  end
81
101
  end
@@ -0,0 +1,15 @@
1
+ require 'bundler/setup'
2
+ require 'test/unit'
3
+ require 'filter_matcher/filter_matcher'
4
+
5
+ include FilterMatcher
6
+
7
+ def assert_matched(db, input, matched)
8
+ db.select {|x| matched.include?(x[:id]) }.each do |entry|
9
+ assert(entry[:matched], "Matched expected: #{entry.inspect}")
10
+ end
11
+
12
+ db.reject {|x| matched.include?(x[:id]) }.each do |entry|
13
+ assert(!entry[:matched], "Not matched expected: #{entry.inspect}")
14
+ end
15
+ end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: filter_matcher
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.0
5
+ version: 0.2.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Jakub Godawa
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-07-19 00:00:00 +02:00
13
+ date: 2011-07-21 00:00:00 +02:00
14
14
  default_executable:
15
15
  dependencies: []
16
16
 
@@ -36,8 +36,9 @@ files:
36
36
  - lib/filter_matcher.rb
37
37
  - lib/filter_matcher/filter_matcher.rb
38
38
  - lib/filter_matcher/version.rb
39
- - test/people_matcher.rb
40
- - test/people_matcher_test.rb
39
+ - test/first_from_top_matcher_test.rb
40
+ - test/single_matcher_test.rb
41
+ - test/test_helper.rb
41
42
  has_rdoc: true
42
43
  homepage: http://github.com/vysogot/filter_matcher
43
44
  licenses: []
@@ -67,5 +68,6 @@ signing_key:
67
68
  specification_version: 3
68
69
  summary: FilterMatcher makes it easy to find a match in a collection
69
70
  test_files:
70
- - test/people_matcher.rb
71
- - test/people_matcher_test.rb
71
+ - test/first_from_top_matcher_test.rb
72
+ - test/single_matcher_test.rb
73
+ - test/test_helper.rb
@@ -1,49 +0,0 @@
1
- #
2
- # example class that uses filter matcher module
3
- #
4
- # @db:
5
- # represents data from which machter tries to filter
6
- # a single result
7
- #
8
- # @input:
9
- # criteria for maching
10
- #
11
-
12
- require 'filter_matcher'
13
-
14
- class PeopleMatcher
15
- include FilterMatcher
16
-
17
- attr_reader :db
18
-
19
- def initialize(db, input)
20
- @db, @input = db, input
21
- end
22
-
23
- #
24
- # run filters on all data for every input element
25
- #
26
- def match
27
- @input.each do |input|
28
- collection = @db
29
-
30
- matcher(collection) do |m|
31
- m.filter do |col|
32
- col.select { |element| element[:name] == input[:name] }
33
- end
34
-
35
- m.filter do |col|
36
- col.select { |element| element[:age] == input[:age]}
37
- end
38
-
39
- m.filter do |col|
40
- col.select { |element| element[:homepage] == input[:homepage] }
41
- end
42
-
43
- m.match do |element|
44
- element[:matched] = true
45
- end
46
- end
47
- end
48
- end
49
- end