whoopee-cushion 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ccc95428b5dc5b3bd5d1ed724eab8fff67072006
4
+ data.tar.gz: 7ca139a81753a6c12336ed13b7619d97db4a667d
5
+ SHA512:
6
+ metadata.gz: c5ad18495ba334ab02ea35604191154533cc4bf625e6908eacaf85776fc03a40368572172288718959c567ce2d4ad998b4a496b94c57c139363084c87b75fd5b
7
+ data.tar.gz: 131aa58433d4280588ffc140844d1d460f981455bdc653a64a9e4d23691e6dbf121de03149dca16a46e58f22f71c40f985293aa4ce919f8fb02f5439058db9b7
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
19
+
20
+ .idea
21
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+ gem 'byebug'
3
+ gem 'recursive-open-struct'
4
+ # Specify your gem's dependencies in whoopee_cushion.gemspec
5
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,27 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ whoopee-cushion (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ byebug (2.6.0)
10
+ columnize (~> 0.3)
11
+ debugger-linecache (~> 1.2)
12
+ columnize (0.3.6)
13
+ debugger-linecache (1.2.0)
14
+ minitest (4.7.5)
15
+ rake (10.1.1)
16
+ recursive-open-struct (0.4.5)
17
+
18
+ PLATFORMS
19
+ ruby
20
+
21
+ DEPENDENCIES
22
+ bundler (~> 1.5)
23
+ byebug
24
+ minitest (~> 4.7.3)
25
+ rake
26
+ recursive-open-struct
27
+ whoopee-cushion!
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 chemica
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Ben Dunkley
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,15 @@
1
+ whoopee-cushion
2
+ ===============
3
+
4
+ Imagine the scene. You're accessing a nice JSON API, but the data you get back is difficult to work with. There are a
5
+ number of reasons why this is, but mainly it's because you're dealing with a giant hash, using camel case for keys.
6
+
7
+ It all leads to ugly, unreadable Ruby code like this:
8
+
9
+ `obj['Products'].first['Customer']['CustomerId']`
10
+
11
+ Wouldn't it be nice if you could access all your API response data like this?
12
+
13
+ `obj.products.first.customer.customer_id`
14
+
15
+ whoopee-cushion will let you do just that. It's a fast and
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'test'
7
+ t.pattern = "test/*_test.rb"
8
+ end
@@ -0,0 +1,3 @@
1
+ module WhoopeeCushion
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,67 @@
1
+ require 'whoopee_cushion/version'
2
+ require 'json'
3
+
4
+ module WhoopeeCushion
5
+
6
+ class Inflate
7
+
8
+ def self.from_json(string, options = {})
9
+ value = JSON.parse string
10
+ from_object value, options
11
+ end
12
+
13
+ def self.from_object(value, options = {})
14
+ case value
15
+ when Array
16
+ from_array value, options
17
+ when Hash
18
+ from_hash value, options
19
+ else
20
+ value
21
+ end
22
+ end
23
+
24
+ def self.from_array(value, options = {})
25
+ value.map {|v| from_object v, options}
26
+ end
27
+
28
+ def self.from_hash(hash, options = {})
29
+ hash = process_hash hash, options
30
+ keys = hash.keys
31
+ model = Struct.new(*keys)
32
+ out = model.new
33
+ hash.each do |key, value|
34
+ out.send("#{key}=", self.from_object(value, options))
35
+ end
36
+ out
37
+ end
38
+
39
+ private
40
+
41
+ def self.process_hash hash, options
42
+ remap = hash.map do |key, value|
43
+ key = underscore_key key unless options[:to_snake_keys] == false
44
+ [key.to_sym, value]
45
+ end
46
+ Hash[remap]
47
+ end
48
+
49
+ def self.underscore_key(key)
50
+ if key.is_a? Symbol
51
+ underscore(key.to_s)
52
+ elsif key.is_a? String
53
+ underscore(key)
54
+ else
55
+ key
56
+ end
57
+ end
58
+
59
+ def self.underscore(string)
60
+ string.gsub(/::/, '/').
61
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
62
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
63
+ tr("-", "_").
64
+ downcase
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,120 @@
1
+ require 'test_helper'
2
+
3
+ describe 'Inflating complex JSON' do
4
+ before do
5
+ @json = '{"one": 1, "two": "2", "three": "three", "recurse": {"z":1000, "y": "999"}, "array": [1,2,{"foo": "bar"}]}'
6
+ end
7
+
8
+ it 'should generate objects' do
9
+ obj = WhoopeeCushion::Inflate.from_json @json
10
+ assert_equal 1, obj.one
11
+ assert_equal 'three', obj.three
12
+ assert obj.recurse.is_a?(Struct), 'Hash not available'
13
+ assert obj.array.is_a?(Array), 'Array not available'
14
+ assert_equal 1000, obj.recurse.z
15
+ assert_equal 1, obj.array[0]
16
+ assert_equal "bar", obj.array[2].foo
17
+ end
18
+ end
19
+
20
+ describe 'converting keys to snake case' do
21
+ before do
22
+ @json = { 'CaseOne' => 'FirstCase', 'caseTwo' => 'secondCase', 'case_three' => 'third_case' }
23
+ end
24
+
25
+ it 'should convert CamelCase' do
26
+ obj = WhoopeeCushion::Inflate.from_hash @json
27
+ assert_equal 'FirstCase', obj.case_one
28
+ end
29
+
30
+ it 'should convert camelBack' do
31
+ obj = WhoopeeCushion::Inflate.from_hash @json
32
+ assert_equal 'secondCase', obj.case_two
33
+ end
34
+
35
+ it 'should not convert camel case with snake keys option false' do
36
+ obj = WhoopeeCushion::Inflate.from_hash @json, :to_snake_keys => false
37
+ assert_equal 'FirstCase', obj.CaseOne
38
+ end
39
+
40
+ it 'should not convert camel back with snake keys option false' do
41
+ obj = WhoopeeCushion::Inflate.from_hash @json, :to_snake_keys => false
42
+ assert_equal 'secondCase', obj.caseTwo
43
+ end
44
+
45
+ it 'should leave snake case alone with snake keys option false' do
46
+ obj = WhoopeeCushion::Inflate.from_hash @json, :to_snake_keys => false
47
+ assert_equal 'third_case', obj.case_three
48
+ end
49
+
50
+ it 'should leave snake case alone without snake keys option false' do
51
+ obj = WhoopeeCushion::Inflate.from_hash @json
52
+ assert_equal 'third_case', obj.case_three
53
+ end
54
+ end
55
+
56
+ describe 'converting recursive hashes' do
57
+ before do
58
+ @json = { 'CaseOne' => {'caseTwo' => { 'ThisIsCaseThree' => 'value' } } }
59
+ end
60
+
61
+ it 'should convert keys to snake case and get the value' do
62
+ obj = WhoopeeCushion::Inflate.from_hash @json
63
+ assert_equal 'value', obj.case_one.case_two.this_is_case_three
64
+ end
65
+
66
+ it 'should not convert keys to snake case with snake keys option false' do
67
+ obj = WhoopeeCushion::Inflate.from_hash @json, :to_snake_keys => false
68
+ assert_equal 'value', obj.CaseOne.caseTwo.ThisIsCaseThree
69
+ end
70
+ end
71
+
72
+ describe 'converting deep arrays' do
73
+ before do
74
+ @json = { 'CaseOne' => [ { 'caseTwo' => 2, 'CaseThree' => [ { 'CaseFour' => 'four'}, 5 ] } ] }
75
+ end
76
+
77
+ it 'should get deep array values correctly' do
78
+ obj = WhoopeeCushion::Inflate.from_hash @json
79
+ assert_equal 2, obj.case_one[0].case_two
80
+ assert_equal 'four', obj.case_one[0].case_three[0].case_four
81
+ assert_equal 5, obj.case_one[0].case_three[1]
82
+ end
83
+
84
+ it 'should get deep array values correctly with snake keys option false' do
85
+ obj = WhoopeeCushion::Inflate.from_hash @json, :to_snake_keys => false
86
+ assert_equal 2, obj.CaseOne[0].caseTwo
87
+ assert_equal 'four', obj.CaseOne[0].CaseThree[0].CaseFour
88
+ assert_equal 5, obj.CaseOne[0].CaseThree[1]
89
+ end
90
+ end
91
+
92
+ describe 'converting from array' do
93
+ it "should return plain values unchanged" do
94
+ obj = WhoopeeCushion::Inflate.from_array [1, 2, 3, 'four', 'FiveFive', 'sixSix']
95
+ assert_equal 1, obj[0]
96
+ assert_equal 2, obj[1]
97
+ assert_equal 3, obj[2]
98
+ assert_equal 'four', obj[3]
99
+ assert_equal 'FiveFive', obj[4]
100
+ assert_equal 'sixSix', obj[5]
101
+ end
102
+
103
+ describe 'with an embedded hash' do
104
+ before do
105
+ @array = [1,2,3,'four',{'power' => 'to', 'ThePeople' => 'now'}]
106
+ end
107
+
108
+ it 'should convert an embedded hash' do
109
+ obj = WhoopeeCushion::Inflate.from_array @array
110
+ assert_equal 1, obj[0]
111
+ assert_equal 'to', obj[4].power
112
+ assert_equal 'now', obj[4].the_people
113
+ end
114
+
115
+ it 'should convert an embedded hash without changing keys with snake keys option false' do
116
+ obj = WhoopeeCushion::Inflate.from_array @array, :to_snake_keys => false
117
+ assert_equal 'now', obj[4].ThePeople
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,54 @@
1
+ require 'whoopee_cushion'
2
+ require 'minitest/unit'
3
+ require 'minitest/autorun'
4
+ require 'minitest/pride'
5
+ require 'byebug'
6
+ require 'benchmark'
7
+ require 'recursive-open-struct'
8
+
9
+ puts "Benchmarking 10000 operations on deep hash:"
10
+
11
+ hash = {:aaaaBbbb => 'ccccDDDD',
12
+ :a => 2, :b => 2, :c => 2, :d => 2, :e => 2,
13
+ :f => 2, :g => 2, :h => 2, :i => 2, :j => 2,
14
+ :k => 2, :l => 2, :m => 2, :n => 2,
15
+ 'eeeeEeee' =>[{"splandact" => {"Griffin" => 4444},
16
+ "splortitude" => [4,5,6]}, "3", "4"]}
17
+ puts "whoopee-cushion"
18
+ puts Benchmark.measure {
19
+ 10000.times do
20
+ a = WhoopeeCushion::Inflate.from_hash hash, :to_snake_keys => false
21
+ raise 'Incorrect value found' if a.eeeeEeee.first.splortitude[0] != 4
22
+ end
23
+ }
24
+ puts 'recursive-open-struct'
25
+
26
+ puts Benchmark.measure {
27
+ 10000.times do
28
+ a = RecursiveOpenStruct.new hash, :recurse_over_arrays => true
29
+ raise 'Incorrect value found' if a.eeeeEeee.first.splortitude[0] != 4
30
+ end
31
+ }
32
+
33
+
34
+
35
+ puts "Benchmarking 10000 operations on light hash:"
36
+
37
+ hash = {:a => 2, :b => 2, :c => 2, :d => 2, :e => 2,
38
+ :f => 2, :g => 2, :h => 2, :i => 2, :j => 2,
39
+ :k => 2, :l => 2, :m => 2, :n => 2, :c => "Lorem Ipsum"}
40
+
41
+
42
+ puts Benchmark.measure {
43
+ 10000.times do
44
+ WhoopeeCushion::Inflate.from_hash hash, :to_snake_keys => false
45
+ end
46
+ }
47
+
48
+ puts "Benchmarking 10000 OpenStruct creates"
49
+
50
+ puts Benchmark.measure {
51
+ 10000.times do
52
+ x = OpenStruct.new(hash)
53
+ end
54
+ }
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'whoopee_cushion/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "whoopee-cushion"
8
+ spec.version = WhoopeeCushion::VERSION
9
+ spec.authors = ["Ben Dunkley"]
10
+ spec.email = ["ben@chemica.co.uk"]
11
+ spec.summary = %q{Allow recursive inflation of JSON data into structs.}
12
+ spec.description = %q{Inflates JSON data recursively into structs to allow use of dot notation for data access.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "minitest", "~> 4.7.3"
24
+ end
25
+
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: whoopee-cushion
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ben Dunkley
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 4.7.3
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 4.7.3
55
+ description: Inflates JSON data recursively into structs to allow use of dot notation
56
+ for data access.
57
+ email:
58
+ - ben@chemica.co.uk
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - Gemfile
65
+ - Gemfile.lock
66
+ - LICENSE
67
+ - LICENSE.txt
68
+ - README.md
69
+ - Rakefile
70
+ - lib/whoopee_cushion.rb
71
+ - lib/whoopee_cushion/version.rb
72
+ - test/inflate_test.rb
73
+ - test/test_helper.rb
74
+ - whoopee_cushion.gemspec
75
+ homepage: ''
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.2.2
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: Allow recursive inflation of JSON data into structs.
99
+ test_files:
100
+ - test/inflate_test.rb
101
+ - test/test_helper.rb