dry-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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e8ad25e8537c7c9b1762fa73ff25103adddc4b45
4
+ data.tar.gz: 1a2be7946a5fc0b70f9b40157ce8c0f6f57fcec0
5
+ SHA512:
6
+ metadata.gz: 9d3aace8acda03bd17706d3b1e3b0ad14584639b8783cee401e2e91ee30f91a55130d99bac7b8994b6eb980ef15b42c772b346387dfb807b6933a0bd67b7c5d3
7
+ data.tar.gz: b36ba76069268bf7f849de3a38102072229f99264d967df36a63dba09ffc90e394a9d034e8369ca0bad0486284d0cdab62e0146a7382bd27177c9ce2dcf56c1e
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require ./spec/spec_helper
@@ -0,0 +1,32 @@
1
+ language: ruby
2
+ sudo: false
3
+ cache: bundler
4
+ bundler_args: --without benchmarks tools
5
+ script:
6
+ - bundle exec rake spec
7
+ rvm:
8
+ - 2.0
9
+ - 2.1
10
+ - 2.2
11
+ - 2.3.0
12
+ - rbx-2
13
+ - jruby-9000
14
+ - ruby-head
15
+ env:
16
+ global:
17
+ - JRUBY_OPTS='--dev -J-Xmx1024M'
18
+ matrix:
19
+ allow_failures:
20
+ - rvm: ruby-head
21
+ - rvm: jruby-head
22
+ include:
23
+ - rvm: jruby-head
24
+ before_install: gem install bundler --no-ri --no-rdoc
25
+ notifications:
26
+ email: false
27
+ webhooks:
28
+ urls:
29
+ - https://webhooks.gitter.im/e/19098b4253a72c9796db
30
+ on_success: change # options: [always|never|change] default: always
31
+ on_failure: always # options: [always|never|change] default: always
32
+ on_start: false # default: false
data/Gemfile ADDED
@@ -0,0 +1,22 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'codeclimate-test-reporter', platform: :rbx, require: false
7
+ end
8
+
9
+ group :tools do
10
+ gem 'byebug', platform: :mri
11
+ gem 'mutant'
12
+ gem 'mutant-rspec'
13
+ end
14
+
15
+ group :benchmarks do
16
+ gem 'sqlite3'
17
+ gem 'activerecord'
18
+ gem 'benchmark-ips'
19
+ gem 'virtus'
20
+ gem 'fast_attributes'
21
+ gem 'attrio'
22
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013-2016 Piotr Solnica
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,13 @@
1
+ # dry-struct [![Join the chat at https://gitter.im/dry-rb/chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dry-rb/chat)
2
+
3
+ WIP extraction from dry-types
4
+
5
+ ## Development
6
+
7
+ 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.
8
+
9
+ 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).
10
+
11
+ ## Contributing
12
+
13
+ Bug reports and pull requests are welcome on GitHub at https://github.com/dry-rb/dry-struct.
@@ -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,57 @@
1
+ require 'dry/struct'
2
+ require 'virtus'
3
+ require 'fast_attributes'
4
+ require 'attrio'
5
+ require 'ostruct'
6
+
7
+ require 'benchmark/ips'
8
+
9
+ class VirtusUser
10
+ include Virtus.model
11
+
12
+ attribute :name, String
13
+ attribute :age, Integer
14
+ end
15
+
16
+ class FastUser
17
+ extend FastAttributes
18
+
19
+ define_attributes initialize: true, attributes: true do
20
+ attribute :name, String
21
+ attribute :age, Integer
22
+ end
23
+ end
24
+
25
+ class AttrioUser
26
+ include Attrio
27
+
28
+ define_attributes do
29
+ attr :name, String
30
+ attr :age, Integer
31
+ end
32
+
33
+ def initialize(attributes = {})
34
+ self.attributes = attributes
35
+ end
36
+
37
+ def attributes=(attributes = {})
38
+ attributes.each do |attr,value|
39
+ self.send("#{attr}=", value) if self.respond_to?("#{attr}=")
40
+ end
41
+ end
42
+ end
43
+
44
+ class DryStructUser < Dry::Struct
45
+ attributes(name: 'string', age: 'form.int')
46
+ end
47
+
48
+ puts DryStructUser.new(name: 'Jane', age: '21').inspect
49
+
50
+ Benchmark.ips do |x|
51
+ x.report('virtus') { VirtusUser.new(name: 'Jane', age: '21') }
52
+ x.report('fast_attributes') { FastUser.new(name: 'Jane', age: '21') }
53
+ x.report('attrio') { AttrioUser.new(name: 'Jane', age: '21') }
54
+ x.report('dry-struct') { DryStructUser.new(name: 'Jane', age: '21') }
55
+
56
+ x.compare!
57
+ end
@@ -0,0 +1,37 @@
1
+ require 'dry/struct'
2
+
3
+ require 'active_record'
4
+ require 'benchmark/ips'
5
+
6
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
7
+
8
+ ActiveRecord::Schema.define do
9
+ create_table :users do |table|
10
+ table.column :name, :string
11
+ table.column :age, :integer
12
+ end
13
+ end
14
+
15
+ class ARUser < ActiveRecord::Base
16
+ self.table_name = :users
17
+ end
18
+
19
+ module Types
20
+ include Dry::Types.module
21
+ end
22
+
23
+ class DryStructUser < Dry::Struct
24
+ attribute :id, Types::Form::Int
25
+ attribute :name, Types::Strict::String.constrained(size: 3..64)
26
+ attribute :age, Types::Form::Int.constrained(gt: 18)
27
+ end
28
+
29
+ puts ARUser.new(id: 1, name: 'Jane', age: '21').inspect
30
+ puts DryStructUser.new(id: 1, name: 'Jane', age: '21').inspect
31
+
32
+ Benchmark.ips do |x|
33
+ x.report('active record') { ARUser.new(id: 1, name: 'Jane', age: '21') }
34
+ x.report('dry-struct') { DryStructUser.new(id: 1, name: 'Jane', age: '21') }
35
+
36
+ x.compare!
37
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'dry/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
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'dry-struct'
5
+ spec.version = '0.0.1'
6
+ spec.authors = ['Piotr Solnica']
7
+ spec.email = ['piotr.solnica@gmail.com']
8
+ spec.license = 'MIT'
9
+
10
+ spec.summary = 'Typed structs and value objects.'
11
+ spec.description = spec.summary
12
+ spec.homepage = 'https://github.com/dryrb/dry-struct'
13
+
14
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
15
+ # delete this section to allow pushing this gem to any host.
16
+ if spec.respond_to?(:metadata)
17
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
18
+ else
19
+ raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
20
+ end
21
+
22
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ spec.bindir = 'exe'
24
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
+ spec.require_paths = ['lib']
26
+
27
+ spec.add_runtime_dependency 'dry-equalizer', '~> 0.2'
28
+ spec.add_runtime_dependency 'dry-configurable', '~> 0.1'
29
+ spec.add_runtime_dependency 'dry-logic', '~> 0.2', '>= 0.2.3'
30
+ spec.add_runtime_dependency 'dry-types', '~> 0.8', '>= 0.8.1'
31
+ spec.add_runtime_dependency 'ice_nine', '~> 0.11'
32
+
33
+ spec.add_development_dependency 'bundler', '~> 1.6'
34
+ spec.add_development_dependency 'rake', '~> 11.0'
35
+ spec.add_development_dependency 'rspec', '~> 3.3'
36
+ end
@@ -0,0 +1 @@
1
+ require 'dry/struct'
@@ -0,0 +1,28 @@
1
+ require 'dry-types'
2
+
3
+ require 'dry/struct/version'
4
+ require 'dry/struct/errors'
5
+ require 'dry/struct/class_interface'
6
+ require 'dry/struct/hashify'
7
+ require 'dry/struct/value'
8
+
9
+ module Dry
10
+ class Struct
11
+ extend ClassInterface
12
+
13
+ def initialize(attributes)
14
+ attributes.each { |key, value| instance_variable_set("@#{key}", value) }
15
+ end
16
+
17
+ def [](name)
18
+ public_send(name)
19
+ end
20
+
21
+ def to_hash
22
+ self.class.schema.keys.each_with_object({}) do |key, result|
23
+ result[key] = Hashify[self[key]]
24
+ end
25
+ end
26
+ alias_method :to_h, :to_hash
27
+ end
28
+ end
@@ -0,0 +1,105 @@
1
+ module Dry
2
+ class Struct
3
+ module ClassInterface
4
+ include Dry::Types::Builder
5
+
6
+ attr_accessor :constructor
7
+
8
+ attr_accessor :equalizer
9
+
10
+ attr_writer :constructor_type
11
+
12
+ protected :constructor=, :equalizer=, :constructor_type=
13
+
14
+ def inherited(klass)
15
+ super
16
+
17
+ klass.equalizer = Equalizer.new(*schema.keys)
18
+ klass.constructor_type = constructor_type
19
+ klass.send(:include, klass.equalizer)
20
+
21
+ unless klass == Value
22
+ klass.constructor = Types['coercible.hash']
23
+ Types.register(Types.identifier(klass), klass)
24
+ end
25
+
26
+ klass.attributes({}) unless equal?(Struct)
27
+ end
28
+
29
+ def attribute(name, type)
30
+ attributes(name => type)
31
+ end
32
+
33
+ def attributes(new_schema)
34
+ check_schema_duplication(new_schema)
35
+
36
+ prev_schema = schema
37
+
38
+ @schema = prev_schema.merge(new_schema)
39
+ @constructor = Types['coercible.hash'].public_send(constructor_type, schema)
40
+
41
+ attr_reader(*new_schema.keys)
42
+ equalizer.instance_variable_get('@keys').concat(new_schema.keys)
43
+
44
+ self
45
+ end
46
+
47
+ def check_schema_duplication(new_schema)
48
+ shared_keys = new_schema.keys & schema.keys
49
+
50
+ fail Types::RepeatedAttributeError, shared_keys.first if shared_keys.any?
51
+ end
52
+ private :check_schema_duplication
53
+
54
+ def constructor_type(type = nil)
55
+ if type
56
+ @constructor_type = type
57
+ else
58
+ @constructor_type || :strict
59
+ end
60
+ end
61
+
62
+ def schema
63
+ super_schema = superclass.respond_to?(:schema) ? superclass.schema : {}
64
+ super_schema.merge(@schema || {})
65
+ end
66
+
67
+ def new(attributes = default_attributes)
68
+ if attributes.instance_of?(self)
69
+ attributes
70
+ else
71
+ super(constructor[attributes])
72
+ end
73
+ rescue Types::SchemaError, Types::SchemaKeyError => error
74
+ raise Struct::Error, "[#{self}.new] #{error}"
75
+ end
76
+ alias_method :call, :new
77
+ alias_method :[], :new
78
+
79
+ def default_attributes
80
+ schema.each_with_object({}) { |(name, type), result|
81
+ result[name] = type.default? ? type.evaluate : type[nil]
82
+ }
83
+ end
84
+
85
+ def try(input)
86
+ Types::Result::Success.new(self[input])
87
+ rescue Struct::Error => e
88
+ failure = Types::Result::Failure.new(input, e.message)
89
+ block_given? ? yield(failure) : failure
90
+ end
91
+
92
+ def maybe?
93
+ false
94
+ end
95
+
96
+ def default?
97
+ false
98
+ end
99
+
100
+ def valid?(value)
101
+ self === value
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,15 @@
1
+ module Dry
2
+ class Struct
3
+ extend Dry::Configurable
4
+
5
+ setting :namespace, self
6
+
7
+ Error = Class.new(TypeError)
8
+
9
+ class RepeatedAttributeError < ArgumentError
10
+ def initialize(key)
11
+ super("Attribute :#{key} has already been defined")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # Converts value to hash recursively
2
+
3
+ module Dry
4
+ class Struct
5
+ module Hashify
6
+ def self.[](value)
7
+ if value.respond_to?(:to_hash)
8
+ value.to_hash
9
+ elsif value.respond_to?(:map)
10
+ value.map { |item| self[item] }
11
+ else
12
+ value
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ require 'ice_nine'
2
+
3
+ require 'dry/struct'
4
+
5
+ module Dry
6
+ class Struct
7
+ class Value < self
8
+ def self.new(*)
9
+ IceNine.deep_freeze(super)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module Dry
2
+ class Struct
3
+ VERSION = '0.0.1'.freeze
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,188 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dry-struct
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Piotr Solnica
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-07-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dry-equalizer
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dry-configurable
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: dry-logic
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.2'
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 0.2.3
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - "~>"
56
+ - !ruby/object:Gem::Version
57
+ version: '0.2'
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 0.2.3
61
+ - !ruby/object:Gem::Dependency
62
+ name: dry-types
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.8'
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: 0.8.1
71
+ type: :runtime
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '0.8'
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: 0.8.1
81
+ - !ruby/object:Gem::Dependency
82
+ name: ice_nine
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '0.11'
88
+ type: :runtime
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '0.11'
95
+ - !ruby/object:Gem::Dependency
96
+ name: bundler
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '1.6'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '1.6'
109
+ - !ruby/object:Gem::Dependency
110
+ name: rake
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '11.0'
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - "~>"
121
+ - !ruby/object:Gem::Version
122
+ version: '11.0'
123
+ - !ruby/object:Gem::Dependency
124
+ name: rspec
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '3.3'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "~>"
135
+ - !ruby/object:Gem::Version
136
+ version: '3.3'
137
+ description: Typed structs and value objects.
138
+ email:
139
+ - piotr.solnica@gmail.com
140
+ executables: []
141
+ extensions: []
142
+ extra_rdoc_files: []
143
+ files:
144
+ - ".gitignore"
145
+ - ".rspec"
146
+ - ".travis.yml"
147
+ - Gemfile
148
+ - LICENSE
149
+ - README.md
150
+ - Rakefile
151
+ - benchmarks/basic.rb
152
+ - benchmarks/constrained.rb
153
+ - bin/console
154
+ - bin/setup
155
+ - dry-struct.gemspec
156
+ - lib/dry-struct.rb
157
+ - lib/dry/struct.rb
158
+ - lib/dry/struct/class_interface.rb
159
+ - lib/dry/struct/errors.rb
160
+ - lib/dry/struct/hashify.rb
161
+ - lib/dry/struct/value.rb
162
+ - lib/dry/struct/version.rb
163
+ homepage: https://github.com/dryrb/dry-struct
164
+ licenses:
165
+ - MIT
166
+ metadata:
167
+ allowed_push_host: https://rubygems.org
168
+ post_install_message:
169
+ rdoc_options: []
170
+ require_paths:
171
+ - lib
172
+ required_ruby_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: '0'
177
+ required_rubygems_version: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ requirements: []
183
+ rubyforge_project:
184
+ rubygems_version: 2.6.3
185
+ signing_key:
186
+ specification_version: 4
187
+ summary: Typed structs and value objects.
188
+ test_files: []