reasonable-value 0.2.1

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: 53d73215512ebc2cc1a5e62547ba69d63fd93a12
4
+ data.tar.gz: 9a9973e5f2791acb93601dc0051a2020e4680d8f
5
+ SHA512:
6
+ metadata.gz: 318aa3ca1bc5fafe0a3c0feeee442007066b2fd562294b90cad0893c9e04698ec1208cf1d2eaf61d212c92bdba91564c55f18741ef86d513faf37a28ba1087e9
7
+ data.tar.gz: 98c79f526df7c612bcbe06e89952caf2a437965c90b9c31354fab1de8055b33edf1c747ce977715ed1057b6d2ac412270b065ebda843bf065facffe95c994b70
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --order random
3
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,85 @@
1
+ AllCops:
2
+ # Use Ruby 2.3 parser
3
+ TargetRubyVersion: 2.3
4
+
5
+ Style/DotPosition:
6
+ EnforcedStyle: trailing
7
+
8
+ ClassLength:
9
+ Max: 150
10
+
11
+ Metrics/LineLength:
12
+ Max: 100
13
+
14
+ Metrics/AbcSize:
15
+ Max: 18
16
+
17
+ Metrics/MethodLength:
18
+ Max: 15
19
+
20
+ Metrics/BlockLength:
21
+ CountComments: false
22
+ Max: 25
23
+ Exclude:
24
+ - 'spec/**/*'
25
+
26
+ Style/AlignParameters:
27
+ EnforcedStyle: with_first_parameter
28
+
29
+ Style/Documentation:
30
+ Enabled: false
31
+
32
+ Style/ExtraSpacing:
33
+ # When true, allows most uses of extra spacing if the intent is to align
34
+ # things with the previous or next line, not counting empty lines or comment
35
+ # lines.
36
+ AllowForAlignment: true
37
+
38
+ Style/LambdaCall:
39
+ Enabled: true
40
+ EnforcedStyle: braces
41
+
42
+ Style/StringLiterals:
43
+ EnforcedStyle: single_quotes
44
+
45
+ Style/EmptyLinesAroundClassBody:
46
+ EnforcedStyle: empty_lines
47
+ SupportedStyles:
48
+ - empty_lines
49
+ - no_empty_lines
50
+
51
+ Style/EmptyLinesAroundModuleBody:
52
+ EnforcedStyle: no_empty_lines
53
+ SupportedStyles:
54
+ - empty_lines
55
+ - no_empty_lines
56
+
57
+ Style/EmptyLinesAroundBlockBody:
58
+ Description: "Keeps track of empty lines around block bodies."
59
+ Enabled: false
60
+
61
+ Style/EmptyLines:
62
+ Description: "Don't use several empty lines in a row."
63
+ Enabled: true
64
+
65
+ Style/EmptyCaseCondition:
66
+ Enabled: false
67
+
68
+ Style/EndOfLine:
69
+ Description: 'Use Unix-style line endings.'
70
+ StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#crlf'
71
+ Enabled: true
72
+
73
+ Style/MultilineOperationIndentation:
74
+ EnforcedStyle: indented
75
+
76
+ Style/MultilineMethodCallIndentation:
77
+ EnforcedStyle: indented
78
+
79
+ Style/WordArray:
80
+ Description: 'Use %w or %W for arrays of words.'
81
+ StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-w'
82
+ Enabled: false
83
+
84
+ Style/FrozenStringLiteralComment:
85
+ Enabled: false
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.4.1
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.1
5
+ before_install: gem install bundler -v 1.14.6
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ ruby '2.4.1'
4
+
5
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Thomas Larrieu
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,132 @@
1
+ # Reasonable::Value
2
+
3
+ Reasonable::Value is a value object implementation in its smallest possible
4
+ form.
5
+
6
+ ## Why another implementation ?
7
+
8
+ ### Virtus
9
+ [Virtus](https://github.com/solnic/virtus) does too many things and is
10
+ deprecated in favor of [dry-struct](https://github.com/dry-rb/dry-struct)
11
+
12
+ ### dry-struct
13
+ I felt that [dry-struct](https://github.com/dry-rb/dry-struct) was
14
+ 1. Not documented enough
15
+ 2. Did not properly handle "truly" optional attributes
16
+
17
+ ## Installation
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem 'reasonable-value'
23
+ ```
24
+
25
+ And then execute:
26
+
27
+ $ bundle
28
+
29
+ Or install it yourself as:
30
+
31
+ $ gem install reasonable-value
32
+
33
+ ## Usage
34
+
35
+ By default attributes are mandatory, but corcible (meaning that passing a Float
36
+ when an Integer is expected will **not** raise an error:
37
+ ``` ruby
38
+ class StandardValue < Reasonable::Value
39
+
40
+ attribute :integer, Integer
41
+
42
+ end
43
+
44
+ p StandardValue.new
45
+ # => TypeError: expected :integer to be a Integer but was a NilClass
46
+
47
+ p StandardValue.new(integer: nil)
48
+ # => TypeError: expected :integer to be a Integer but was a NilClass
49
+
50
+ p StandardValue.new(integer: 1)
51
+ # => #<StandardValue:0x007f65ec156720 @attributes={:integer=>1}>
52
+
53
+ p StandardValue.new(integer: 1.1)
54
+ # => #<StandardValue:0x007f65ec166738 @attributes={:integer=>1}>
55
+ ```
56
+
57
+ If you want optional attributes, you can say so like that:
58
+ ``` ruby
59
+ class OptionalValue < Reasonable::Value
60
+
61
+ attribute :string, String, optional: true
62
+
63
+ end
64
+
65
+ p OptionalValue.new
66
+ # => #<OptionalValue:0x0055ecec2ae2c8 @attributes={}>
67
+
68
+ p OptionalValue.new(string: nil)
69
+ # => #<OptionalValue:0x007f65ec16c430 @attributes={}>
70
+
71
+ p OptionalValue.new(string: 'string')
72
+ # => #<OptionalValue:0x007f65ec174f18 @attributes={:string=>"string"}>
73
+
74
+ p OptionalValue.new(string: 1.1)
75
+ # => #<OptionalValue:0x007f65ec1792e8 @attributes={:string=>"1.1"}>
76
+ ```
77
+
78
+ You are not limited to Integer or String, you can use any type you want:
79
+ ``` ruby
80
+ class ValueWithCustomType < Reasonable::Value
81
+
82
+ attribute :custom, StandardValue
83
+
84
+ end
85
+
86
+ p ValueWithCustomType.new(custom: StandardValue.new(integer: 1))
87
+ # => #<ValueWithCustomType:0x007f65ec18d540 @attributes={:custom=>#<StandardValue:0x007f65ec18d6a8 @attributes={:integer=>1}>}>
88
+
89
+ p ValueWithCustomType.new(custom: { integer: 1 })
90
+ # => #<ValueWithCustomType:0x007f65ec19f920 @attributes={:custom=>#<StandardValue:0x007f65ec19f358 @attributes={:integer=>1}>}>
91
+ ```
92
+
93
+ If you define the appropriate method on the class of the attribute,
94
+ Reasonable::Value will handle casting gracefully:
95
+ ``` ruby
96
+ class CastableType
97
+
98
+ def to_standard_value
99
+ StandardValue.new(integer: 1)
100
+ end
101
+
102
+ end
103
+
104
+ p ValueWithCustomType.new(custom: CastableType.new)
105
+ # => #<ValueWithCustomType:0x007f65ec1a6590 @attributes={:custom=>#<StandardValue:0x007f65ec1a5bb8 @attributes={:integer=>1}>}>
106
+ ```
107
+
108
+ Equality is based on attributes, instead of identity:
109
+
110
+ ``` ruby
111
+ p StandardValue.new(integer: 1) == StandardValue.new(integer: 1)
112
+ # => true
113
+
114
+ p StandardValue.new(integer: 1) == StandardValue.new(integer: 2)
115
+ # => false
116
+ ```
117
+
118
+ ## Development
119
+
120
+ 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.
121
+
122
+ 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).
123
+
124
+ ## Contributing
125
+
126
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/reasonable-value.
127
+
128
+
129
+ ## License
130
+
131
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
132
+
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,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'reasonable/value'
5
+
6
+ require 'irb'
7
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'reasonable/value/version'
4
+ require 'active_support/core_ext/string/inflections'
5
+
6
+ module Reasonable
7
+ class Value
8
+
9
+ include Comparable
10
+ def <=>(other)
11
+ @attributes <=> other.instance_variable_get(:@attributes)
12
+ end
13
+
14
+ def initialize(**attributes)
15
+ @attributes = {}
16
+
17
+ self.class.send(:config).each do |name, config|
18
+ next if attributes[name].nil? && config[:options][:optional]
19
+
20
+ @attributes[name] = coerce(attributes, name, config)
21
+ end
22
+ end
23
+
24
+ class << self
25
+
26
+ def inherited(subklass)
27
+ config.each do |name, config|
28
+ subklass.attribute(name, config[:type], options: config[:options])
29
+ end
30
+ end
31
+
32
+ protected
33
+
34
+ def attribute(name, type, **options)
35
+ mutex.synchronize { config[name] = { type: type, options: options } }
36
+
37
+ define_method(name) { @attributes[name] }
38
+ end
39
+
40
+ private
41
+
42
+ def mutex
43
+ return @mutex if defined?(@mutex)
44
+
45
+ @mutex = Thread::Mutex.new
46
+ end
47
+
48
+ def config
49
+ return @config if defined?(@config)
50
+
51
+ @config = {}
52
+ end
53
+
54
+ end
55
+
56
+ private
57
+
58
+ def coerce(attributes, name, config)
59
+ Coercer.(config[:type], attributes[name])
60
+ rescue TypeError
61
+ type_error(name, config[:type], attributes[name].class)
62
+ end
63
+
64
+ def type_error(name, expected, actual)
65
+ raise(
66
+ TypeError,
67
+ "expected :#{name} to be a #{expected} but was a #{actual}"
68
+ )
69
+ end
70
+
71
+ class Coercer
72
+
73
+ class << self
74
+
75
+ def call(type, value)
76
+ built_in(type, value) || custom(type, value)
77
+ end
78
+
79
+ private
80
+
81
+ def built_in(type, value)
82
+ return unless Kernel.respond_to?(type.to_s)
83
+
84
+ Kernel.public_send(type.to_s, value)
85
+ end
86
+
87
+ def custom(type, value)
88
+ return value if value.is_a?(type)
89
+
90
+ if value.respond_to?("to_#{type.to_s.underscore}")
91
+ return value.public_send("to_#{type.to_s.underscore}")
92
+ end
93
+
94
+ raise TypeError unless value.is_a?(Hash)
95
+ raise TypeError unless type.ancestors.include?(Reasonable::Value)
96
+
97
+ type.new(value)
98
+ end
99
+
100
+ end
101
+
102
+ end
103
+ private_constant :Coercer
104
+
105
+ end
106
+ end
@@ -0,0 +1,7 @@
1
+ module Reasonable
2
+ class Value
3
+
4
+ VERSION = '0.2.1'.freeze
5
+
6
+ end
7
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'reasonable/value/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'reasonable-value'
9
+ spec.version = Reasonable::Value::VERSION
10
+ spec.authors = ['Thomas Larrieu']
11
+ spec.email = ['thomas.larrieu@gmail.com']
12
+
13
+ spec.summary = 'Simple value object gem with straighforward type validation'
14
+ spec.homepage = 'https://www.github.com/jobteaser/reasonable-value'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = 'exe'
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = %w(lib)
23
+
24
+ spec.add_dependency 'activesupport'
25
+
26
+ spec.add_development_dependency 'bundler', '~> 1.14'
27
+ spec.add_development_dependency 'rake', '~> 10.0'
28
+ spec.add_development_dependency 'rspec', '~> 3.0'
29
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: reasonable-value
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Thomas Larrieu
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-04-03 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: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.14'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.14'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description:
70
+ email:
71
+ - thomas.larrieu@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".rubocop.yml"
79
+ - ".ruby-version"
80
+ - ".travis.yml"
81
+ - Gemfile
82
+ - LICENSE.txt
83
+ - README.md
84
+ - Rakefile
85
+ - bin/console
86
+ - bin/setup
87
+ - lib/reasonable/value.rb
88
+ - lib/reasonable/value/version.rb
89
+ - reasonable-value.gemspec
90
+ homepage: https://www.github.com/jobteaser/reasonable-value
91
+ licenses:
92
+ - MIT
93
+ metadata: {}
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 2.6.11
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: Simple value object gem with straighforward type validation
114
+ test_files: []