filter_matcher 0.2.0 → 0.2.1

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/Gemfile CHANGED
@@ -2,3 +2,4 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in filter_matcher.gemspec
4
4
  gemspec
5
+ gem 'rake'
data/README.md CHANGED
@@ -1,142 +1,78 @@
1
- filter_matcher - filter your collections
2
- ===
1
+ # filter_matcher
3
2
 
4
- How to install?
5
- ==
3
+ filter your collection with ease
6
4
 
7
- gem install filter_matcher
5
+ ## How to install?
8
6
 
9
- What it does?
10
- ==
7
+ gem install filter_matcher
11
8
 
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.
9
+ ## What it does?
13
10
 
14
- Example
15
- ==
11
+ Invokes a method on filtered elements from a collection.
16
12
 
17
- This example describes SingleMatcher.
13
+ ## Example
18
14
 
19
15
  Lets say we have following data about people:
20
16
 
21
- db = [
22
- { :id => 1, :name => "John", :age => 33, :homepage => "www.johny.com", :matched => false },
23
- { :id => 2, :name => "Mike", :age => 30, :homepage => "www.mikes.com", :matched => false },
24
- { :id => 3, :name => "Johny", :age => 25, :homepage => "www.johny.com", :matched => false },
25
- { :id => 4, :name => "Mike", :age => 30, :homepage => "www.realmike.com", :matched => false },
26
- { :id => 5, :name => "Dan", :age => 25, :homepage => "www.danny.com", :matched => false },
27
- { :id => 6, :name => "Dan", :age => 40, :homepage => "www.fakedanny.com", :matched => false }
28
- ]
29
-
30
- And we want to set the matched flag to true (or run any other more complicated action), only for the entries that match the input data. Lets say that input is:
31
-
32
- input = [
33
- { :name => "Mike", :age => 30, :homepage => "www.realmike.com" },
34
- { :name => "Dan", :homepage => "www.danny.com" }
35
- ]
36
-
37
- First we need to specify how we want to filter our data to find the best match. As the filters run sequentially the order matters.
38
-
39
- Lets say the most important is the name. If name filter finds a single result then this is a match. We can define the filter like:
40
-
17
+ ```ruby
18
+ db = [
19
+ { :id => 1, :name => "John", :age => 33, :homepage => "www.johny.com", :matched => false },
20
+ { :id => 2, :name => "Mike", :age => 30, :homepage => "www.mikes.com", :matched => false },
21
+ { :id => 3, :name => "Johny", :age => 25, :homepage => "www.johny.com", :matched => false },
22
+ { :id => 4, :name => "Mike", :age => 30, :homepage => "www.realmike.com", :matched => false },
23
+ { :id => 5, :name => "Dan", :age => 25, :homepage => "www.danny.com", :matched => false },
24
+ { :id => 6, :name => "Dan", :age => 40, :homepage => "www.fakedanny.com", :matched => false }
25
+ ]
26
+ ```
27
+
28
+ And we want to match them with the following input:
29
+
30
+ ```ruby
31
+ input = [
32
+ { :name => "Mike", :age => 30, :homepage => "www.realmike.com" },
33
+ { :name => "Dan", :homepage => "www.danny.com" }
34
+ ]
35
+ ```
36
+
37
+ We specifiy filters in order of importance and the method to be invoked on matched element(s):
38
+
39
+ ```ruby
40
+ input.each do |input|
41
+ matcher(db, :single) do |m|
41
42
  m.filter do |col|
42
- col.select { |element| element[:name] == name }
43
+ col.select { |element| element[:name] == input[:name] }
43
44
  end
44
45
 
45
- If this is not sufficient we run another filter on the result of the previous one. Lets define another filter, saying that the second most important thing to find a match is age.
46
-
47
46
  m.filter do |col|
48
- col.select { |element| element[:age] == age}
47
+ col.select { |element| element[:age] == input[:age]}
49
48
  end
50
49
 
51
- Simple and very similar to the previous one. Filters can have any logic you need. Lets define the last one - homepage filter.
52
-
53
50
  m.filter do |col|
54
- col.select { |element| element[:homepage] == homepage }
51
+ col.select { |element| element[:homepage] == input[:homepage] }
55
52
  end
56
53
 
57
- Now, in order to run them all on the collection, we run the matcher method:
58
-
59
- @input.each do |input|
60
- collection = @db
61
-
62
- matcher(collection, :single) do |m|
63
- m.filter do |col|
64
- col.select { |element| element[:name] == input[:name] }
65
- end
66
-
67
- m.filter do |col|
68
- col.select { |element| element[:age] == input[:age]}
69
- end
70
-
71
- m.filter do |col|
72
- col.select { |element| element[:homepage] == input[:homepage] }
73
- end
74
-
75
- m.match do |element|
76
- element[:matched] = true
77
- end
78
- end
79
- end
80
-
81
- If any filter returns an empty result then next filter is given the collection of the last not empty result or the original data itself.
82
- If a single result will be found the match block will be triggered. In this example we just change the entry's flag:
83
-
84
54
  m.match do |element|
85
55
  element[:matched] = true
86
56
  end
57
+ end
58
+ end
59
+ ```
87
60
 
88
61
  The expected result is the db matched like:
89
62
 
90
- db == [
91
- {:id => 1, :name => "John", :age => 33, :homepage => "www.johny.com", :matched => false}
92
- {:id => 2, :name => "Mike", :age => 30, :homepage => "www.mikes.com", :matched => false}
93
- {:id => 3, :name => "Johny", :age => 25, :homepage => "www.johny.com", :matched => false}
94
- {:id => 4, :name => "Mike", :age => 30, :homepage => "www.realmike.com", :matched => true}
95
- {:id => 5, :name => "Dan", :age => 25, :homepage => "www.danny.com", :matched => true}
96
- {:id => 6, :name => "Dan", :age => 40, :homepage => "www.fakedanny.com", :matched => false}
97
- ]
63
+ ```ruby
64
+ db == [
65
+ {:id => 1, :name => "John", :age => 33, :homepage => "www.johny.com", :matched => false}
66
+ {:id => 2, :name => "Mike", :age => 30, :homepage => "www.mikes.com", :matched => false}
67
+ {:id => 3, :name => "Johny", :age => 25, :homepage => "www.johny.com", :matched => false}
68
+ {:id => 4, :name => "Mike", :age => 30, :homepage => "www.realmike.com", :matched => true}
69
+ {:id => 5, :name => "Dan", :age => 25, :homepage => "www.danny.com", :matched => true}
70
+ {:id => 6, :name => "Dan", :age => 40, :homepage => "www.fakedanny.com", :matched => false}
71
+ ]
72
+ ```
98
73
 
99
- Full PeopleMatcher class example, that is responsible only for the matching could look like that:
74
+ ## Visual examples
100
75
 
101
- class PeopleMatcher
102
- include FilterMatcher
103
-
104
- attr_reader :db
105
-
106
- def initialize(db, input)
107
- @db, @input = db, input
108
- end
109
-
110
- #
111
- # run filters on all data for every input element
112
- #
113
- def match
114
- @input.each do |input|
115
- collection = @db
116
-
117
- matcher(collection) do |m|
118
- m.filter do |col|
119
- col.select { |element| element[:name] == input[:name] }
120
- end
121
-
122
- m.filter do |col|
123
- col.select { |element| element[:age] == input[:age]}
124
- end
125
-
126
- m.filter do |col|
127
- col.select { |element| element[:homepage] == input[:homepage] }
128
- end
129
-
130
- m.match do |element|
131
- element[:matched] = true
132
- end
133
- end
134
- end
135
- end
136
- end
137
-
138
- Visual examples
139
- ==
140
76
  Filtering a collection with SingleMatcher
141
77
 
142
78
  [1,2,3] --first_filter--> [2,3] ---second_filter--> [2] -> match
@@ -162,18 +98,52 @@ Filtering a collection with FirstFromTopMatcher
162
98
  [1,2,3] --first_filter--> [1,2,3] --second_filter--> [2,3] --third_filter--> [2,3] -> matches 2
163
99
 
164
100
 
165
- Filtering real data by SQL or JS
166
- ==
101
+ ## Filtering class example
102
+
103
+ Full PeopleMatcher class example, that is responsible only for the matching could look like that:
104
+
105
+ ```ruby
106
+ class PeopleMatcher
107
+ include FilterMatcher
108
+
109
+ attr_reader :db
110
+
111
+ def initialize(db, input)
112
+ @db, @input = db, input
113
+ end
114
+
115
+ def match
116
+ @input.each do |input|
117
+ collection = @db
118
+
119
+ matcher(collection, :filter_type) do |m|
120
+ m.filter do |col|
121
+ # filtering code
122
+ end
123
+
124
+ m.filter do |col|
125
+ # filtering code
126
+ end
127
+
128
+ m.match do |element|
129
+ # match code
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ ```
136
+
137
+ ## Usecases
167
138
 
