filter_matcher 0.0.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/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in filter_matcher.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Jakub Godawa
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,122 @@
1
+ filter_matcher - filter your collections
2
+ ===
3
+
4
+ How to install?
5
+ ==
6
+
7
+ gem install filter_matcher
8
+
9
+ What it does?
10
+ ==
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.
13
+
14
+ Example
15
+ ==
16
+
17
+ Lets say we have following data about people:
18
+
19
+ db = [
20
+ { :id => 1, :name => "John", :age => 33, :homepage => "www.johny.com", :matched => false },
21
+ { :id => 2, :name => "Mike", :age => 30, :homepage => "www.mikes.com", :matched => false },
22
+ { :id => 3, :name => "Johny", :age => 25, :homepage => "www.johny.com", :matched => false },
23
+ { :id => 4, :name => "Mike", :age => 30, :homepage => "www.realmike.com", :matched => false },
24
+ { :id => 5, :name => "Dan", :age => 25, :homepage => "www.danny.com", :matched => false },
25
+ { :id => 6, :name => "Dan", :age => 40, :homepage => "www.fakedanny.com", :matched => false }
26
+ ]
27
+
28
+ 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:
29
+
30
+ input = [
31
+ { :name => "Mike", :age => 30, :homepage => "www.realmike.com" },
32
+ { :name => "Dan", :homepage => "www.danny.com" }
33
+ ]
34
+
35
+ 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.
36
+
37
+ 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:
38
+
39
+ def name_filter(collection, name)
40
+ collection.select { |element| element[:name] == name }
41
+ end
42
+
43
+ The filter has to be named in the convention: <what_it_filters>_filter
44
+
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
+ def age_filter(collection, age)
48
+ collection.select { |element| element[:age] == age}
49
+ end
50
+
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
+ def homepage_filter(collection, homepage)
54
+ collection.select { |element| element[:homepage] == homepage }
55
+ end
56
+
57
+ Now, in order to run them all on the collection, we run the filter_and_match method:
58
+
59
+ @input.each do |input|
60
+ collection = @db
61
+
62
+ filter_and_match collection, {
63
+ :by_name => input[:name],
64
+ :by_age => input[:age],
65
+ :by_homepage => input[:homepage]
66
+ }
67
+ end
68
+
69
+ 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.
70
+
71
+ If a single result will be found the elemnt_matched method will be triggered. In this example we just change the entry's flag:
72
+
73
+ def element_matched(element)
74
+ element[:matched] = true
75
+ end
76
+
77
+ Full PeopleMatcher class example, that is responsible only for the matching is shown below:
78
+
79
+ class PeopleMatcher
80
+ include FilterMatcher
81
+
82
+ attr_reader :db
83
+
84
+ def initialize(db, input)
85
+ @db, @input = db, input
86
+ end
87
+
88
+ def match
89
+ @input.each do |input|
90
+ collection = @db
91
+
92
+ filter_and_match collection, {
93
+ :by_name => input[:name],
94
+ :by_age => input[:age],
95
+ :by_homepage => input[:homepage]
96
+ }
97
+ end
98
+ end
99
+
100
+ private
101
+
102
+ def name_filter(collection, name)
103
+ collection.select { |element| element[:name] == name }
104
+ end
105
+
106
+ def age_filter(collection, age)
107
+ collection.select { |element| element[:age] == age}
108
+ end
109
+
110
+ def homepage_filter(collection, homepage)
111
+ collection.select { |element| element[:homepage] == homepage }
112
+ end
113
+
114
+ def element_matched(element)
115
+ element[:matched] = true
116
+ end
117
+ end
118
+
119
+ Feel free to contribute or give any feedback
120
+ ==
121
+
122
+ 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.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "filter_matcher/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "filter_matcher"
7
+ s.version = FilterMatcher::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Jakub Godawa"]
10
+ s.email = ["jakub.godawa@gmail.com"]
11
+ s.homepage = "http://github.com/vysogot/filter_matcher"
12
+ s.summary = %q{FilterMatcher makes it easy to find a match in a collection}
13
+ s.description = %q{FilterMatcher is helpful when it comes to find a single match in the collection
14
+ by a group of filters. Every filter is can be easly defined and chained with others. Filters narrow the
15
+ collection sequantially.}
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ end
@@ -0,0 +1,23 @@
1
+ module FilterMatcher
2
+
3
+ #
4
+ # define filter in a class that uses this module
5
+ # named like:
6
+ # - name_filter
7
+ # - age_filter
8
+ #
9
+ # they should a filtered array
10
+ #
11
+
12
+ def filter_and_match(collection, filters)
13
+ filters.each do |key, param|
14
+ method_name = key.to_s.tr('by_', '') + "_filter"
15
+ filtered = send(method_name.to_sym, collection, param)
16
+ collection = filtered.empty? ? collection : filtered
17
+ if collection.size == 1
18
+ element_matched(collection.first)
19
+ break
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,71 @@
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
+ #
31
+ # filters are run sequentially until they find
32
+ # a single result
33
+ #
34
+ # they are called by the following convention:
35
+ # :by_<what_it_filters => paramters of <what_it_filters>_filter method
36
+ #
37
+ # when one of the filters reveal a single result
38
+ # next filters are not run for current input
39
+ #
40
+ filter_and_match collection, {
41
+ :by_name => input[:name],
42
+ :by_age => input[:age],
43
+ :by_homepage => input[:homepage]
44
+ }
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ #
51
+ # filters are named by the following convention:
52
+ # <what_it_filters>_filter
53
+ #
54
+ # every filter should return a filtered result
55
+ #
56
+ def name_filter(collection, name)
57
+ collection.select { |element| element[:name] == name }
58
+ end
59
+
60
+ def age_filter(collection, age)
61
+ collection.select { |element| element[:age] == age}
62
+ end
63
+
64
+ def homepage_filter(collection, homepage)
65
+ collection.select { |element| element[:homepage] == homepage }
66
+ end
67
+
68
+ def element_matched(element)
69
+ element[:matched] = true
70
+ end
71
+ end
@@ -0,0 +1,3 @@
1
+ module FilterMatcher
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1 @@
1
+ require 'filter_matcher/filter_matcher'
@@ -0,0 +1,81 @@
1
+ require 'bundler/setup'
2
+ require 'test/unit'
3
+ require 'filter_matcher/people_matcher'
4
+
5
+ class PeopleMatcherTest < Test::Unit::TestCase
6
+ def setup
7
+ @db = [
8
+ { :id => 1, :name => "John", :age => 33, :homepage => "www.johny.com", :matched => false },
9
+ { :id => 2, :name => "Mike", :age => 30, :homepage => "www.mikes.com", :matched => false },
10
+ { :id => 3, :name => "Johny", :age => 25, :homepage => "www.johny.com", :matched => false },
11
+ { :id => 4, :name => "Mike", :age => 30, :homepage => "www.realmike.com", :matched => false },
12
+ { :id => 5, :name => "Dan", :age => 25, :homepage => "www.danny.com", :matched => false },
13
+ { :id => 6, :name => "Dan", :age => 40, :homepage => "www.fakedanny.com", :matched => false }
14
+ ]
15
+ end
16
+
17
+ def test_filters_by_name
18
+ input = [
19
+ { :name => "John" },
20
+ { :name => "Johny" }
21
+ ]
22
+
23
+ assert_matched(input, [1, 3])
24
+ end
25
+
26
+ def test_filters_by_name_and_then_by_age
27
+ input = [
28
+ { :name => "Dan", :age => 40 }
29
+ ]
30
+
31
+ assert_matched(input, [6])
32
+ end
33
+
34
+ def test_filters_by_name_and_then_by_age_and_then_by_homepage
35
+ input = [
36
+ { :name => "Mike", :age => 30, :homepage => "www.realmike.com" },
37
+ { :name => "Dan", :homepage => "www.danny.com" }
38
+ ]
39
+
40
+ assert_matched(input, [4, 5])
41
+ end
42
+
43
+ def test_filters_by_age_and_then_by_homepage_name_not_given
44
+ input = [
45
+ { :age => 25, :homepage => "www.johny.com" }
46
+ ]
47
+
48
+ assert_matched(input, [3])
49
+ end
50
+
51
+ def test_doesnt_match_if_nothing_matched
52
+ input = [
53
+ { :name => "George" }
54
+ ]
55
+
56
+ assert_matched(input, [])
57
+ end
58
+
59
+ def test_doesnt_match_if_many_matched
60
+ input = [
61
+ { :name => "Mike", :age => 30 }
62
+ ]
63
+
64
+ assert_matched(input, [])
65
+ end
66
+
67
+ private
68
+
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
80
+ end
81
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: filter_matcher
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Jakub Godawa
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-07-16 00:00:00 +02:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: |-
18
+ FilterMatcher is helpful when it comes to find a single match in the collection
19
+ by a group of filters. Every filter is can be easly defined and chained with others. Filters narrow the
20
+ collection sequantially.
21
+ email:
22
+ - jakub.godawa@gmail.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - .gitignore
31
+ - Gemfile
32
+ - LICENSE.txt
33
+ - README.md
34
+ - Rakefile
35
+ - filter_matcher.gemspec
36
+ - lib/filter_matcher.rb
37
+ - lib/filter_matcher/filter_matcher.rb
38
+ - lib/filter_matcher/people_matcher.rb
39
+ - lib/filter_matcher/version.rb
40
+ - test/people_matcher_test.rb
41
+ has_rdoc: true
42
+ homepage: http://github.com/vysogot/filter_matcher
43
+ licenses: []
44
+
45
+ post_install_message:
46
+ rdoc_options: []
47
+
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ requirements: []
63
+
64
+ rubyforge_project:
65
+ rubygems_version: 1.6.2
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: FilterMatcher makes it easy to find a match in a collection
69
+ test_files:
70
+ - test/people_matcher_test.rb