evil-struct 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 +7 -0
- data/.codeclimate.yml +18 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +38 -0
- data/.travis.yml +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +104 -0
- data/Rakefile +6 -0
- data/codeclimate.yml +18 -0
- data/evil-struct.gemspec +22 -0
- data/lib/evil/struct/attributes.rb +24 -0
- data/lib/evil/struct.rb +143 -0
- data/lib/evil-struct.rb +4 -0
- data/spec/features/attributes_spec.rb +23 -0
- data/spec/features/constructor_aliases_spec.rb +39 -0
- data/spec/features/constructor_arguments_spec.rb +25 -0
- data/spec/features/equalizer_spec.rb +21 -0
- data/spec/features/hashifier_spec.rb +41 -0
- data/spec/features/shared_options_spec.rb +15 -0
- data/spec/spec_helper.rb +21 -0
- metadata +143 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1f83443166df4c93a0697048a1bb7a6c0757ff9b
|
4
|
+
data.tar.gz: a6942c1bda0626e51fdd0163cfe23d3e0098767c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a887a24df325459ea5bdd6b0ce655a7c07d038a65272268ce281e3b62d1304689dc134d57c5cc704cee84dce6a122d359781090c3c0ef5b8ba98227549c2c7e5
|
7
|
+
data.tar.gz: 8dfc7556f97bb308624d9755074b16273fda5bea610e4f599b0e0a98c6305c721638a32b785659ed1d4085d6a2109a3e46619955af27e09fcdd219a8121526e9
|
data/.codeclimate.yml
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
---
|
2
|
+
AllCops:
|
3
|
+
DisplayCopNames: true
|
4
|
+
DisplayStyleGuide: true
|
5
|
+
StyleGuideCopsOnly: true
|
6
|
+
TargetRubyVersion: 2.3
|
7
|
+
|
8
|
+
Style/Alias:
|
9
|
+
Enabled: false
|
10
|
+
|
11
|
+
Style/ClassAndModuleChildren:
|
12
|
+
EnforcedStyle: compact
|
13
|
+
|
14
|
+
Style/FileName:
|
15
|
+
Enabled: false
|
16
|
+
|
17
|
+
Style/FrozenStringLiteralComment:
|
18
|
+
Enabled: false
|
19
|
+
|
20
|
+
Style/StringLiterals:
|
21
|
+
EnforcedStyle: double_quotes
|
22
|
+
|
23
|
+
Style/StringLiteralsInInterpolation:
|
24
|
+
EnforcedStyle: double_quotes
|
25
|
+
|
26
|
+
# Settings for Struct#hashify
|
27
|
+
|
28
|
+
Metrics/CyclomaticComplexity:
|
29
|
+
Max: 7
|
30
|
+
|
31
|
+
Metrics/AbcSize:
|
32
|
+
Max: 17.12
|
33
|
+
|
34
|
+
Metrics/MethodLength:
|
35
|
+
Max: 15
|
36
|
+
|
37
|
+
Metrics/PerceivedComplexity:
|
38
|
+
Max: 8
|
data/.travis.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
---
|
2
|
+
sudo: false
|
3
|
+
language: ruby
|
4
|
+
cache: bundler
|
5
|
+
script:
|
6
|
+
- bundle exec rspec
|
7
|
+
- bundle exec rubocop
|
8
|
+
rvm:
|
9
|
+
- '2.3.0'
|
10
|
+
- ruby-head
|
11
|
+
- jruby-9.1.0.0
|
12
|
+
- jruby-head
|
13
|
+
before_install: gem install bundler -v 1.12.5
|
14
|
+
matrix:
|
15
|
+
allow_failures:
|
16
|
+
- rvm: ruby-head
|
17
|
+
- rvm: jruby-head
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Andrew Kozin (aka nepalez)
|
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,104 @@
|
|
1
|
+
# Evil::Struct
|
2
|
+
|
3
|
+
Nested structure with type constraints, based on the [dry-initializer][dry-initializer] DSL.
|
4
|
+
|
5
|
+
<a href="https://evilmartians.com/">
|
6
|
+
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
|
7
|
+
|
8
|
+
[![Gem Version][gem-badger]][gem]
|
9
|
+
[![Build Status][travis-badger]][travis]
|
10
|
+
[![Dependency Status][gemnasium-badger]][gemnasium]
|
11
|
+
[![Inline docs][inch-badger]][inch]
|
12
|
+
|
13
|
+
[gem-badger]: https://img.shields.io/gem/v/evil-struct.svg?style=flat
|
14
|
+
[gem]: https://rubygems.org/gems/evil-struct
|
15
|
+
[gemnasium-badger]: https://img.shields.io/gemnasium/nepalez/evil-struct.svg?style=flat
|
16
|
+
[gemnasium]: https://gemnasium.com/nepalez/evil-struct
|
17
|
+
[inch-badger]: http://inch-ci.org/github/nepalez/evil-struct.svg
|
18
|
+
[inch]: https://inch-ci.org/github/nepalez/evil-struct
|
19
|
+
[travis-badger]: https://img.shields.io/travis/nepalez/evil-struct/master.svg?style=flat
|
20
|
+
[travis]: https://travis-ci.org/nepalez/evil-struct
|
21
|
+
|
22
|
+
## Installation
|
23
|
+
|
24
|
+
Add this line to your application's Gemfile:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
gem 'evil-struct'
|
28
|
+
```
|
29
|
+
|
30
|
+
And then execute:
|
31
|
+
|
32
|
+
$ bundle
|
33
|
+
|
34
|
+
Or install it yourself as:
|
35
|
+
|
36
|
+
$ gem install evil-struct
|
37
|
+
|
38
|
+
## Synopsis
|
39
|
+
|
40
|
+
The structure is like [dry-struct][dry-struct] except for it controls optional attributes and default values aside of type constraints.
|
41
|
+
|
42
|
+
Its DSL is taken from [dry-initializer][dry-initializer]. Its method `attribute` is just an alias for `option`.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
require "evil-struct"
|
46
|
+
require "dry-types"
|
47
|
+
|
48
|
+
class Product < Evil::Struct
|
49
|
+
attribute :title
|
50
|
+
attribute :price, Dry::Types["coercible.float"]
|
51
|
+
attribute :quantity, Dry::Types["coercible.int"], default: proc { 0 }
|
52
|
+
|
53
|
+
# shared options
|
54
|
+
attributes optional: true do
|
55
|
+
attribute :subtitle
|
56
|
+
attribute :description
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Accepts both symbolic and string keys.
|
61
|
+
# Class methods `[]`, `call`, and `load` are just aliases for `new`
|
62
|
+
product = Product[title: "apple", "price" => "10.9", description: "a fruit"]
|
63
|
+
|
64
|
+
# Attributes are available via methods or `[]`
|
65
|
+
product.title # => "apple"
|
66
|
+
product[:price] # => 10.9
|
67
|
+
product["quantity"] # => 0
|
68
|
+
product.description # => "a fruit"
|
69
|
+
|
70
|
+
# unassigned value differs from `nil`
|
71
|
+
product.subtitle # => Dry::Initializer::UNDEFINED
|
72
|
+
|
73
|
+
# Raises in case a mandatory value not assigned
|
74
|
+
Product.new # BOOM! because neither title, nor price are assigned
|
75
|
+
|
76
|
+
# Hashifies all attributes except for undefined subtitle
|
77
|
+
# You can use `dump` as an alias for `to_h`
|
78
|
+
product.to_h
|
79
|
+
# => { title: "apple", price: 10.9, description: "a fruit", quantity: 0 }
|
80
|
+
|
81
|
+
# The structure is comparable to any object that responds to `#to_h`
|
82
|
+
product == { title: "apple", price: 10.9, description: "a fruit", quantity: 0 }
|
83
|
+
# => true
|
84
|
+
```
|
85
|
+
|
86
|
+
## Compatibility
|
87
|
+
|
88
|
+
Tested under rubies [compatible to MRI 2.2+](.travis.yml).
|
89
|
+
|
90
|
+
## Contributing
|
91
|
+
|
92
|
+
* [Fork the project](https://github.com/dry-rb/dry-initializer)
|
93
|
+
* Create your feature branch (`git checkout -b my-new-feature`)
|
94
|
+
* Add tests for it
|
95
|
+
* Commit your changes (`git commit -am '[UPDATE] Add some feature'`)
|
96
|
+
* Push to the branch (`git push origin my-new-feature`)
|
97
|
+
* Create a new Pull Request
|
98
|
+
|
99
|
+
## License
|
100
|
+
|
101
|
+
The gem is available as open source under the terms of the [MIT License][license].
|
102
|
+
|
103
|
+
[dry-initializer]: https://rom-rb.org/gems/dry-initializer
|
104
|
+
[dry-struct]: https://rom-rb.org/gems/dry-struct
|
data/Rakefile
ADDED
data/codeclimate.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
---
|
2
|
+
engines:
|
3
|
+
rubocop:
|
4
|
+
enabled: true
|
5
|
+
checks:
|
6
|
+
Rubocop/Style/FrozenStringLiteralComment:
|
7
|
+
enabled: false
|
8
|
+
Rubocop/Style/Documentation:
|
9
|
+
enabled: false
|
10
|
+
Rubocop/Style/ClassAndModuleChildren:
|
11
|
+
enabled: false
|
12
|
+
Rubocop/Style/SpaceInLambdaLiteral:
|
13
|
+
enabled: false
|
14
|
+
Style/SingleLineBlockParams:
|
15
|
+
enabled: false
|
16
|
+
ratings:
|
17
|
+
paths:
|
18
|
+
- "lib/**.rb"
|
data/evil-struct.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Gem::Specification.new do |gem|
|
2
|
+
gem.name = "evil-struct"
|
3
|
+
gem.version = "0.0.1"
|
4
|
+
gem.author = "Andrew Kozin (nepalez)"
|
5
|
+
gem.email = "andrew.kozin@gmail.com"
|
6
|
+
gem.homepage = "https://github.com/evilmartians/evil-client"
|
7
|
+
gem.summary = "Structure with type constraints based on dry-initializer"
|
8
|
+
gem.license = "MIT"
|
9
|
+
|
10
|
+
gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
11
|
+
gem.test_files = gem.files.grep(/^spec/)
|
12
|
+
gem.extra_rdoc_files = Dir["README.md", "LICENSE", "CHANGELOG.md"]
|
13
|
+
|
14
|
+
gem.required_ruby_version = ">= 2.3"
|
15
|
+
|
16
|
+
gem.add_runtime_dependency "dry-initializer", "~> 0.10"
|
17
|
+
|
18
|
+
gem.add_development_dependency "dry-types", "~> 0.9"
|
19
|
+
gem.add_development_dependency "rspec", "~> 3.0"
|
20
|
+
gem.add_development_dependency "rake", "~> 11"
|
21
|
+
gem.add_development_dependency "rubocop", "~> 0.44"
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Handler for shared options
|
2
|
+
class Evil::Struct::Attributes
|
3
|
+
# @private
|
4
|
+
def self.call(*args, &block)
|
5
|
+
new(*args).instance_eval(&block)
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def initialize(klass, **options)
|
11
|
+
@klass = klass
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
# Declares an attribute that shares options of the block
|
16
|
+
# @param (see Struct.attribute)
|
17
|
+
# @option (see Struct.attribute)
|
18
|
+
# @return (see Struct.attribute)
|
19
|
+
def attribute(name, type = nil, **options)
|
20
|
+
@klass.send :attribute, name, type, @options.merge(options)
|
21
|
+
end
|
22
|
+
alias_method :option, :attribute
|
23
|
+
alias_method :param, :attribute
|
24
|
+
end
|
data/lib/evil/struct.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
require "dry-initializer"
|
2
|
+
|
3
|
+
# Nested structure with type constraints, based on the `dry-initializer` DSL
|
4
|
+
class Evil::Struct
|
5
|
+
extend Dry::Initializer::Mixin
|
6
|
+
|
7
|
+
require_relative "struct/attributes"
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# Builds a struct from value that respond to `to_h` or `to_hash`
|
11
|
+
#
|
12
|
+
# @param [#to_h, #to_hash] value (nil)
|
13
|
+
# @return [Evil::Struct]
|
14
|
+
#
|
15
|
+
# @alias :call
|
16
|
+
# @alias :[]
|
17
|
+
# @alias :load
|
18
|
+
#
|
19
|
+
def new(value = {})
|
20
|
+
value if value.instance_of? self.class
|
21
|
+
|
22
|
+
hash = value if value.is_a? Hash
|
23
|
+
hash ||= value.to_h if value.respond_to? :to_h
|
24
|
+
hash ||= value.to_hash if value.respond_to? :to_hash
|
25
|
+
|
26
|
+
hash_with_symbolic_keys = hash.each_with_object({}) do |(key, val), obj|
|
27
|
+
obj[key.to_sym] = val
|
28
|
+
end
|
29
|
+
super hash_with_symbolic_keys
|
30
|
+
end
|
31
|
+
alias_method :call, :new
|
32
|
+
alias_method :[], :new
|
33
|
+
alias_method :load, :new
|
34
|
+
|
35
|
+
# Shares options between definitions made inside the block
|
36
|
+
#
|
37
|
+
# @param [Hash<Symbol, Object>] options Shared options
|
38
|
+
# @param [Proc] block Block with definitions of attributes
|
39
|
+
# @return [self] itself
|
40
|
+
#
|
41
|
+
def attributes(**options, &block)
|
42
|
+
Attributes.call(self, **options, &block)
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the list of defined attributes
|
47
|
+
#
|
48
|
+
# @return [Array<Symbol>]
|
49
|
+
#
|
50
|
+
def list_of_attributes
|
51
|
+
@list_of_attributes ||= []
|
52
|
+
end
|
53
|
+
|
54
|
+
# Declares the attribute
|
55
|
+
#
|
56
|
+
# @param [#to_sym] name The name of the key
|
57
|
+
# @param [#call] type (nil) The constraint
|
58
|
+
# @option options [#to_sym] :as The name of the attribute
|
59
|
+
# @option options [Proc] :default Block returning a default value
|
60
|
+
# @option options [Boolean] :optional (nil) Whether key is optional
|
61
|
+
# @return [self]
|
62
|
+
#
|
63
|
+
# @alias :attribute
|
64
|
+
# @alias :param
|
65
|
+
#
|
66
|
+
def option(name, type = nil, as: nil, **opts)
|
67
|
+
super.tap { list_of_attributes << (as || name).to_sym }
|
68
|
+
self
|
69
|
+
end
|
70
|
+
alias_method :attribute, :option
|
71
|
+
alias_method :param, :option
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def inherited(klass)
|
76
|
+
super
|
77
|
+
klass.instance_variable_set :@list_of_attributes, list_of_attributes.dup
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Checks an equality to other object that respond to `to_h` or `to_hash`
|
82
|
+
#
|
83
|
+
# @param [Object] other
|
84
|
+
# @return [Boolean]
|
85
|
+
#
|
86
|
+
def ==(other)
|
87
|
+
if other&.respond_to?(:to_h)
|
88
|
+
to_h == other.to_h
|
89
|
+
elsif other.respond_to?(:to_hash)
|
90
|
+
to_h == other.to_hash
|
91
|
+
else
|
92
|
+
false
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Converts nested structure to hash
|
97
|
+
#
|
98
|
+
# Makes conversion through nested hashes, arrays, enumerables, as well
|
99
|
+
# as trhough values that respond to `to_a`, `to_h`, and `to_hash`.
|
100
|
+
# Doesn't convert `nil`.
|
101
|
+
#
|
102
|
+
# @return [Hash]
|
103
|
+
#
|
104
|
+
# @alias :to_hash
|
105
|
+
# @alias :dump
|
106
|
+
#
|
107
|
+
def to_h
|
108
|
+
self.class.list_of_attributes.each_with_object({}) do |key, hash|
|
109
|
+
val = send(key)
|
110
|
+
hash[key] = hashify(val) unless val == Dry::Initializer::UNDEFINED
|
111
|
+
end
|
112
|
+
end
|
113
|
+
alias_method :to_hash, :to_h
|
114
|
+
alias_method :dump, :to_h
|
115
|
+
|
116
|
+
# @!method [](key)
|
117
|
+
# Gets the attribute value by name
|
118
|
+
#
|
119
|
+
# @param [Symbol, String] key The name of the attribute
|
120
|
+
# @return [Object] A value of the attribute
|
121
|
+
#
|
122
|
+
alias_method :[], :send
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def hashify(value)
|
127
|
+
if value.is_a? Hash
|
128
|
+
value.each_with_object({}) { |(key, val), obj| obj[key] = hashify(val) }
|
129
|
+
elsif value.is_a? Array
|
130
|
+
value.map { |item| hashify(item) }
|
131
|
+
elsif value&.respond_to? :to_a
|
132
|
+
hashify(value.to_a)
|
133
|
+
elsif value&.respond_to? :to_h
|
134
|
+
hashify(value.to_h)
|
135
|
+
elsif value.respond_to? :to_hash
|
136
|
+
hashify(value.to_hash)
|
137
|
+
elsif value.is_a? Enumerable
|
138
|
+
value.map { |item| hashify(item) }
|
139
|
+
else
|
140
|
+
value
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
data/lib/evil-struct.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "attributes" do
|
4
|
+
before do
|
5
|
+
class Test::Foo < Evil::Struct
|
6
|
+
attribute :"some argument", as: "qux"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:struct) { Test::Foo.new "some argument": "bar" }
|
11
|
+
|
12
|
+
it "accessible via method" do
|
13
|
+
expect(struct.qux).to eq "bar"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "accessible by symbolic key" do
|
17
|
+
expect(struct[:qux]).to eq "bar"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "accessible by string key" do
|
21
|
+
expect(struct["qux"]).to eq "bar"
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "constructor aliases" do
|
4
|
+
it ".new" do
|
5
|
+
class Test::Foo < Evil::Struct
|
6
|
+
attribute :foo
|
7
|
+
attribute :baz, default: proc { "qux" }
|
8
|
+
end
|
9
|
+
|
10
|
+
expect(Test::Foo.new foo: "bar").to eq foo: "bar", baz: "qux"
|
11
|
+
end
|
12
|
+
|
13
|
+
it ".call" do
|
14
|
+
class Test::Foo < Evil::Struct
|
15
|
+
attribute :foo
|
16
|
+
attribute :baz, default: proc { "qux" }
|
17
|
+
end
|
18
|
+
|
19
|
+
expect(Test::Foo.call foo: "bar").to eq foo: "bar", baz: "qux"
|
20
|
+
end
|
21
|
+
|
22
|
+
it ".load" do
|
23
|
+
class Test::Foo < Evil::Struct
|
24
|
+
attribute :foo
|
25
|
+
attribute :baz, default: proc { "qux" }
|
26
|
+
end
|
27
|
+
|
28
|
+
expect(Test::Foo.load foo: "bar").to eq foo: "bar", baz: "qux"
|
29
|
+
end
|
30
|
+
|
31
|
+
it ".[]" do
|
32
|
+
class Test::Foo < Evil::Struct
|
33
|
+
attribute :foo
|
34
|
+
attribute :baz, default: proc { "qux" }
|
35
|
+
end
|
36
|
+
|
37
|
+
expect(Test::Foo[foo: "bar"]).to eq foo: "bar", baz: "qux"
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "constructor" do
|
4
|
+
before do
|
5
|
+
class Test::Foo < Evil::Struct
|
6
|
+
attribute :foo, default: proc { "qux" }
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
it "accepts hash with symbolic keys" do
|
11
|
+
expect(Test::Foo.new foo: "bar").to eq foo: "bar"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "accepts hash with string keys" do
|
15
|
+
expect(Test::Foo.new "foo" => "bar").to eq foo: "bar"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "accepts nil" do
|
19
|
+
expect(Test::Foo.new nil).to eq foo: "qux"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "accepts no arguments" do
|
23
|
+
expect(Test::Foo.new).to eq foo: "qux"
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "equalizer" do
|
4
|
+
it "uses #to_h for comparison" do
|
5
|
+
class Test::Foo < Evil::Struct
|
6
|
+
attribute :foo
|
7
|
+
end
|
8
|
+
|
9
|
+
expect(Test::Foo.new foo: "bar").to eq foo: "bar"
|
10
|
+
expect(Test::Foo.new foo: "bar").to eq double(to_h: { foo: "bar" })
|
11
|
+
end
|
12
|
+
|
13
|
+
it "makes struct not equal to nil" do
|
14
|
+
class Test::Foo < Evil::Struct
|
15
|
+
attribute :foo, optional: true
|
16
|
+
end
|
17
|
+
|
18
|
+
expect(Test::Foo.new).to eq({})
|
19
|
+
expect(Test::Foo.new).not_to eq nil
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "hashifier" do
|
4
|
+
before do
|
5
|
+
class Test::Foo < Evil::Struct
|
6
|
+
attribute :foo
|
7
|
+
attribute :bar, optional: true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it "converts struct to hash" do
|
12
|
+
result = Test::Foo[foo: "bar", bar: "baz"].to_h
|
13
|
+
|
14
|
+
expect(result).to be_instance_of Hash
|
15
|
+
expect(result).to eq foo: "bar", bar: "baz"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "hides unassigned values" do
|
19
|
+
result = Test::Foo[foo: "bar"].to_h
|
20
|
+
|
21
|
+
expect(result).to eq foo: "bar"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "has alias .to_hash" do
|
25
|
+
result = Test::Foo[foo: "bar"].to_hash
|
26
|
+
|
27
|
+
expect(result).to eq foo: "bar"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "has alias .dump" do
|
31
|
+
result = Test::Foo[foo: "bar"].dump
|
32
|
+
|
33
|
+
expect(result).to eq foo: "bar"
|
34
|
+
end
|
35
|
+
|
36
|
+
it "applied deeply" do
|
37
|
+
data = { foo: double(to_h: { baz: [double(to_hash: { qux: nil })] }) }
|
38
|
+
|
39
|
+
expect(Test::Foo[data].to_h).to eq foo: { baz: [{ qux: nil }] }
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "shared options" do
|
4
|
+
it "supported via .attributes" do
|
5
|
+
class Test::Foo < Evil::Struct
|
6
|
+
attributes type: Dry::Types["strict.string"], default: proc { "bar" } do
|
7
|
+
attribute :foo
|
8
|
+
attribute :baz, default: proc { "qux" }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
expect(Test::Foo.new).to eq foo: "bar", baz: "qux"
|
13
|
+
expect { Test::Foo.new foo: 1 }.to raise_error(TypeError)
|
14
|
+
end
|
15
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require "pry"
|
3
|
+
rescue LoadError
|
4
|
+
nil
|
5
|
+
end
|
6
|
+
|
7
|
+
require "evil-struct"
|
8
|
+
require "dry-types"
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.order = :random
|
12
|
+
config.filter_run focus: true
|
13
|
+
config.run_all_when_everything_filtered = true
|
14
|
+
|
15
|
+
# Prepare the Test namespace for constants defined in specs
|
16
|
+
config.around(:each) do |example|
|
17
|
+
Test = Class.new(Module)
|
18
|
+
example.run
|
19
|
+
Object.send :remove_const, :Test
|
20
|
+
end
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: evil-struct
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrew Kozin (nepalez)
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-11-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: dry-initializer
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.10'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: dry-types
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.9'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.9'
|
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
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '11'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '11'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.44'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.44'
|
83
|
+
description:
|
84
|
+
email: andrew.kozin@gmail.com
|
85
|
+
executables: []
|
86
|
+
extensions: []
|
87
|
+
extra_rdoc_files:
|
88
|
+
- README.md
|
89
|
+
files:
|
90
|
+
- ".codeclimate.yml"
|
91
|
+
- ".gitignore"
|
92
|
+
- ".rspec"
|
93
|
+
- ".rubocop.yml"
|
94
|
+
- ".travis.yml"
|
95
|
+
- Gemfile
|
96
|
+
- LICENSE.txt
|
97
|
+
- README.md
|
98
|
+
- Rakefile
|
99
|
+
- codeclimate.yml
|
100
|
+
- evil-struct.gemspec
|
101
|
+
- lib/evil-struct.rb
|
102
|
+
- lib/evil/struct.rb
|
103
|
+
- lib/evil/struct/attributes.rb
|
104
|
+
- spec/features/attributes_spec.rb
|
105
|
+
- spec/features/constructor_aliases_spec.rb
|
106
|
+
- spec/features/constructor_arguments_spec.rb
|
107
|
+
- spec/features/equalizer_spec.rb
|
108
|
+
- spec/features/hashifier_spec.rb
|
109
|
+
- spec/features/shared_options_spec.rb
|
110
|
+
- spec/spec_helper.rb
|
111
|
+
homepage: https://github.com/evilmartians/evil-client
|
112
|
+
licenses:
|
113
|
+
- MIT
|
114
|
+
metadata: {}
|
115
|
+
post_install_message:
|
116
|
+
rdoc_options: []
|
117
|
+
require_paths:
|
118
|
+
- lib
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '2.3'
|
124
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
requirements: []
|
130
|
+
rubyforge_project:
|
131
|
+
rubygems_version: 2.6.4
|
132
|
+
signing_key:
|
133
|
+
specification_version: 4
|
134
|
+
summary: Structure with type constraints based on dry-initializer
|
135
|
+
test_files:
|
136
|
+
- spec/features/attributes_spec.rb
|
137
|
+
- spec/features/constructor_aliases_spec.rb
|
138
|
+
- spec/features/constructor_arguments_spec.rb
|
139
|
+
- spec/features/equalizer_spec.rb
|
140
|
+
- spec/features/hashifier_spec.rb
|
141
|
+
- spec/features/shared_options_spec.rb
|
142
|
+
- spec/spec_helper.rb
|
143
|
+
has_rdoc:
|