168
- Remeber that often you can filter your data much more efficient with DB languages. This filtering method should be used when the filtering logic is highly application-dependent. Good SQL query (or smart map reduce) before application filter-matching is always a better approach.
139
+ Matching JSON, csv or hash data. Helpful to apply any filter that cannot be applied on DB level.
169
140
 
170
- Feel free to contribute or give any feedback
171
- ==
141
+ ## Contribution
172
142
 
173
- Did you find it usefull any how? Please let me know, and if you can want to improve it just send me a pull request.
143
+ Did you find it usefull anyhow?
144
+ Please let me know, and if you want to improve it just send me an email or pull request.
174
145
 
175
- Contributors
176
- ==
146
+ Contributors:
177
147
 
178
148
  [Albert Llop](https://github.com/mrsimo),
179
149
  [Sebastian Röbke](https://github.com/boosty)
data/Rakefile CHANGED
@@ -1,2 +1,12 @@
1
1
  require 'bundler'
2
- Bundler::GemHelper.install_tasks
2
+ #require 'bundler/gem_tasks'
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+
6
+ task :default => :test
7
+
8
+ Rake::TestTask.new do |t|
9
+ t.libs << '.'
10
+ t.test_files = FileList['test/**/*_test.rb']
11
+ t.verbose = true
12
+ end
@@ -12,7 +12,7 @@ module FilterMatcher
12
12
  end
13
13
 
14
14
  @collection = collection
15
- @matcher = get_matcher(matcher_type)
15
+ @matcher = self.class.get_matcher(matcher_type)
16
16
  end
17
17
 
18
18
  def filter(&block)
@@ -29,62 +29,61 @@ module FilterMatcher
29
29
 
30
30
  private
31
31
 
32
- def get_matcher(symbol)
32
+ def self.get_matcher(symbol)
33
33
  klass_name = symbol.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
34
34
  klass_name << "Matcher"
35
- klass = Object.const_get(klass_name)
35
+ klass = self.const_get(klass_name)
36
36
  klass.new
37
37
  end
38
- end
39
38
 
40
- # matches only when filters can narrow collection to one element
41
- class SingleMatcher
42
- attr_accessor :filters
39
+ # matches only when filters can narrow collection to one element
40
+ class SingleMatcher
41
+ attr_accessor :filters
43
42
 
44
- def initialize
45
- @filters = []
46
- end
43
+ def initialize
44
+ @filters = []
45
+ end
47
46
 
48
- def run(collection, match)
49
- @filters.each do |filter|
50
- filtered = filter.call(collection)
51
- collection = filtered.empty? ? collection : filtered
47
+ def run(collection, match)
48
+ @filters.each do |filter|
49
+ filtered = filter.call(collection)
50
+ collection = filtered.empty? ? collection : filtered
52
51
 
53
- if collection.size == 1
54
- match.call(collection.first)
55
- break
52
+ if collection.size == 1
53
+ match.call(collection.first)
54
+ break
55
+ end
56
56
  end
57
57
  end
58
58
  end
59
- end
60
59
 
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
60
+ # matches the result from the top of the filtered collection
61
+ # does not match only when filtered didn't filtered anything
62
+ class FirstFromTopMatcher
63
+ attr_accessor :filters
65
64
 
66
- def initialize
67
- @filters = []
68
- end
65
+ def initialize
66
+ @filters = []
67
+ end
69
68
 
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
69
+ def run(collection, match)
70
+ last_filter_index = @filters.size - 1
71
+ collection_init_size = collection.size
72
+ @filters.each_with_index do |filter, index|
73
+ filtered = filter.call(collection)
74
+ collection = filtered.empty? ? collection : filtered
76
75
 
77
- if (collection.size == 1) ||
78
- ((last_filter_index == index) && (collection_init_size != collection.size))
76
+ if (collection.size == 1) ||
77
+ ((last_filter_index == index) && (collection_init_size != collection.size))
79
78
 
80
- match.call(collection.first)
81
- break
79
+ match.call(collection.first)
80
+ break
81
+ end
82
82
  end
83
83
  end
84
84
  end
85
85
  end
86
86
 
87
-
88
87
  def matcher(collection, matcher_type)
89
88
  matcher = Matcher.new(collection, matcher_type)
90
89
  yield matcher
@@ -1,3 +1,3 @@
1
1
  module FilterMatcher
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  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.2.0
5
+ version: 0.2.1
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-21 00:00:00 +02:00
13
+ date: 2011-08-11 00:00:00 +02:00
14
14
  default_executable:
15
15
  dependencies: []
16
16