crazy_hash_filter 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,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: