shallow_attributes 0.9.0 → 0.9.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8d9c84f7e0cad4b197b19de9247b98e29355907f
4
- data.tar.gz: be6015df6fed9d6efb0d65abc9f2a6a2a4bcadd2
3
+ metadata.gz: eae949ec12824705349bf498a92b2f524bd912a0
4
+ data.tar.gz: 1d3a7b3754419370e910cd6b90978425ff12010b
5
5
  SHA512:
6
- metadata.gz: 1eca913e91af6856f031b0e677d115f315d079de53d263601e6b7a51df4f186799813d26f30f3d21ae4536faa34e571debc767ed2db76d911cf30f97610140fb
7
- data.tar.gz: 9f430c8bbe5c73d3a2ad22b0f6cdbe8d0a9b8309e7d5e15291735dea260facc1f7406e532bd53eca02e1c11a698acfb440c4ea5cc472f0fdec5c3ac4b0f6efa5
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
8
- - 2.2.4
9
- - 2.3.0
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
@@ -0,0 +1,2 @@
1
+ # ShallowAttributes Changes
2
+ ## HEAD
data/Gemfile CHANGED
@@ -6,3 +6,5 @@ gemspec
6
6
  if !ENV['TRAVIS']
7
7
  gem 'yard', require: false
8
8
  end
9
+
10
+ gem 'coveralls', require: false
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 to be
11
- defined with their types, for example: virtus, fast_attributes or attrio. However, the disadvantage of these
12
- gems is performance or API. So, the goal of ShallowAttributes is to provide a simple solution which is similar
13
- to the Virtus API, simple, fast, understandable and extendable.
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](https://gist.github.com/davydovanton/7cf0da532eae71381cbd) of ShallowAttributes compared to virtus gems.
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 # => "Anton"
67
+ user.name # => "Anton"
57
68
 
58
69
  user.age = '31' # => 31
59
- user.age = nil # => nil
60
- user.age.class # => Fixnum
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 # => "Jane"
69
- user.age # => 21
79
+ user.name # => "Jane"
80
+ user.age # => 21
70
81
 
71
82
  super_user = SuperUser.new
72
- user.age = nil # => 0
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
- ## Embedded Value
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 # => "Street 1/2"
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 # => Hash
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 # => [#<Address:0x007fdb2d3be448 @address="1234 Any St.", @locality="Anytown", @region="DC", @postal_code="21234">]
232
-
233
- user.attributes # => {
234
- # => :phone_numbers => [
235
- # => { :number => '212-555-1212' },
236
- # => { :number => '919-444-3265' } ],
237
- # => :addresses => [
238
- # => { :address => '1234 Any St.', :locality => 'Anytown', :region => "DC", :postal_code => "21234" } ]
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.3+
336
+ * 2.0
337
+ * 2.1
338
+ * 2.2
339
+ * 2.3
340
+ * 2.4
341
+ * jruby-head
292
342
 
293
- In future I want ot support other ruby sersion/platforms.
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
@@ -7,4 +7,4 @@ Rake::TestTask.new(:test) do |t|
7
7
  t.test_files = FileList['test/**/*_test.rb']
8
8
  end
9
9
 
10
- task :default => :test
10
+ task default: :test
@@ -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
- if superclass.respond_to?(:default_values)
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
- @default_values ||= {}
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} = ShallowAttributes::Type.coerce(#{type}, value, #{options})
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 = -> (value) { value.respond_to?(:to_hash) ? value.to_hash : value }
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] attributes the attributes contained in the class
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 = attrs.delete_if { |key, _| !default_values.key?(key) }
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, options = {})
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
- # Returns class object for specific Type class
62
- #
63
- # @private
64
- #
65
- # @param [Class] type the type class object
66
- #
67
- # @example Returns Sting type class
68
- # ShallowAttributes::Type.instance_for(String)
69
- # # => ShallowAttributes::Type::Sting class
70
- #
71
- # @example Returns other type class
72
- # ShallowAttributes::Type.instance_for(MySpecialStringType)
73
- # # => MySpecialStringType class
74
- #
75
- # @return [Class]
76
- #
77
- # @since 0.1.0
78
- def self.type_instance(klass)
79
- DEFAULT_TYPE_OBJECTS[klass] || ShallowAttributes::Type.const_get(klass.name).new
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
@@ -39,7 +39,7 @@ module ShallowAttributes
39
39
  # @return [boolean]
40
40
  #
41
41
  # @since 0.1.0
42
- def coerce(value, options = {})
42
+ def coerce(value, _options = {})
43
43
  if TRUE_VALUES.include?(value)
44
44
  true
45
45
  elsif FALSE_VALUES.include?(value)
@@ -22,7 +22,7 @@ module ShallowAttributes
22
22
  # @return [DateTime]
23
23
  #
24
24
  # @since 0.1.0
25
- def coerce(value, options = {})
25
+ def coerce(value, _options = {})
26
26
  case value
27
27
  when ::DateTime then value
28
28
  when ::Time then ::DateTime.parse(value.to_s)
@@ -1,6 +1,6 @@
1
1
  module ShallowAttributes
2
2
  module Type
3
- # Abstract class for typecast object to Float type.
3
+ # This class needs for cange object type to Float.
4
4
  #
5
5
  # @abstract
6
6
  #
@@ -1,6 +1,6 @@
1
1
  module ShallowAttributes
2
2
  module Type
3
- # Abstract class for typecast object to Integer type.
3
+ # This class cange object to Integer.
4
4
  #
5
5
  # @abstract
6
6
  #
@@ -20,7 +20,7 @@ module ShallowAttributes
20
20
  # @return [Sting]
21
21
  #
22
22
  # @since 0.1.0
23
- def coerce(value, options = {})
23
+ def coerce(value, _options = {})
24
24
  case value
25
25
  when ::Array then value.join
26
26
  when ::Hash, ::Class then error(value)
@@ -22,7 +22,7 @@ module ShallowAttributes
22
22
  # @return [Time]
23
23
  #
24
24
  # @since 0.1.0
25
- def coerce(value, options = {})
25
+ def coerce(value, _options = {})
26
26
  case value
27
27
  when ::Time then value
28
28
  when ::Integer then ::Time.at(value)
@@ -2,5 +2,5 @@ module ShallowAttributes
2
2
  # Defines the full version
3
3
  #
4
4
  # @since 0.1.0
5
- VERSION = "0.9.0".freeze
5
+ VERSION = "0.9.2".freeze
6
6
  end
@@ -22,5 +22,4 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency "bundler", "~> 1.11"
23
23
  spec.add_development_dependency "rake", "~> 10.0"
24
24
  spec.add_development_dependency "minitest", "~> 5.0"
25
- spec.add_development_dependency "simplecov", '~> 0'
26
25
  end
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.0
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: 2016-03-24 00:00:00.000000000 Z
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
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here