crazy_hash_filter 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .idea
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - 1.9.2
4
+ - jruby
5
+ - rbx
6
+ script: "bundle exec rake spec"
7
+
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+ gem 'activesupport'
7
+
8
+ group :test do
9
+ gem "rspec"
10
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Laurynas Butkus
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # CrazyHashFilter
2
+
3
+ [![Build Status](https://secure.travis-ci.org/friendlyfashion/crazy_hash_filter.png)](http://travis-ci.org/friendlyfashion/crazy_hash_filter)
4
+
5
+ Hash filtering by advanced rules.
6
+
7
+ Can be used to reduce API data throughput if you are building API responses from hashes.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'crazy_hash_filter'
14
+
15
+ ## Usage
16
+
17
+ ### Sample hash
18
+
19
+ ```ruby
20
+ @sample = {
21
+ :code => 0,
22
+ :items => [
23
+ { :id => 1,
24
+ :title => 'First item',
25
+ :thumbs => [
26
+ { :code => '50', :url => 'http://some-url/1/50' },
27
+ { :code => '100', :url => 'http://some-url/1/100' },
28
+ { :code => '150', :url => 'http://some-url/1/150' }
29
+ ]
30
+ },
31
+ { :id => 2,
32
+ :title => 'Second item',
33
+ :thumbs => [
34
+ { :code => '50', :url => 'http://some-url/2/50' },
35
+ { :code => '100', :url => 'http://some-url/2/100' },
36
+ { :code => '150', :url => 'http://some-url/2/150' }
37
+ ]
38
+ },
39
+ ]
40
+ }
41
+ ```
42
+
43
+ ### Simple filtering
44
+
45
+ ```ruby
46
+ rules = { :select => [ :code ] }
47
+ ```
48
+
49
+ #### Result
50
+
51
+ ```ruby
52
+ result = CrazyHashFilter.process(@sample, rules)
53
+
54
+ { :code => 0 }
55
+ ```
56
+
57
+ ### Filtering with nested rules
58
+
59
+ ```ruby
60
+ rules = {
61
+ :select => [ :code, :items ],
62
+ :child_rules => {
63
+ :items => { :select => [ :id, :title ] }
64
+ }
65
+ }
66
+ ```
67
+
68
+ #### Result
69
+
70
+ ```ruby
71
+ result = CrazyHashFilter.process(@sample, rules)
72
+
73
+ { :code=>0,
74
+ :items=> [
75
+ {:id=>1, :title=>"First item"},
76
+ {:id=>2, :title=>"Second item"}
77
+ ]
78
+ }
79
+ ```
80
+
81
+ ### Filtering with conditions
82
+
83
+ ```ruby
84
+ rules = {
85
+ :select => [ :code, :items ],
86
+ :child_rules => {
87
+ :items => {
88
+ :select => [ :id, :thumbs ],
89
+
90
+ :child_rules => {
91
+ :thumbs => {
92
+ :conditions => {
93
+ :code => '50'
94
+ }
95
+ }
96
+ }
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ #### Result
103
+
104
+ ```ruby
105
+ result = CrazyHashFilter.process(@sample, rules)
106
+
107
+ { :code=>0,
108
+ :items=> [
109
+ { :id=>1, :thumbs=> [ { :code=>"50", :url=> "http://some-url/1/50" } ] },
110
+ { :id=>2, :thumbs=> [ { :code=>"50", :url=> "http://some-url/2/50" } ] }
111
+ ]}
112
+ ```
113
+
114
+ ### Filtering with conditions array
115
+
116
+ ```ruby
117
+ rules = {
118
+ :select => [ :code, :items ],
119
+ :child_rules => {
120
+ :items => {
121
+ :select => [ :id, :thumbs ],
122
+
123
+ :child_rules => {
124
+ :thumbs => {
125
+ :conditions => {
126
+ :code => [ '50', '100' ]
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }
132
+ }
133
+ ```
134
+
135
+ #### Result
136
+
137
+ ```ruby
138
+ result = CrazyHashFilter.process(@sample, rules)
139
+
140
+ { :code => 0,
141
+ :items=> [
142
+ { :id=>1,
143
+ :thumbs=>[
144
+ { :code => "50", :url => "http://some-url/1/50" },
145
+ { :code => "100", :url => "http://some-url/1/100" }
146
+ ]
147
+ },
148
+ { :id => 2,
149
+ :thumbs => [
150
+ { :code => "50", :url => "http://some-url/2/50" },
151
+ { :code => "100", :url => "http://some-url/2/100" }
152
+ ]
153
+ }
154
+ ]
155
+ }
156
+ ```
157
+
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new
6
+
7
+ task :default => :spec
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/crazy_hash_filter/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Laurynas Butkus", "Tomas Didziokas", "Justas Janauskas", "Edvinas Bartkus"]
6
+ gem.email = ["laurynas.butkus@gmail.com", "tomas.did@gmail.com", "jjanauskas@gmail.com", "edvinas.bartkus@gmail.com"]
7
+
8
+ gem.description = %q{Hash filtering by advanced rules}
9
+ gem.summary = %q{Hash filtering by advanced rules. Can be used to reduce API data throughput if you are building API responses from hashes.}
10
+ gem.homepage = "http://github.com/friendlyfashion/crazy_hash_filter"
11
+
12
+ gem.files = `git ls-files`.split($\)
13
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.name = "crazy_hash_filter"
16
+ gem.require_paths = ["lib"]
17
+ gem.version = CrazyHashFilter::VERSION
18
+ end
@@ -0,0 +1,3 @@
1
+ module CrazyHashFilter
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,78 @@
1
+ require "crazy_hash_filter/version"
2
+ require "active_support/core_ext/hash/indifferent_access.rb"
3
+
4
+ module CrazyHashFilter
5
+
6
+ def self.process(hash, rules)
7
+ rules = HashWithIndifferentAccess.new(rules || {}) unless rules.is_a?(HashWithIndifferentAccess)
8
+ child_rules = rules[:child_rules] || {}
9
+ result = {}
10
+ select = rules[:select].collect(&:to_sym) unless rules[:select].nil?
11
+
12
+ hash.each do |key, value|
13
+ next if !select.nil? && !select.include?(key.to_sym)
14
+
15
+ if child_rules[key].nil?
16
+ result[key] = value
17
+ else
18
+ if value.is_a?(Array)
19
+ result[key] = self.filter_array(value, child_rules[key])
20
+ else
21
+ result[key] = self.process(value, child_rules[key])
22
+ end
23
+ end
24
+ end
25
+
26
+ result
27
+ end
28
+
29
+ private
30
+
31
+ def self.filter_array(array, rules)
32
+ result = []
33
+ conditions = rules[:conditions] || {}
34
+
35
+ array.each do |value|
36
+ result << self.process(value, rules) if match_conditions(value, conditions)
37
+ end
38
+
39
+ result
40
+ end
41
+
42
+ def self.match_conditions(value, conditions)
43
+ indifferent_value = HashWithIndifferentAccess.new(value)
44
+
45
+ conditions.inject(true) do |b, r|
46
+ key, match = r
47
+
48
+ if match.is_a?(Array)
49
+ b & self.is_value_in_array?(indifferent_value[key], match)
50
+ else
51
+ b & self.values_match?(indifferent_value[key], match)
52
+ end
53
+ end
54
+ end
55
+
56
+ def self.is_value_in_array?(value, array)
57
+ array.each do |v|
58
+ return true if self.values_match?(v, value)
59
+ end
60
+
61
+ return false
62
+ end
63
+
64
+ def self.values_match?(a, b)
65
+ a == self.equalize_type(b, a)
66
+ end
67
+
68
+ def self.equalize_type(value, target)
69
+ if target.is_a?(Integer) && value.is_a?(String)
70
+ return value.to_i
71
+ elsif target.is_a?(String) && value.is_a?(Integer)
72
+ return value.to_s
73
+ else
74
+ return value
75
+ end
76
+ end
77
+
78
+ end
@@ -0,0 +1,188 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrazyHashFilter do
4
+
5
+ before do
6
+ @sample = {
7
+ :code => 0,
8
+ :items => [
9
+ { :id => 1,
10
+ :title => 'First item',
11
+ :thumbs => [
12
+ { :code => '50', :url => 'http://some-url/1/50' },
13
+ { :code => '100', :url => 'http://some-url/1/100' },
14
+ { :code => '150', :url => 'http://some-url/1/150' }
15
+ ]
16
+ },
17
+ { :id => 2,
18
+ :title => 'Second item',
19
+ :thumbs => [
20
+ { :code => '50', :url => 'http://some-url/2/50' },
21
+ { :code => '100', :url => 'http://some-url/2/100' },
22
+ { :code => '150', :url => 'http://some-url/2/150' }
23
+ ]
24
+ },
25
+ ]
26
+ }
27
+ end
28
+
29
+ it "should return same hash if no filters passed" do
30
+ result = CrazyHashFilter.process(@sample, {})
31
+ result.should == @sample
32
+ end
33
+
34
+ it "should select only certain keys from the hash" do
35
+ rules = { :select => [ :code ] }
36
+
37
+ result = CrazyHashFilter.process(@sample, rules)
38
+
39
+ result.should == { :code => 0 }
40
+ end
41
+
42
+ it "should filter with nested rules" do
43
+ rules = {
44
+ :select => [ :code, :items ],
45
+ :child_rules => {
46
+ :items => {
47
+ :select => [ :id, :title ]
48
+ }
49
+ }
50
+ }
51
+
52
+ result = CrazyHashFilter.process(@sample, rules)
53
+
54
+ result.keys.should include(:code)
55
+ result.keys.should include(:items)
56
+
57
+ result[:items][0].keys.should include(:id)
58
+ result[:items][0].keys.should include(:title)
59
+ result[:items][0].keys.should_not include(:thumbs)
60
+ result[:items][1].keys.should include(:id)
61
+ result[:items][1].keys.should include(:title)
62
+ result[:items][1].keys.should_not include(:thumbs)
63
+ end
64
+
65
+ it "should filter with nested rules and conditions" do
66
+ rules = {
67
+ :select => [ :code, :items ],
68
+
69
+ :child_rules => {
70
+ :items => {
71
+ :select => [ :id, :title, :thumbs ],
72
+
73
+ :child_rules => {
74
+ :thumbs => {
75
+ :conditions => {
76
+ :code => [ '50', '100' ]
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
83
+
84
+ result = CrazyHashFilter.process(@sample, rules)
85
+
86
+ result.keys.should include(:code)
87
+ result.keys.should include(:items)
88
+
89
+ result[:items][0].keys.should include(:id)
90
+ result[:items][0].keys.should include(:title)
91
+ result[:items][0].keys.should include(:thumbs)
92
+ result[:items][0][:thumbs].collect { |t| t[:code] }.should include('50')
93
+ result[:items][0][:thumbs].collect { |t| t[:code] }.should include('100')
94
+ result[:items][0][:thumbs].collect { |t| t[:code] }.should_not include('150')
95
+ end
96
+
97
+ it "should accept condition strings" do
98
+ rules = {
99
+ :select => [ :code, :items ],
100
+
101
+ :child_rules => {
102
+ :items => {
103
+ :select => [ :id, :thumbs ],
104
+
105
+ :child_rules => {
106
+ :thumbs => {
107
+ :conditions => {
108
+ :code => '50'
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ result = CrazyHashFilter.process(@sample, rules)
117
+
118
+ result[:items][0][:thumbs].collect { |t| t[:code] }.should include('50')
119
+ result[:items][0][:thumbs].collect { |t| t[:code] }.should_not include('100')
120
+ result[:items][0][:thumbs].collect { |t| t[:code] }.should_not include('150')
121
+ end
122
+
123
+ it "should allow using strings in rules" do
124
+ rules = {
125
+ 'select' => [ 'code', 'items' ],
126
+ 'child_rules' => {
127
+ 'items' => {
128
+ 'select' => [ 'id', 'title' ]
129
+ }
130
+ }
131
+ }
132
+
133
+ result = CrazyHashFilter.process(@sample, rules)
134
+
135
+ result.keys.should include(:code)
136
+ result.keys.should include(:items)
137
+
138
+ result[:items][0].keys.should include(:id)
139
+ result[:items][0].keys.should include(:title)
140
+ result[:items][0].keys.should_not include(:thumbs)
141
+ result[:items][1].keys.should include(:id)
142
+ result[:items][1].keys.should include(:title)
143
+ result[:items][1].keys.should_not include(:thumbs)
144
+ end
145
+
146
+ it "should match integer conditions" do
147
+ rules = {
148
+ :select => [ :code, :items ],
149
+
150
+ :child_rules => {
151
+ :items => {
152
+ :select => [ :id, :thumbs ],
153
+
154
+ :conditions => {
155
+ :id => 1
156
+ }
157
+ }
158
+ }
159
+ }
160
+
161
+ result = CrazyHashFilter.process(@sample, rules)
162
+
163
+ result[:items].collect { |i| i[:id] }.should include(1)
164
+ result[:items].collect { |i| i[:id] }.should_not include(2)
165
+ end
166
+
167
+ it "should match integer to string conditions" do
168
+ rules = {
169
+ :select => [ :code, :items ],
170
+
171
+ :child_rules => {
172
+ :items => {
173
+ :select => [ :id, :thumbs ],
174
+
175
+ :conditions => {
176
+ :id => '1'
177
+ }
178
+ }
179
+ }
180
+ }
181
+
182
+ result = CrazyHashFilter.process(@sample, rules)
183
+
184
+ result[:items].collect { |i| i[:id] }.should include(1)
185
+ result[:items].collect { |i| i[:id] }.should_not include(2)
186
+ end
187
+
188
+ end
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'crazy_hash_filter'
4
+
5
+ RSpec.configure do |config|
6
+
7
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: crazy_hash_filter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Laurynas Butkus
9
+ - Tomas Didziokas
10
+ - Justas Janauskas
11
+ - Edvinas Bartkus
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+ date: 2012-08-07 00:00:00.000000000 Z
16
+ dependencies: []
17
+ description: Hash filtering by advanced rules
18
+ email:
19
+ - laurynas.butkus@gmail.com
20
+ - tomas.did@gmail.com
21
+ - jjanauskas@gmail.com
22
+ - edvinas.bartkus@gmail.com
23
+ executables: []
24
+ extensions: []
25
+ extra_rdoc_files: []
26
+ files:
27
+ - .gitignore
28
+ - .rspec
29
+ - .travis.yml
30
+ - Gemfile
31
+ - LICENSE
32
+ - README.md
33
+ - Rakefile
34
+ - crazy_hash_filter.gemspec
35
+ - lib/crazy_hash_filter.rb
36
+ - lib/crazy_hash_filter/version.rb
37
+ - spec/crazy_hash_filter_spec.rb
38
+ - spec/spec_helper.rb
39
+ homepage: http://github.com/friendlyfashion/crazy_hash_filter
40
+ licenses: []
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 1.8.24
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: Hash filtering by advanced rules. Can be used to reduce API data throughput
63
+ if you are building API responses from hashes.
64
+ test_files:
65
+ - spec/crazy_hash_filter_spec.rb
66
+ - spec/spec_helper.rb
67
+ has_rdoc: