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.
@@ -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
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ vendor/bundle
13
+ gems.locked
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -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
@@ -0,0 +1,2 @@
1
+ # 0.1.0
2
+ * initial version
@@ -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
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -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__)
@@ -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")
@@ -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")
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -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: []