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 +1 -0
- data/README.md +91 -121
- data/Rakefile +11 -1
- data/lib/filter_matcher/filter_matcher.rb +35 -36
- data/lib/filter_matcher/version.rb +1 -1
- metadata +2 -2
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,142 +1,78 @@
|
|
1
|
-
filter_matcher
|
2
|
-
===
|
1
|
+
# filter_matcher
|
3
2
|
|
4
|
-
|
5
|
-
==
|
3
|
+
filter your collection with ease
|
6
4
|
|
7
|
-
|
5
|
+
## How to install?
|
8
6
|
|
9
|
-
|
10
|
-
==
|
7
|
+
gem install filter_matcher
|
11
8
|
|
12
|
-
|
9
|
+
## What it does?
|
13
10
|
|
14
|
-
|
15
|
-
==
|
11
|
+
Invokes a method on filtered elements from a collection.
|
16
12
|
|
17
|
-
|
13
|
+
## Example
|
18
14
|
|
19
15
|
Lets say we have following data about people:
|
20
16
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
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
|
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
|
-
|
139
|
+
Matching JSON, csv or hash data. Helpful to apply any filter that cannot be applied on DB level.
|
169
140
|
|
170
|
-
|
171
|
-
==
|
141
|
+
## Contribution
|
172
142
|
|
173
|
-
Did you find it usefull
|
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
|
-
|
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 =
|
35
|
+
klass = self.const_get(klass_name)
|
36
36
|
klass.new
|
37
37
|
end
|
38
|
-
end
|
39
38
|
|
40
|
-
|
41
|
-
|
42
|
-
|
39
|
+
# matches only when filters can narrow collection to one element
|
40
|
+
class SingleMatcher
|
41
|
+
attr_accessor :filters
|
43
42
|
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
def initialize
|
44
|
+
@filters = []
|
45
|
+
end
|
47
46
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
def run(collection, match)
|
48
|
+
@filters.each do |filter|
|
49
|
+
filtered = filter.call(collection)
|
50
|
+
collection = filtered.empty? ? collection : filtered
|
52
51
|
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
65
|
+
def initialize
|
66
|
+
@filters = []
|
67
|
+
end
|
69
68
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
78
|
-
|
76
|
+
if (collection.size == 1) ||
|
77
|
+
((last_filter_index == index) && (collection_init_size != collection.size))
|
79
78
|
|
80
|
-
|
81
|
-
|
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
|
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.
|
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-
|
13
|
+
date: 2011-08-11 00:00:00 +02:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|