camel_snake_struct 0.1.0
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 +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.rubocop.yml +21 -0
- data/CHANGELOG.md +2 -0
- data/README.md +81 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/bin/setup +8 -0
- data/camel_snake_struct.gemspec +25 -0
- data/gems.rb +15 -0
- data/lib/camel_snake_struct.rb +139 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e030119b3af6eaca6666865803895cf418913a62152decc3578af302ee62dc60
|
4
|
+
data.tar.gz: 9bd9ef3d9d215900202ba1388c13b784e54207f7e11accf96a6bd23332b0bcb7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: eca1edf5388aa041ac1c68438a2e24139f4a2e9263470f766bef1ff65c95943bd5593dd47d9ffe14d423a7ada766aedc6f8834859b142551dc8593e4c84d2ee3
|
7
|
+
data.tar.gz: 0306446d56b056675a72d591ba125b7eaa1a98e507069d4cbedd76161b5983eff18d1e47de77306e8e4d44b074bb2517a08a6e1acbf0d8bd0d0a6aa2629aaa73
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-rspec
|
3
|
+
- rubocop-rake
|
4
|
+
|
5
|
+
AllCops:
|
6
|
+
NewCops: enable
|
7
|
+
TargetRubyVersion: 2.4
|
8
|
+
Exclude:
|
9
|
+
- 'vendor/**/*'
|
10
|
+
|
11
|
+
Style:
|
12
|
+
Enabled: false
|
13
|
+
|
14
|
+
Metrics/AbcSize:
|
15
|
+
Max: 25
|
16
|
+
|
17
|
+
Metrics/MethodLength:
|
18
|
+
Max: 15
|
19
|
+
|
20
|
+
Metrics/ClassLength:
|
21
|
+
Max: 200
|
data/CHANGELOG.md
ADDED
data/README.md
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# CamelSnake
|
2
|
+
|
3
|
+
Easily access camelCased hashes in a ruby_friendly_way.
|
4
|
+
Main focus is handling responses from APIs that use camelCased keys.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'camel_snake'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle install
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install camel_snake
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
For once of hashes
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
result = CamelSnakeStruct.new('version' => 1, 'rubyVersion' => '2.5.0',
|
28
|
+
'sites' => [{ 'url' => "https://duckduckgo.com", 'searchEngine' => true },
|
29
|
+
{ 'url' => "https://d.tube/", 'searchEngine' => false }])
|
30
|
+
|
31
|
+
puts result.version # 1
|
32
|
+
puts result.ruby_version # 2.5.0
|
33
|
+
puts result.sites[0].url # https://duckduckgo.com
|
34
|
+
puts result.sites[1].url # https://d.tube/
|
35
|
+
puts result.unknown # NoMethodError
|
36
|
+
puts result['version'] # 1
|
37
|
+
```
|
38
|
+
|
39
|
+
Or Learning Structs
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
MyLearningStruct = Class.new(CamelSnakeStruct)
|
43
|
+
|
44
|
+
result1 = MyLearningStruct.new('data' => [{ 'name' => 'Jeff' }])
|
45
|
+
puts result1.data.map(&:name) # ["Jeff"]
|
46
|
+
# never received errors key before
|
47
|
+
puts result.errors # NoMethodError
|
48
|
+
|
49
|
+
result2 = MyLearningStruct.new('errors' => ['failed to get response'])
|
50
|
+
# it remembers the shape from the first succesfull request
|
51
|
+
puts result2.data.map(&:name) # []
|
52
|
+
puts result.errors # ["failed to get response"]
|
53
|
+
|
54
|
+
MyLoadedStruct = Class.new(CamelSnakeStruct)
|
55
|
+
|
56
|
+
MyLoadedStruct.example('data' => [{ 'name' => 'Jeff' }], 'errors' => [], 'date' => { 'timezone' => 'UTC', 'unixTime' => 0})
|
57
|
+
|
58
|
+
result3 = MyLoadedStruct.new({'date' => { }})
|
59
|
+
puts result3.data # []
|
60
|
+
puts result3.errors # []
|
61
|
+
puts result3.date.timezone # nil
|
62
|
+
puts result3.date.unix_time # nil
|
63
|
+
```
|
64
|
+
|
65
|
+
### Limitations
|
66
|
+
|
67
|
+
* Expects to receive a hash
|
68
|
+
* Only works with string keys
|
69
|
+
* Does not work well with keys that point to array of arrays
|
70
|
+
* expects the hash to always have the same struct for learning structs
|
71
|
+
|
72
|
+
## Development
|
73
|
+
|
74
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
75
|
+
|
76
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
77
|
+
|
78
|
+
## Contributing
|
79
|
+
|
80
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/camel_snake.
|
81
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "camel_snake_struct"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/rspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rspec' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../gems.rb",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rspec-core", "rspec")
|
data/bin/rubocop
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rubocop' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../gems.rb",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rubocop", "rubocop")
|
data/bin/setup
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = "camel_snake_struct"
|
3
|
+
spec.version = "0.1.0"
|
4
|
+
spec.authors = ["Grant Petersen-Speelman"]
|
5
|
+
spec.email = ["grant@nexl.io"]
|
6
|
+
|
7
|
+
spec.summary = "Easily access camelCased hashes in a ruby friendly way"
|
8
|
+
spec.description = "Easily access camelCased hashes in a ruby friendly way"
|
9
|
+
spec.homepage = "https://github.com/NEXL-LTS/camel_snake_struct-ruby"
|
10
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
|
11
|
+
|
12
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
13
|
+
spec.metadata["source_code_uri"] = "https://github.com/NEXL-LTS/camel_snake_struct-ruby"
|
14
|
+
spec.metadata["changelog_uri"] = "https://github.com/NEXL-LTS/camel_snake_struct/CHANGELOG.md"
|
15
|
+
|
16
|
+
# Specify which files should be added to the gem when it is released.
|
17
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
18
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
19
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
20
|
+
end
|
21
|
+
spec.bindir = "exe"
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
spec.add_dependency 'activesupport', '>= 3.2', '< 7.0'
|
25
|
+
end
|
data/gems.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in camel_snake.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem "rake", "~> 12.0"
|
7
|
+
gem "rspec", "~> 3.0"
|
8
|
+
gem "rubocop"
|
9
|
+
gem "rubocop-rake"
|
10
|
+
gem "rubocop-rspec"
|
11
|
+
gem "simplecov"
|
12
|
+
|
13
|
+
if ENV['GEM_VERSIONS'] == 'min'
|
14
|
+
gem 'activesupport', '~> 3.2.0'
|
15
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'active_support/core_ext/string'
|
2
|
+
|
3
|
+
class CamelSnakeStruct
|
4
|
+
def self.example(data)
|
5
|
+
new_example = new(data)
|
6
|
+
walk_example(new_example)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.walk_example(new_example)
|
10
|
+
new_example.send(:_method_to_key).keys.each do |m_name|
|
11
|
+
result = new_example.public_send(m_name)
|
12
|
+
if result.is_a?(CamelSnakeStruct)
|
13
|
+
walk_example(result)
|
14
|
+
elsif result.is_a?(Array) && result.first.is_a?(CamelSnakeStruct)
|
15
|
+
walk_example(result.first)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(hash)
|
21
|
+
@_raw_hash = hash&.to_h || {}
|
22
|
+
@_method_to_key = @_raw_hash.keys.each_with_object({}) do |key, mapping|
|
23
|
+
normalize_key = key.gsub('@', '').gsub('.', '_')
|
24
|
+
mapping[normalize_key] = key
|
25
|
+
mapping[normalize_key.underscore] = key
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def [](key)
|
30
|
+
_val(@_raw_hash[key])
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_h
|
34
|
+
to_hash
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_hash
|
38
|
+
@_raw_hash
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
attr_reader :_method_to_key
|
44
|
+
|
45
|
+
def _val(val)
|
46
|
+
if val.is_a?(Hash)
|
47
|
+
CamelSnakeStruct.new(val)
|
48
|
+
elsif val.is_a?(Array)
|
49
|
+
val.map { |v| _val(v) }
|
50
|
+
else
|
51
|
+
val
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def method_missing(method_name, *arguments, &block)
|
56
|
+
camelize_key = __method_to_key(method_name)
|
57
|
+
if camelize_key
|
58
|
+
if _define_new_method(method_name, camelize_key)
|
59
|
+
send(method_name)
|
60
|
+
else # no method defined for empty arrays as we don't know what it returns
|
61
|
+
@_raw_hash[camelize_key]
|
62
|
+
end
|
63
|
+
else
|
64
|
+
super
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def respond_to_missing?(method_name, include_private = false)
|
69
|
+
camelize_key = __method_to_key(method_name)
|
70
|
+
!camelize_key.nil? || super
|
71
|
+
end
|
72
|
+
|
73
|
+
def __method_to_key(method_name)
|
74
|
+
@_method_to_key[method_name.to_s]
|
75
|
+
end
|
76
|
+
|
77
|
+
def _define_hash_method(name, key)
|
78
|
+
is_sub_class = self.class != CamelSnakeStruct
|
79
|
+
if is_sub_class
|
80
|
+
klass = _define_sub_class(name)
|
81
|
+
self.class.send(:define_method, name) { @_raw_hash[key] && klass.new(@_raw_hash[key]) }
|
82
|
+
else
|
83
|
+
define_singleton_method(name) { CamelSnakeStruct.new(@_raw_hash[key]) }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def _define_array_method(name, key)
|
88
|
+
is_sub_class = self.class != CamelSnakeStruct
|
89
|
+
if is_sub_class
|
90
|
+
klass = _define_sub_class(name.to_s.singularize)
|
91
|
+
self.class.send(:define_method, name) { (@_raw_hash[key] || []).map { |v| klass.new(v) } }
|
92
|
+
else
|
93
|
+
define_singleton_method(name) { @_raw_hash[key].map { |v| CamelSnakeStruct.new(v) } }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def _define_scaler_array_method(name, key)
|
98
|
+
is_sub_class = self.class != CamelSnakeStruct
|
99
|
+
if is_sub_class
|
100
|
+
self.class.send(:define_method, name) { (@_raw_hash[key] || []) }
|
101
|
+
else
|
102
|
+
define_singleton_method(name) { @_raw_hash[key] }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def _define_value_method(name, key)
|
107
|
+
is_sub_class = self.class != CamelSnakeStruct
|
108
|
+
if is_sub_class
|
109
|
+
self.class.send(:define_method, name) { @_raw_hash[key] }
|
110
|
+
else
|
111
|
+
define_singleton_method(name) { @_raw_hash[key] }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def _define_new_method(name, key)
|
116
|
+
name = name.to_sym
|
117
|
+
val = @_raw_hash[key]
|
118
|
+
if val.is_a?(Hash)
|
119
|
+
_define_hash_method(name, key)
|
120
|
+
elsif val.is_a?(Array) && val.first.is_a?(Hash)
|
121
|
+
_define_array_method(name, key)
|
122
|
+
elsif val.is_a?(Array) && val.empty?
|
123
|
+
return false
|
124
|
+
elsif val.is_a?(Array)
|
125
|
+
_define_scaler_array_method(name, key)
|
126
|
+
else
|
127
|
+
_define_value_method(name, key)
|
128
|
+
end
|
129
|
+
true
|
130
|
+
end
|
131
|
+
|
132
|
+
def _define_sub_class(name)
|
133
|
+
sub_class_name = name.to_s.camelize(:upper)
|
134
|
+
self.class.const_get(sub_class_name, false)
|
135
|
+
rescue NameError
|
136
|
+
self.class.const_set(sub_class_name, Class.new(CamelSnakeStruct))
|
137
|
+
self.class.const_get(sub_class_name, false)
|
138
|
+
end
|
139
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: camel_snake_struct
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Grant Petersen-Speelman
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-01-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.2'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '7.0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.2'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '7.0'
|
33
|
+
description: Easily access camelCased hashes in a ruby friendly way
|
34
|
+
email:
|
35
|
+
- grant@nexl.io
|
36
|
+
executables: []
|
37
|
+
extensions: []
|
38
|
+
extra_rdoc_files: []
|
39
|
+
files:
|
40
|
+
- ".gitignore"
|
41
|
+
- ".rspec"
|
42
|
+
- ".rubocop.yml"
|
43
|
+
- CHANGELOG.md
|
44
|
+
- README.md
|
45
|
+
- Rakefile
|
46
|
+
- bin/console
|
47
|
+
- bin/rspec
|
48
|
+
- bin/rubocop
|
49
|
+
- bin/setup
|
50
|
+
- camel_snake_struct.gemspec
|
51
|
+
- gems.rb
|
52
|
+
- lib/camel_snake_struct.rb
|
53
|
+
homepage: https://github.com/NEXL-LTS/camel_snake_struct-ruby
|
54
|
+
licenses: []
|
55
|
+
metadata:
|
56
|
+
homepage_uri: https://github.com/NEXL-LTS/camel_snake_struct-ruby
|
57
|
+
source_code_uri: https://github.com/NEXL-LTS/camel_snake_struct-ruby
|
58
|
+
changelog_uri: https://github.com/NEXL-LTS/camel_snake_struct/CHANGELOG.md
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 2.4.0
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubygems_version: 3.1.4
|
75
|
+
signing_key:
|
76
|
+
specification_version: 4
|
77
|
+
summary: Easily access camelCased hashes in a ruby friendly way
|
78
|
+
test_files: []
|