nested_struct 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e20230e4bbc23eee451104b684a2d23f4b7b7b01
4
+ data.tar.gz: 4e18420e5346de8da7b71db5d49b7c22362950c4
5
+ SHA512:
6
+ metadata.gz: 97e10af76a774ed5c2a6e8fddb922912d07a22756793d103b5a9fddf3daaaba9e4865b5f0f08063f67d9224db300d5e080eab9e565c2ea3e10229444632528d2
7
+ data.tar.gz: 943170d3ca5ac6e4a27210dfe1f3a78b0e403f48ad95a18f4a6e394a2b096c2fc15f40a1f70d768e5323eea2a559971ce41cfeba7ff32325596c4455c0575d64
data/.gitignore ADDED
@@ -0,0 +1,11 @@
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
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.16.2
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in nested_struct.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Krzysztof Buszewicz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all 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,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # NestedStruct
2
+
3
+ Build nested structs from hash/array structures.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'nested_struct'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install nested_struct
20
+
21
+ ## Usage
22
+
23
+ ```ruby
24
+ class AddressForm
25
+ include NestedStruct::Interface
26
+
27
+ field :street
28
+ end
29
+
30
+ class SkillForm
31
+ include NestedStruct::Interface
32
+
33
+ field :name
34
+ end
35
+
36
+ class UserForm
37
+ include NestedStruct::Interface
38
+
39
+ field :name
40
+ field :address, AddressForm
41
+ field :skills, [SkillForm]
42
+ end
43
+
44
+ attributes = {
45
+ name: 'Tom',
46
+ address: { street: 'Long' },
47
+ skills: [{ name: 'Jumping' }, { name: 'Kicking' }, {}]
48
+ }
49
+
50
+ form = UserForm.new(attributes)
51
+ #=> #<UserForm:0x0055cceb5c9438 @name="Tom", @address=#<AddressForm:0x0055cceb5c9320 @street="Long">, @skills=[#<SkillForm:0x0055cceb5c91e0 @name="Jumping">, #<SkillForm:0x0055cceb5c9118 @name="Kicking">, #<SkillForm:0x0055cceb5c90a0 @name=nil>]>
52
+ ```
53
+
54
+ ## Contributing
55
+
56
+ Bug reports and pull requests are welcome.
57
+
58
+ ## License
59
+
60
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -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
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "nested_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/setup ADDED
@@ -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,44 @@
1
+ module NestedStruct
2
+ class Coercer
3
+ attr_reader :type, :target
4
+
5
+ def initialize(expression)
6
+ if expression.is_a?(Class)
7
+ @type = :single
8
+ @target = expression
9
+ elsif expression.is_a?(Array)
10
+ raise Errors::InvalidCoercerExpression if expression.size != 1
11
+ @type = :multiple
12
+ @target = expression.first
13
+ else
14
+ raise Errors::InvalidCoercerExpression
15
+ end
16
+
17
+ raise Errors::InvalidCoercerTarget if !valid_target?
18
+ end
19
+
20
+ def coerce(value)
21
+ if multiple?
22
+ raise Errors::InvalidCoercerValue if !value.is_a?(Array)
23
+ value.map { |e| single_coercion(e) }
24
+ else
25
+ single_coercion(value)
26
+ end
27
+ end
28
+
29
+ def multiple?
30
+ type == :multiple
31
+ end
32
+
33
+ private
34
+
35
+ def valid_target?
36
+ target.ancestors.include?(NestedStruct::Interface)
37
+ end
38
+
39
+ def single_coercion(hash)
40
+ raise Errors::InvalidCoercerValue if !hash.is_a?(Hash)
41
+ target.new(hash)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,9 @@
1
+ module NestedStruct
2
+ module Errors
3
+ class InvalidCoercerExpression < StandardError
4
+ def initialize
5
+ super('Coercer expression should be a class or an array containing class.')
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module NestedStruct
2
+ module Errors
3
+ class InvalidCoercerTarget < StandardError
4
+ def initialize
5
+ super('Coercer target\'s ancestors must include NestedStruct::Interface.')
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module NestedStruct
2
+ module Errors
3
+ class InvalidCoercerValue < StandardError
4
+ def initialize
5
+ super('Coercer value must be an array for multiple coercion or a hash for single one.')
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,20 @@
1
+ module NestedStruct
2
+ class Field
3
+ attr_reader :name, :coercer
4
+
5
+ def initialize(name, coercer_expression = nil)
6
+ @name = name
7
+ @coercer = Coercer.new(coercer_expression) if coercer_expression
8
+ end
9
+
10
+ def with(value)
11
+ return value if value.nil?
12
+ return value if !coerced?
13
+ coercer.coerce(value)
14
+ end
15
+
16
+ def coerced?
17
+ !coercer.nil?
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,52 @@
1
+ module NestedStruct
2
+ module Interface
3
+ def self.included(base)
4
+ base.class_eval do
5
+ @field_names ||= []
6
+ @fields ||= []
7
+
8
+ class << self
9
+ attr_reader :field_names, :fields
10
+
11
+ def field(name, coercer_expression = nil)
12
+ if !field_names.include?(name)
13
+ @field_names << name
14
+ @fields << Field.new(name, coercer_expression)
15
+ attr_accessor(name)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ def initialize(attributes = {})
23
+ assign_field_values(attributes)
24
+ end
25
+
26
+ def to_hash
27
+ Hash[
28
+ self.class.fields.map do |field|
29
+ [field.name.to_sym, primitivize(public_send(field.name))]
30
+ end
31
+ ]
32
+ end
33
+
34
+ private
35
+
36
+ def assign_field_values(attributes = {})
37
+ self.class.fields.each do |field|
38
+ instance_variable_set(
39
+ "@#{field.name}",
40
+ field.with(attributes[field.name])
41
+ )
42
+ end
43
+ end
44
+
45
+ def primitivize(value)
46
+ return nil if value.nil?
47
+ return value.map(&:to_hash) if value.is_a?(Array)
48
+ return value.to_hash if value.respond_to?(:to_hash)
49
+ value
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,3 @@
1
+ module NestedStruct
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,13 @@
1
+ require 'nested_struct/version'
2
+
3
+ require 'nested_struct/errors/invalid_coercer_expression'
4
+ require 'nested_struct/errors/invalid_coercer_target'
5
+ require 'nested_struct/errors/invalid_coercer_value'
6
+
7
+ require 'nested_struct/coercer'
8
+ require 'nested_struct/field'
9
+
10
+ require 'nested_struct/interface'
11
+
12
+ module NestedStruct
13
+ end
@@ -0,0 +1,26 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "nested_struct/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "nested_struct"
7
+ spec.version = NestedStruct::VERSION
8
+ spec.authors = ["Krzysztof Buszewicz"]
9
+ spec.email = ["krzysztof.buszewicz@gmail.com"]
10
+
11
+ spec.summary = "Compose nested structs from hash/array structures."
12
+ spec.homepage = "https://github.com/buszu/nested_struct"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
16
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ end
18
+
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.16"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec", "~> 3.0"
26
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nested_struct
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Krzysztof Buszewicz
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-06-14 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.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description:
56
+ email:
57
+ - krzysztof.buszewicz@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".travis.yml"
65
+ - Gemfile
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - bin/console
70
+ - bin/setup
71
+ - lib/nested_struct.rb
72
+ - lib/nested_struct/coercer.rb
73
+ - lib/nested_struct/errors/invalid_coercer_expression.rb
74
+ - lib/nested_struct/errors/invalid_coercer_target.rb
75
+ - lib/nested_struct/errors/invalid_coercer_value.rb
76
+ - lib/nested_struct/field.rb
77
+ - lib/nested_struct/interface.rb
78
+ - lib/nested_struct/version.rb
79
+ - nested_struct.gemspec
80
+ homepage: https://github.com/buszu/nested_struct
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 2.5.1
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: Compose nested structs from hash/array structures.
104
+ test_files: []