extant 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +80 -0
- data/Rakefile +9 -0
- data/extant.gemspec +24 -0
- data/lib/extant.rb +8 -0
- data/lib/extant/attribute.rb +78 -0
- data/lib/extant/attribute_definition.rb +28 -0
- data/lib/extant/attributes.rb +130 -0
- data/lib/extant/coercers.rb +47 -0
- data/lib/extant/coercers/array.rb +56 -0
- data/lib/extant/coercers/base.rb +27 -0
- data/lib/extant/coercers/boolean.rb +15 -0
- data/lib/extant/coercers/date_time.rb +16 -0
- data/lib/extant/coercers/fixnum.rb +11 -0
- data/lib/extant/coercers/float.rb +11 -0
- data/lib/extant/coercers/hash.rb +63 -0
- data/lib/extant/coercers/integer.rb +11 -0
- data/lib/extant/coercers/string.rb +10 -0
- data/lib/extant/coercers/symbol.rb +20 -0
- data/lib/extant/coercers/time.rb +18 -0
- data/lib/extant/coercers/uuid.rb +14 -0
- data/lib/extant/uncoerced_value.rb +7 -0
- data/lib/extant/unset_value.rb +7 -0
- data/lib/extant/version.rb +3 -0
- metadata +144 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 59966f18b3d32352d1ab9700a02a424c1dc812f7
|
4
|
+
data.tar.gz: 5b8021c55eef67a6643fd7c50fdf6828953f814c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bc0226bf520414c661d44cfd55a6398553fdc72c1e57144c50317492b4e554eca5bd920a1da12ae5c9a4bf72f327d4f60d4fa12432dd47c289ff05e744368443
|
7
|
+
data.tar.gz: 198d0d6440bb0c89aa66957a1d836c4edc0c82901a56c328b0683fab6083933979509da904cdc7dc5c5c587de299a38fa2aa7632f89685f0cbfe60e458a4458f
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 NCC Group Domain Services
|
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,80 @@
|
|
1
|
+
# Extant
|
2
|
+
|
3
|
+
_Sponsored by NCC Group Domain Services_
|
4
|
+
|
5
|
+
Extant provides attribute management for Ruby objects. It is somewhat similar to
|
6
|
+
`attr_accessor`, except it does many additional things like, type coercion,
|
7
|
+
change tracking (dirty attributes), and most importantly tracking of existence.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
``` terminal
|
12
|
+
$ gem install extant
|
13
|
+
```
|
14
|
+
|
15
|
+
or in your **Gemfile**
|
16
|
+
|
17
|
+
``` ruby
|
18
|
+
gem 'extant'
|
19
|
+
```
|
20
|
+
|
21
|
+
## Getting Started
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
class Widget
|
25
|
+
include Extant::Attributes
|
26
|
+
|
27
|
+
attribute :name, String
|
28
|
+
attribute :price, Integer
|
29
|
+
attribute :production_ready, Extant::Coercers::Boolean, default: false
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
Basic Usage
|
34
|
+
```ruby
|
35
|
+
widget = Widget.new(
|
36
|
+
name: 'Doodad',
|
37
|
+
price: 100
|
38
|
+
)
|
39
|
+
|
40
|
+
widget.name # => 'Doodad'
|
41
|
+
widget.price # => 100
|
42
|
+
widget.production_ready # => false
|
43
|
+
|
44
|
+
widget.attributes # => { name: 'Doodad', price: 100, production_ready: false }
|
45
|
+
```
|
46
|
+
|
47
|
+
Coercion
|
48
|
+
```ruby
|
49
|
+
widget = Widget.new(
|
50
|
+
price: 'not a number'
|
51
|
+
)
|
52
|
+
|
53
|
+
widget.extant_attributes[:price].coerced? # => false
|
54
|
+
widget.price # => nil
|
55
|
+
widget.extant_attributes[:price].pre_coerce_value # => 'not a number'
|
56
|
+
```
|
57
|
+
|
58
|
+
Existence
|
59
|
+
```ruby
|
60
|
+
widget = Widget.new
|
61
|
+
|
62
|
+
widget.name # => nil
|
63
|
+
widget.extant_attributes[:name].set? # => false
|
64
|
+
|
65
|
+
widget = Widget.new(
|
66
|
+
name: nil
|
67
|
+
)
|
68
|
+
|
69
|
+
widget.name # => nil
|
70
|
+
widget.extant_attributes[:name].set? # => true
|
71
|
+
widget.extant_attributes[:name].coerced? # => true
|
72
|
+
```
|
73
|
+
|
74
|
+
## Contributing
|
75
|
+
|
76
|
+
1. Fork
|
77
|
+
2. Make changes
|
78
|
+
3. Pull request
|
79
|
+
4. Celebrate
|
80
|
+
|
data/Rakefile
ADDED
data/extant.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'extant/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "extant"
|
8
|
+
spec.version = Extant::VERSION
|
9
|
+
spec.authors = ["NCC Group Domain Services"]
|
10
|
+
spec.email = ["brady.love@nccgroup.trust", "emily.dobervich@nccgroup.trust"]
|
11
|
+
spec.licenses = ['MIT']
|
12
|
+
|
13
|
+
spec.summary = %q{Extant, it helps you manage your attributes' existance.}
|
14
|
+
spec.description = %q{Define attributes, readers and writers on classes.}
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.add_development_dependency "bundler", "~> 1.9"
|
20
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
21
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
22
|
+
spec.add_development_dependency "pry-nav"
|
23
|
+
spec.add_development_dependency "pry-doc"
|
24
|
+
end
|
data/lib/extant.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
module Extant
|
2
|
+
class Attribute
|
3
|
+
attr_reader :name, :coercer_class, :pre_coerce_value, :value
|
4
|
+
|
5
|
+
def initialize(name, value, definition)
|
6
|
+
self.name = name
|
7
|
+
self.has_been_coereced = false
|
8
|
+
self.definition = definition
|
9
|
+
|
10
|
+
if UnsetValue == value
|
11
|
+
@value = nil
|
12
|
+
self.is_unset = true
|
13
|
+
else
|
14
|
+
set_value(value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def value=(val)
|
19
|
+
set_value(val)
|
20
|
+
self.value_has_changed = true
|
21
|
+
end
|
22
|
+
|
23
|
+
def coerced?
|
24
|
+
!!has_been_coereced
|
25
|
+
end
|
26
|
+
|
27
|
+
def has_changed?
|
28
|
+
!!value_has_changed
|
29
|
+
end
|
30
|
+
|
31
|
+
def unset?
|
32
|
+
!set?
|
33
|
+
end
|
34
|
+
|
35
|
+
def set?
|
36
|
+
!is_unset
|
37
|
+
end
|
38
|
+
|
39
|
+
def coercer_name
|
40
|
+
coercer_class.try(:coercer_name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def inspect
|
44
|
+
"<Extant::Attribute:#{self.object_id} Name: :#{name} Value: #{value.inspect}>"
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def coercer_class
|
50
|
+
definition.coercer_class
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_value(val)
|
54
|
+
self.pre_coerce_value = val
|
55
|
+
|
56
|
+
@value = if coercer_class
|
57
|
+
coercer = coercer_class.new(val)
|
58
|
+
coercer.coerce.tap { self.has_been_coereced = coercer.coerced? }
|
59
|
+
else
|
60
|
+
self.has_been_coereced = true
|
61
|
+
val
|
62
|
+
end
|
63
|
+
|
64
|
+
if UncoercedValue == @value
|
65
|
+
@value = nil
|
66
|
+
|
67
|
+
if definition.allow_nil && pre_coerce_value == nil
|
68
|
+
self.has_been_coereced = true
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
self.is_unset = false
|
73
|
+
end
|
74
|
+
|
75
|
+
attr_accessor :value_has_changed, :has_been_coereced, :is_unset, :definition
|
76
|
+
attr_writer :name, :coercer_class, :pre_coerce_value
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Extant
|
2
|
+
class AttributeDefinition
|
3
|
+
attr_accessor :name, :type, :coercer_class, :has_default, :default_value,
|
4
|
+
:allow_nil
|
5
|
+
|
6
|
+
def initialize(model, name, opts={})
|
7
|
+
self.name = name
|
8
|
+
self.type = opts[:type]
|
9
|
+
self.has_default = opts.key?(:default)
|
10
|
+
self.default_value = opts[:default]
|
11
|
+
self.allow_nil = opts.fetch(:allow_nil, true)
|
12
|
+
|
13
|
+
if type
|
14
|
+
self.coercer_class = Extant::Coercers.find(type)
|
15
|
+
end
|
16
|
+
|
17
|
+
model.instance_eval do
|
18
|
+
define_method name do
|
19
|
+
extant_attributes[name].value
|
20
|
+
end
|
21
|
+
|
22
|
+
define_method "#{name}=" do |val|
|
23
|
+
extant_attributes[name].value = val
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'extant/attribute'
|
2
|
+
require 'extant/attribute_definition'
|
3
|
+
|
4
|
+
module Extant
|
5
|
+
module Attributes
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def attribute_definitions
|
12
|
+
@attributes ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def attribute(name, type=nil, **opts)
|
16
|
+
opts[:type] = type
|
17
|
+
attribute_definitions[name] = AttributeDefinition.new(self, name, opts)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(attrs={})
|
22
|
+
attrs = Hash[attrs.map{ |k, v| [k.to_sym, v] }]
|
23
|
+
attribute_definitions.each do |k, definition|
|
24
|
+
value = if attrs.key?(k)
|
25
|
+
attrs[k]
|
26
|
+
else
|
27
|
+
UnsetValue
|
28
|
+
end
|
29
|
+
|
30
|
+
if UnsetValue == value && definition.has_default
|
31
|
+
value = definition.default_value.is_a?(Proc) ? definition.default_value.call : definition.default_value
|
32
|
+
end
|
33
|
+
|
34
|
+
extant_attributes[k] =
|
35
|
+
Extant::Attribute.new(k, value, definition)
|
36
|
+
end
|
37
|
+
|
38
|
+
self.original_extant_attributes = extant_attributes
|
39
|
+
end
|
40
|
+
|
41
|
+
def attributes
|
42
|
+
self.class.attribute_definitions.each_with_object({}) do |(k, _), attrs|
|
43
|
+
attrs[k] = send(k)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def attribute_definitions
|
48
|
+
self.class.attribute_definitions
|
49
|
+
end
|
50
|
+
|
51
|
+
def extant_attributes
|
52
|
+
@extant_attributes ||= {}
|
53
|
+
end
|
54
|
+
|
55
|
+
def set_attributes
|
56
|
+
self.class.attribute_definitions.each_with_object({}) do |(k, _), attrs|
|
57
|
+
if extant_attributes[k].set?
|
58
|
+
attrs[k] = send(k)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def changed_attributes
|
64
|
+
self.class.attribute_definitions.each_with_object({}) do |(k, _), attrs|
|
65
|
+
if extant_attributes[k].has_changed?
|
66
|
+
attrs[k] = send(k)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def each_extant_attribute
|
72
|
+
extant_attributes.values.each do |v|
|
73
|
+
yield v
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def update_attributes(attrs={})
|
78
|
+
attrs.each do |k, v|
|
79
|
+
send("#{k}=".to_sym, v) if respond_to?("#{k}=".to_sym)
|
80
|
+
end
|
81
|
+
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
def dirty?(attr=nil)
|
86
|
+
if attr
|
87
|
+
!!changed_extant_attributes[attr]
|
88
|
+
else
|
89
|
+
changed_extant_attributes.any?
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def original_extant_attributes
|
94
|
+
@original_extant_attributes ||= {}
|
95
|
+
end
|
96
|
+
|
97
|
+
def with(attrs={}, metadata={})
|
98
|
+
new_instance = self.class.new(self.attributes)
|
99
|
+
new_instance.update_attributes(attrs)
|
100
|
+
|
101
|
+
new_instance.send(:with_metadata=, metadata)
|
102
|
+
new_instance.send(:original_extant_attributes=, original_extant_attributes)
|
103
|
+
new_instance
|
104
|
+
end
|
105
|
+
|
106
|
+
def with_metadata
|
107
|
+
@with_metadata || {}
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
attr_writer :with_metadata
|
113
|
+
|
114
|
+
def changed_extant_attributes
|
115
|
+
self.class.attribute_definitions.each_with_object({}) do |(k, _), attrs|
|
116
|
+
if extant_attributes[k].has_changed?
|
117
|
+
attrs[k] = extant_attributes[k]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def original_extant_attributes=(attrs={})
|
123
|
+
attrs.each do |k, v|
|
124
|
+
original_extant_attributes[k] = v.dup
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
require 'extant/coercers'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Extant
|
2
|
+
module Coercers
|
3
|
+
def self.find(type)
|
4
|
+
if type.is_a?(::Hash)
|
5
|
+
coercer_options = {
|
6
|
+
hash_format: type
|
7
|
+
}
|
8
|
+
|
9
|
+
type = HashCoercerBuilder.build(coercer_options)
|
10
|
+
end
|
11
|
+
|
12
|
+
if type.is_a?(::Array)
|
13
|
+
coercer_options = {
|
14
|
+
array_format: type
|
15
|
+
}
|
16
|
+
|
17
|
+
type = ArrayCoercerBuilder.build(coercer_options)
|
18
|
+
end
|
19
|
+
|
20
|
+
if type.is_a?(Class) && type.ancestors.include?(Extant::Coercers::Base)
|
21
|
+
coercer_class = type
|
22
|
+
else
|
23
|
+
coercer_class = Object.const_get("Extant::Coercers::#{type}")
|
24
|
+
|
25
|
+
unless coercer_class.method_defined?(:coerce)
|
26
|
+
raise "#{type} must respond to #coerce"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
coercer_class
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'extant/coercers/base'
|
36
|
+
|
37
|
+
require 'extant/coercers/boolean'
|
38
|
+
require 'extant/coercers/date_time'
|
39
|
+
require 'extant/coercers/fixnum'
|
40
|
+
require 'extant/coercers/float'
|
41
|
+
require 'extant/coercers/hash'
|
42
|
+
require 'extant/coercers/array'
|
43
|
+
require 'extant/coercers/integer'
|
44
|
+
require 'extant/coercers/string'
|
45
|
+
require 'extant/coercers/symbol'
|
46
|
+
require 'extant/coercers/time'
|
47
|
+
require 'extant/coercers/uuid'
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Extant::Coercers
|
2
|
+
class Array < Base
|
3
|
+
class << self
|
4
|
+
attr_accessor :value_type
|
5
|
+
end
|
6
|
+
|
7
|
+
def coerce
|
8
|
+
return ArgumentError unless value.respond_to?(:to_a)
|
9
|
+
result = value.to_a
|
10
|
+
return ArgumentError unless result.is_a?(::Array)
|
11
|
+
|
12
|
+
if value_type
|
13
|
+
result = result.map do |v|
|
14
|
+
coerce_value(v)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
self.coerced = true
|
19
|
+
|
20
|
+
result
|
21
|
+
rescue ArgumentError
|
22
|
+
UncoercedValue
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def coerce_value(v)
|
28
|
+
coercer = value_type.new(v)
|
29
|
+
result = coercer.coerce
|
30
|
+
raise ArgumentError unless coercer.coerced?
|
31
|
+
result
|
32
|
+
end
|
33
|
+
|
34
|
+
def key_type
|
35
|
+
self.class.key_type
|
36
|
+
end
|
37
|
+
|
38
|
+
def value_type
|
39
|
+
self.class.value_type
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class ArrayCoercerBuilder
|
44
|
+
def self.build(opts)
|
45
|
+
value_type_local = Extant::Coercers.find(opts[:array_format].first)
|
46
|
+
|
47
|
+
Class.new(Extant::Coercers::Array) do
|
48
|
+
self.value_type = value_type_local
|
49
|
+
|
50
|
+
def self.coercer_name
|
51
|
+
'array'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Extant::Coercers
|
2
|
+
class Base
|
3
|
+
UncoercedValue = Extant::UncoercedValue
|
4
|
+
|
5
|
+
def self.coercer_name
|
6
|
+
self.name.split("::").last.gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(value, options={})
|
10
|
+
self.value = value
|
11
|
+
self.coerced = false
|
12
|
+
self.options = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def coerce
|
16
|
+
rase NotImplementedError.new("#{self.class.name} must implement #{coerce}")
|
17
|
+
end
|
18
|
+
|
19
|
+
def coerced?
|
20
|
+
coerced
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_accessor :value, :coerced, :options
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Extant::Coercers
|
2
|
+
class Boolean < Base
|
3
|
+
def coerce
|
4
|
+
if [true, "true", 1].include?(value)
|
5
|
+
self.coerced = true
|
6
|
+
return true
|
7
|
+
elsif [false, "false", 0].include?(value)
|
8
|
+
self.coerced = true
|
9
|
+
return false
|
10
|
+
end
|
11
|
+
|
12
|
+
Extant::UncoercedValue
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Extant::Coercers
|
2
|
+
class DateTime < Base
|
3
|
+
def coerce
|
4
|
+
if value.is_a?(::DateTime)
|
5
|
+
self.coerced = true
|
6
|
+
return value
|
7
|
+
end
|
8
|
+
|
9
|
+
result = ::DateTime.parse(value.to_s)
|
10
|
+
self.coerced = true
|
11
|
+
result
|
12
|
+
rescue ArgumentError
|
13
|
+
UncoercedValue
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Extant::Coercers
|
2
|
+
class Hash < Base
|
3
|
+
class << self
|
4
|
+
attr_accessor :key_type, :value_type
|
5
|
+
end
|
6
|
+
|
7
|
+
def coerce
|
8
|
+
result = ::Hash[value]
|
9
|
+
|
10
|
+
if key_type && value_type
|
11
|
+
result = result.each_with_object({}) do |(k, v), h|
|
12
|
+
h[coerce_key(k)] = coerce_value(v)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
self.coerced = true
|
17
|
+
|
18
|
+
result
|
19
|
+
rescue ArgumentError
|
20
|
+
UncoercedValue
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def coerce_key(v)
|
26
|
+
coercer = key_type.new(v)
|
27
|
+
result = coercer.coerce
|
28
|
+
raise ArgumentError unless coercer.coerced?
|
29
|
+
result
|
30
|
+
end
|
31
|
+
|
32
|
+
def coerce_value(v)
|
33
|
+
coercer = value_type.new(v)
|
34
|
+
result = coercer.coerce
|
35
|
+
raise ArgumentError unless coercer.coerced?
|
36
|
+
result
|
37
|
+
end
|
38
|
+
|
39
|
+
def key_type
|
40
|
+
self.class.key_type
|
41
|
+
end
|
42
|
+
|
43
|
+
def value_type
|
44
|
+
self.class.value_type
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class HashCoercerBuilder
|
49
|
+
def self.build(opts)
|
50
|
+
key_type_local = Extant::Coercers.find(opts[:hash_format].keys.first)
|
51
|
+
value_type_local = Extant::Coercers.find(opts[:hash_format].values.first)
|
52
|
+
|
53
|
+
Class.new(Extant::Coercers::Hash) do
|
54
|
+
self.key_type = key_type_local
|
55
|
+
self.value_type = value_type_local
|
56
|
+
|
57
|
+
def self.coercer_name
|
58
|
+
'hash'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Extant::Coercers
|
2
|
+
class Symbol < Base
|
3
|
+
def coerce
|
4
|
+
return UncoercedValue if value == nil
|
5
|
+
|
6
|
+
if value.is_a?(::String)
|
7
|
+
result = value.to_sym
|
8
|
+
elsif value.is_a?(::Symbol)
|
9
|
+
result = value
|
10
|
+
else
|
11
|
+
raise ArgumentError
|
12
|
+
end
|
13
|
+
|
14
|
+
self.coerced = true
|
15
|
+
result
|
16
|
+
rescue ArgumentError
|
17
|
+
UncoercedValue
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module Extant::Coercers
|
4
|
+
class Time < Base
|
5
|
+
def coerce
|
6
|
+
if value.is_a?(::Time)
|
7
|
+
self.coerced = true
|
8
|
+
return value
|
9
|
+
end
|
10
|
+
|
11
|
+
result = ::Time.parse(value.to_s)
|
12
|
+
self.coerced = true
|
13
|
+
result
|
14
|
+
rescue ArgumentError
|
15
|
+
UncoercedValue
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: extant
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- NCC Group Domain Services
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-10-19 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.9'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.9'
|
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
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry-nav
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry-doc
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Define attributes, readers and writers on classes.
|
84
|
+
email:
|
85
|
+
- brady.love@nccgroup.trust
|
86
|
+
- emily.dobervich@nccgroup.trust
|
87
|
+
executables: []
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- ".gitignore"
|
92
|
+
- ".rspec"
|
93
|
+
- ".travis.yml"
|
94
|
+
- Gemfile
|
95
|
+
- LICENSE.txt
|
96
|
+
- README.md
|
97
|
+
- Rakefile
|
98
|
+
- extant.gemspec
|
99
|
+
- lib/extant.rb
|
100
|
+
- lib/extant/attribute.rb
|
101
|
+
- lib/extant/attribute_definition.rb
|
102
|
+
- lib/extant/attributes.rb
|
103
|
+
- lib/extant/coercers.rb
|
104
|
+
- lib/extant/coercers/array.rb
|
105
|
+
- lib/extant/coercers/base.rb
|
106
|
+
- lib/extant/coercers/boolean.rb
|
107
|
+
- lib/extant/coercers/date_time.rb
|
108
|
+
- lib/extant/coercers/fixnum.rb
|
109
|
+
- lib/extant/coercers/float.rb
|
110
|
+
- lib/extant/coercers/hash.rb
|
111
|
+
- lib/extant/coercers/integer.rb
|
112
|
+
- lib/extant/coercers/string.rb
|
113
|
+
- lib/extant/coercers/symbol.rb
|
114
|
+
- lib/extant/coercers/time.rb
|
115
|
+
- lib/extant/coercers/uuid.rb
|
116
|
+
- lib/extant/uncoerced_value.rb
|
117
|
+
- lib/extant/unset_value.rb
|
118
|
+
- lib/extant/version.rb
|
119
|
+
homepage:
|
120
|
+
licenses:
|
121
|
+
- MIT
|
122
|
+
metadata: {}
|
123
|
+
post_install_message:
|
124
|
+
rdoc_options: []
|
125
|
+
require_paths:
|
126
|
+
- lib
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
requirements: []
|
138
|
+
rubyforge_project:
|
139
|
+
rubygems_version: 2.0.14
|
140
|
+
signing_key:
|
141
|
+
specification_version: 4
|
142
|
+
summary: Extant, it helps you manage your attributes' existance.
|
143
|
+
test_files: []
|
144
|
+
has_rdoc:
|