shallow_attributes 0.9.0 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -3
- data/CHANGELOG.md +2 -0
- data/Gemfile +2 -0
- data/README.md +84 -28
- data/Rakefile +1 -1
- data/lib/shallow_attributes/class_methods.rb +21 -9
- data/lib/shallow_attributes/instance_methods.rb +8 -4
- data/lib/shallow_attributes/type.rb +48 -46
- data/lib/shallow_attributes/type/boolean.rb +1 -1
- data/lib/shallow_attributes/type/date_time.rb +1 -1
- data/lib/shallow_attributes/type/float.rb +1 -1
- data/lib/shallow_attributes/type/integer.rb +1 -1
- data/lib/shallow_attributes/type/string.rb +1 -1
- data/lib/shallow_attributes/type/time.rb +1 -1
- data/lib/shallow_attributes/version.rb +1 -1
- data/shallow_attributes.gemspec +0 -1
- metadata +3 -18
- data/bin/console +0 -14
- data/bin/setup +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eae949ec12824705349bf498a92b2f524bd912a0
|
4
|
+
data.tar.gz: 1d3a7b3754419370e910cd6b90978425ff12010b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 109b2b4f4e0f699c1d4ba567c988ddc6da547e7be22b10f063b347f1489fc1f1a6c81325822ca7427ce7bca7dc6f3a7b1c1ecafb2609ab0cf0099023eb4adf8b
|
7
|
+
data.tar.gz: 32b707b41127ab624d0566a6d0fbbb455326c0b36085daacf8a71d0d6d1685d5a3e63c9b9c8deafad65dbb5f01ef44a8305969f0bd513a57837fc5655bf91489
|
data/.travis.yml
CHANGED
@@ -4,11 +4,14 @@ cache: bundler
|
|
4
4
|
before_install: gem install bundler -v 1.11.2
|
5
5
|
rvm:
|
6
6
|
- 2.0.0
|
7
|
-
- 2.1.
|
8
|
-
- 2.2.
|
9
|
-
- 2.3.
|
7
|
+
- 2.1.9
|
8
|
+
- 2.2.5
|
9
|
+
- 2.3.1
|
10
|
+
- 2.4.0
|
10
11
|
- jruby-head
|
11
12
|
- rbx-2
|
13
|
+
- ruby-head
|
12
14
|
matrix:
|
13
15
|
allow_failures:
|
14
16
|
- rvm: rbx-2
|
17
|
+
- rvm: ruby-head
|
data/CHANGELOG.md
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
# ShallowAttributes
|
2
2
|
[![Build Status](https://travis-ci.org/davydovanton/shallow_attributes.svg?branch=master)](https://travis-ci.org/davydovanton/shallow_attributes)
|
3
3
|
[![Code Climate](https://codeclimate.com/github/davydovanton/shallow_attributes/badges/gpa.svg)](https://codeclimate.com/github/davydovanton/shallow_attributes)
|
4
|
+
[![Coverage Status](https://coveralls.io/repos/github/davydovanton/shallow_attributes/badge.svg?branch=master)](https://coveralls.io/github/davydovanton/shallow_attributes?branch=master)
|
4
5
|
[![Inline docs](http://inch-ci.org/github/davydovanton/shallow_attributes.svg?branch=master)](http://inch-ci.org/github/davydovanton/shallow_attributes)
|
5
6
|
|
6
|
-
Simple and lightweight Virtus analog.
|
7
|
+
Simple and lightweight Virtus analog without any dependencies. [Documentation][doc-link].
|
7
8
|
|
8
9
|
## Motivation
|
9
10
|
|
10
|
-
There are already a lot of good and flexible gems which solve a similar problem, allowing attributes
|
11
|
-
defined with their types, for example: virtus, fast_attributes
|
12
|
-
|
13
|
-
to the Virtus API, simple, fast,
|
11
|
+
There are already a lot of good and flexible gems which solve a similar problem, allowing attributes
|
12
|
+
to be defined with their types, for example: [virtus][virtus-link], [fast_attributes][fast-attributes-link]
|
13
|
+
or [attrio][attrio-link]. However, the disadvantage of these gems is performance or API. So, the goal
|
14
|
+
of `ShallowAttributes` is to provide a simple solution which is similar to the `Virtus` API, simple, fast,
|
15
|
+
understandable and extendable.
|
14
16
|
|
15
|
-
This is [the performance benchmark]
|
17
|
+
This is [the performance benchmark][performance-benchmark] of ShallowAttributes compared to virtus gems.
|
16
18
|
|
17
19
|
## Installation
|
18
20
|
|
@@ -31,6 +33,15 @@ Or install it yourself as:
|
|
31
33
|
$ gem install shallow_attributes
|
32
34
|
|
33
35
|
## Examples
|
36
|
+
### Table of contents
|
37
|
+
* [Using ShallowAttributes with Classes](#using-shallowattributes-with-classes)
|
38
|
+
* [Default Values](#default-values)
|
39
|
+
* [Embedded Value](#embedded-value)
|
40
|
+
* [Custom Coercions](#custom-coercions)
|
41
|
+
* [Collection Member Coercions](#collection-member-coercions)
|
42
|
+
* [Note about Member Coercions](#important-note-about-member-coercions)
|
43
|
+
* [Overriding setters](#overriding-setters)
|
44
|
+
* [ActiveModel validation](#activemodel-validation)
|
34
45
|
|
35
46
|
### Using ShallowAttributes with Classes
|
36
47
|
You can create classes extended with Virtus and define attributes:
|
@@ -53,11 +64,11 @@ class SuperUser < User
|
|
53
64
|
end
|
54
65
|
|
55
66
|
user = User.new(name: 'Anton', age: 31)
|
56
|
-
user.name
|
67
|
+
user.name # => "Anton"
|
57
68
|
|
58
69
|
user.age = '31' # => 31
|
59
|
-
user.age = nil
|
60
|
-
user.age.class
|
70
|
+
user.age = nil # => nil
|
71
|
+
user.age.class # => Fixnum
|
61
72
|
|
62
73
|
user.birthday = 'November 18th, 1983' # => #<DateTime: 1983-11-18T00:00:00+00:00 (4891313/2,0/1,2299161)>
|
63
74
|
|
@@ -65,11 +76,11 @@ user.attributes # => { name: "Anton", age: 31, birthday: nil }
|
|
65
76
|
|
66
77
|
# mass-assignment
|
67
78
|
user.attributes = { name: 'Jane', age: 21 }
|
68
|
-
user.name
|
69
|
-
user.age
|
79
|
+
user.name # => "Jane"
|
80
|
+
user.age # => 21
|
70
81
|
|
71
82
|
super_user = SuperUser.new
|
72
|
-
user.age = nil
|
83
|
+
user.age = nil # => 0
|
73
84
|
```
|
74
85
|
|
75
86
|
### Default Values
|
@@ -109,7 +120,7 @@ page.reset_attribute(:views) # => 0
|
|
109
120
|
page.views # => 0
|
110
121
|
```
|
111
122
|
|
112
|
-
|
123
|
+
### Embedded Value
|
113
124
|
|
114
125
|
``` ruby
|
115
126
|
class City
|
@@ -142,7 +153,7 @@ user = User.new(address: {
|
|
142
153
|
}
|
143
154
|
})
|
144
155
|
|
145
|
-
user.address.street
|
156
|
+
user.address.street # => "Street 1/2"
|
146
157
|
user.address.city.name # => "NYC"
|
147
158
|
```
|
148
159
|
|
@@ -165,7 +176,7 @@ end
|
|
165
176
|
|
166
177
|
user = User.new
|
167
178
|
user.info = '{"email":"john@domain.com"}' # => {"email"=>"john@domain.com"}
|
168
|
-
user.info.class
|
179
|
+
user.info.class # => Hash
|
169
180
|
|
170
181
|
# With a custom attribute encapsulating coercion-specific configuration
|
171
182
|
class NoisyString
|
@@ -228,15 +239,49 @@ user = User.new(
|
|
228
239
|
{ :address => '1234 Any St.', :locality => 'Anytown', :region => "DC", :postal_code => "21234" } ])
|
229
240
|
|
230
241
|
user.phone_numbers # => [#<PhoneNumber:0x007fdb2d3bef88 @number="212-555-1212">, #<PhoneNumber:0x007fdb2d3beb00 @number="919-444-3265">]
|
231
|
-
user.addresses
|
232
|
-
|
233
|
-
user.attributes
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
242
|
+
user.addresses # => [#<Address:0x007fdb2d3be448 @address="1234 Any St.", @locality="Anytown", @region="DC", @postal_code="21234">]
|
243
|
+
|
244
|
+
user.attributes
|
245
|
+
# => {
|
246
|
+
# => :phone_numbers => [
|
247
|
+
# => { :number => '212-555-1212' },
|
248
|
+
# => { :number => '919-444-3265' }
|
249
|
+
# => ],
|
250
|
+
# => :addresses => [
|
251
|
+
# => {
|
252
|
+
# => :address => '1234 Any St.',
|
253
|
+
# => :locality => 'Anytown',
|
254
|
+
# => :region => "DC",
|
255
|
+
# => :postal_code => "21234"
|
256
|
+
# => }
|
257
|
+
# => ]
|
258
|
+
# => }
|
259
|
+
```
|
260
|
+
|
261
|
+
### IMPORTANT note about member coercions
|
262
|
+
|
263
|
+
ShallowAttributes performs coercions only when a value is being assigned. If you mutate the value later on using its own interfaces then coercion won't be triggered.
|
264
|
+
|
265
|
+
Here's an example:
|
266
|
+
|
267
|
+
``` ruby
|
268
|
+
class Book
|
269
|
+
include ShallowAttributes
|
270
|
+
attribute :title, String
|
271
|
+
end
|
272
|
+
|
273
|
+
class Library
|
274
|
+
include ShallowAttributes
|
275
|
+
attribute :books, Array, of: Book
|
276
|
+
end
|
277
|
+
|
278
|
+
library = Library.new
|
279
|
+
|
280
|
+
# This will coerce Hash to a Book instance
|
281
|
+
library.books = [ { :title => 'Introduction' } ]
|
282
|
+
|
283
|
+
# This WILL NOT COERCE the value because you mutate the books array with Array#<<
|
284
|
+
library.books << { :title => 'Another Introduction' }
|
240
285
|
```
|
241
286
|
|
242
287
|
### Overriding setters
|
@@ -286,11 +331,16 @@ user.valid? # => true
|
|
286
331
|
|
287
332
|
## Ruby version support
|
288
333
|
|
289
|
-
ShallowAttributes is known to work correctly with the following rubies:
|
334
|
+
ShallowAttributes is [known to work correctly][travis-link] with the following rubies:
|
290
335
|
|
291
|
-
* 2.
|
336
|
+
* 2.0
|
337
|
+
* 2.1
|
338
|
+
* 2.2
|
339
|
+
* 2.3
|
340
|
+
* 2.4
|
341
|
+
* jruby-head
|
292
342
|
|
293
|
-
|
343
|
+
Also we run rbx-2 buld too.
|
294
344
|
|
295
345
|
## Contributing
|
296
346
|
|
@@ -298,8 +348,14 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/davydo
|
|
298
348
|
This project is intended to be a safe, welcoming space for collaboration, and contributors are expected
|
299
349
|
to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
300
350
|
|
301
|
-
|
302
351
|
## License
|
303
352
|
|
304
353
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
305
354
|
|
355
|
+
|
356
|
+
[doc-link]: http://www.rubydoc.info/github/davydovanton/shallow_attributes/master
|
357
|
+
[virtus-link]: https://github.com/solnic/virtus
|
358
|
+
[fast-attributes-link]: https://github.com/applift/fast_attributes
|
359
|
+
[attrio-link]: https://github.com/jetrockets/attrio
|
360
|
+
[performance-benchmark]: https://gist.github.com/davydovanton/d14b51ab63e3fab63ecb
|
361
|
+
[travis-link]: https://travis-ci.org/davydovanton/shallow_attributes
|
data/Rakefile
CHANGED
@@ -6,6 +6,19 @@ module ShallowAttributes
|
|
6
6
|
#
|
7
7
|
# @since 0.1.0
|
8
8
|
module ClassMethods
|
9
|
+
# Inject our default values into subclasses.
|
10
|
+
#
|
11
|
+
# @private
|
12
|
+
#
|
13
|
+
# @param [Object] subclass
|
14
|
+
#
|
15
|
+
def inherited(subclass)
|
16
|
+
super
|
17
|
+
if respond_to?(:default_values)
|
18
|
+
subclass.default_values.merge!(default_values)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
9
22
|
# Returns hash which contain default values for each attribute
|
10
23
|
#
|
11
24
|
# @private
|
@@ -14,16 +27,11 @@ module ShallowAttributes
|
|
14
27
|
#
|
15
28
|
# @since 0.1.0
|
16
29
|
def default_values
|
17
|
-
|
18
|
-
@default_values.merge!(superclass.default_values) { |_, v, _| v }
|
19
|
-
else
|
20
|
-
@default_values
|
21
|
-
end
|
30
|
+
@default_values ||= {}
|
22
31
|
end
|
23
32
|
|
24
33
|
# Returns all class attributes.
|
25
34
|
#
|
26
|
-
#
|
27
35
|
# @example Create new User instance
|
28
36
|
# class User
|
29
37
|
# include ShallowAttributes
|
@@ -66,8 +74,7 @@ module ShallowAttributes
|
|
66
74
|
def attribute(name, type, options = {})
|
67
75
|
options[:default] ||= [] if type == Array
|
68
76
|
|
69
|
-
|
70
|
-
@default_values[name] = options.delete(:default)
|
77
|
+
default_values[name] = options.delete(:default)
|
71
78
|
|
72
79
|
initialize_setter(name, type, options)
|
73
80
|
initialize_getter(name)
|
@@ -89,7 +96,12 @@ module ShallowAttributes
|
|
89
96
|
def initialize_setter(name, type, options)
|
90
97
|
module_eval <<-EOS, __FILE__, __LINE__ + 1
|
91
98
|
def #{name}=(value)
|
92
|
-
@#{name} =
|
99
|
+
@#{name} = if value.is_a?(#{type}) && !value.is_a?(Array)
|
100
|
+
value
|
101
|
+
else
|
102
|
+
ShallowAttributes::Type.coerce(#{type}, value, #{options})
|
103
|
+
end
|
104
|
+
|
93
105
|
@attributes[:#{name}] = @#{name}
|
94
106
|
end
|
95
107
|
EOS
|
@@ -12,11 +12,11 @@ module ShallowAttributes
|
|
12
12
|
# @private
|
13
13
|
#
|
14
14
|
# @since 0.1.0
|
15
|
-
TO_H_PROC = ->
|
15
|
+
TO_H_PROC = ->(value) { value.respond_to?(:to_hash) ? value.to_hash : value }
|
16
16
|
|
17
17
|
# Initialize instance object with specific attributes
|
18
18
|
#
|
19
|
-
# @param [Hash]
|
19
|
+
# @param [Hash] attrs the attributes contained in the class
|
20
20
|
#
|
21
21
|
# @example Create new User instance
|
22
22
|
# class User
|
@@ -30,7 +30,11 @@ module ShallowAttributes
|
|
30
30
|
#
|
31
31
|
# @since 0.1.0
|
32
32
|
def initialize(attrs = {})
|
33
|
-
@attributes =
|
33
|
+
@attributes = {}
|
34
|
+
attrs.each_pair do |key, value|
|
35
|
+
key = key.to_sym
|
36
|
+
@attributes[key] = value if default_values.key?(key)
|
37
|
+
end
|
34
38
|
define_attributes
|
35
39
|
define_default_attributes
|
36
40
|
end
|
@@ -131,7 +135,7 @@ module ShallowAttributes
|
|
131
135
|
# @return the object
|
132
136
|
#
|
133
137
|
# @since 0.1.0
|
134
|
-
def coerce(value,
|
138
|
+
def coerce(value, _options = {})
|
135
139
|
self.attributes = value
|
136
140
|
self
|
137
141
|
end
|
@@ -17,33 +17,6 @@ module ShallowAttributes
|
|
17
17
|
class InvalidValueError < TypeError
|
18
18
|
end
|
19
19
|
|
20
|
-
# Convert value object to specific Type class
|
21
|
-
#
|
22
|
-
# @private
|
23
|
-
#
|
24
|
-
# @param [Class] type the type class object
|
25
|
-
# @param [Object] value the value that should be submit to the necessary type
|
26
|
-
# @param [Hash] options the options to create a message with.
|
27
|
-
# @option options [String] :of The type of array
|
28
|
-
# @option options [boolean] :allow_nil cast `nil` to integer or float
|
29
|
-
#
|
30
|
-
# @example Convert integer to sting type
|
31
|
-
# ShallowAttributes::Type.coerce(String, 1)
|
32
|
-
# # => '1'
|
33
|
-
#
|
34
|
-
# @example Convert string to custom hash type
|
35
|
-
# ShallowAttributes::Type.instance_for(JsonType, '{"a"=>1}')
|
36
|
-
# # => { a: 1 }
|
37
|
-
#
|
38
|
-
# @return the converted value object
|
39
|
-
#
|
40
|
-
# @since 0.1.0
|
41
|
-
def self.coerce(type, value, options = {})
|
42
|
-
type_instance(type).coerce(value, options)
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
20
|
# Hash object with cached type objects.
|
48
21
|
#
|
49
22
|
# @private
|
@@ -58,25 +31,54 @@ module ShallowAttributes
|
|
58
31
|
::Time => ShallowAttributes::Type::Time.new
|
59
32
|
}.freeze
|
60
33
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
34
|
+
class << self
|
35
|
+
# Convert value object to specific Type class
|
36
|
+
#
|
37
|
+
# @private
|
38
|
+
#
|
39
|
+
# @param [Class] type the type class object
|
40
|
+
# @param [Object] value the value that should be submit to the necessary type
|
41
|
+
# @param [Hash] options the options to create a message with.
|
42
|
+
# @option options [String] :of The type of array
|
43
|
+
# @option options [boolean] :allow_nil cast `nil` to integer or float
|
44
|
+
#
|
45
|
+
# @example Convert integer to sting type
|
46
|
+
# ShallowAttributes::Type.coerce(String, 1)
|
47
|
+
# # => '1'
|
48
|
+
#
|
49
|
+
# @example Convert string to custom hash type
|
50
|
+
# ShallowAttributes::Type.instance_for(JsonType, '{"a"=>1}')
|
51
|
+
# # => { a: 1 }
|
52
|
+
#
|
53
|
+
# @return the converted value object
|
54
|
+
#
|
55
|
+
# @since 0.1.0
|
56
|
+
def coerce(type, value, options = {})
|
57
|
+
type_instance(type).coerce(value, options)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# Returns class object for specific Type class
|
63
|
+
#
|
64
|
+
# @private
|
65
|
+
#
|
66
|
+
# @param [Class] type the type class object
|
67
|
+
#
|
68
|
+
# @example Returns Sting type class
|
69
|
+
# ShallowAttributes::Type.instance_for(String)
|
70
|
+
# # => ShallowAttributes::Type::Sting class
|
71
|
+
#
|
72
|
+
# @example Returns other type class
|
73
|
+
# ShallowAttributes::Type.instance_for(MySpecialStringType)
|
74
|
+
# # => MySpecialStringType class
|
75
|
+
#
|
76
|
+
# @return [Class]
|
77
|
+
#
|
78
|
+
# @since 0.1.0
|
79
|
+
def type_instance(klass)
|
80
|
+
DEFAULT_TYPE_OBJECTS[klass] || ShallowAttributes::Type.const_get(klass.name).new
|
81
|
+
end
|
80
82
|
end
|
81
83
|
end
|
82
84
|
end
|
data/shallow_attributes.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shallow_attributes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anton Davydov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,20 +52,6 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '5.0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: simplecov
|
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
55
|
description: Attributes for Plain Old Ruby Objects
|
70
56
|
email:
|
71
57
|
- antondavydov.o@gmail.com
|
@@ -75,13 +61,12 @@ extra_rdoc_files: []
|
|
75
61
|
files:
|
76
62
|
- ".gitignore"
|
77
63
|
- ".travis.yml"
|
64
|
+
- CHANGELOG.md
|
78
65
|
- CODE_OF_CONDUCT.md
|
79
66
|
- Gemfile
|
80
67
|
- LICENSE.txt
|
81
68
|
- README.md
|
82
69
|
- Rakefile
|
83
|
-
- bin/console
|
84
|
-
- bin/setup
|
85
70
|
- lib/shallow_attributes.rb
|
86
71
|
- lib/shallow_attributes/class_methods.rb
|
87
72
|
- lib/shallow_attributes/instance_methods.rb
|
data/bin/console
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "bundler/setup"
|
4
|
-
require "shallow_attributes"
|
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
|