activemodel 5.2.0.rc1 → 5.2.0.rc2

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
  SHA256:
3
- metadata.gz: 666a46bf39ff339f606b0d60219d61138a7de2def4808f6135255ee2a2b43ff0
4
- data.tar.gz: 5de9942d5e2dfd21df46a9ac538bc83b4bc879c57884e3f26192f4204dac6d33
3
+ metadata.gz: 97b6d1fb9b61ef956d88c6ee764394e9ccc0fcad2333073a876066fd9c7fcb7a
4
+ data.tar.gz: 86e60a71d98b585c900c3351a4c4fcf1f16ac73e4c0fc0b4595a443733c31f50
5
5
  SHA512:
6
- metadata.gz: f1f362e75e4a2cf7f08d2a89c2c8465516a72f014fc9d5d230a5cbc18195a4c734deb1b0c5bdede79d7126c7450154218b33824b5ab7b778ce033a800f156c68
7
- data.tar.gz: b00a94be4f5c30d482412abfb0e395c46804195b6c64ab57ba0f4cb4f01481bf2ff6ae4b32f0b51177859b496f8253413bbe8672602390f80b71df7a31b8a500
6
+ metadata.gz: 3d11a69342faac6bed87647ec40059830c8441459f30a6c9d94f3656ff2ce89a08fe3b12eed6846bae3352eb5fb929d4e30ab91611e569e7dae5f9e851fe47ba
7
+ data.tar.gz: 3493c445e4a3db83fcad5b043a82d16cf1242286bd49e73755bebf82a17f6b0f8f78d9563d2eac7d6ba739dc921b8a9a8a9ce2108098e4aab72bfa335658feec
@@ -1,3 +1,10 @@
1
+ ## Rails 5.2.0.rc2 (March 20, 2018) ##
2
+
3
+ * Do not lose all multiple `:includes` with options in serialization.
4
+
5
+ *Mike Mangino*
6
+
7
+
1
8
  ## Rails 5.2.0.rc1 (January 30, 2018) ##
2
9
 
3
10
  * Models using the attributes API with a proc default can now be marshalled.
@@ -239,7 +239,7 @@ The latest version of Active Model can be installed with RubyGems:
239
239
 
240
240
  Source code can be downloaded as part of the Rails project on GitHub
241
241
 
242
- * https://github.com/rails/rails/tree/master/activemodel
242
+ * https://github.com/rails/rails/tree/5-2-stable/activemodel
243
243
 
244
244
 
245
245
  == License
@@ -35,6 +35,8 @@ module ActiveModel
35
35
  _assign_attributes(sanitize_for_mass_assignment(attributes))
36
36
  end
37
37
 
38
+ alias attributes= assign_attributes
39
+
38
40
  private
39
41
 
40
42
  def _assign_attributes(attributes)
@@ -35,10 +35,6 @@ module ActiveModel
35
35
  end
36
36
  end
37
37
 
38
- def changed_attribute_names
39
- attr_names.select { |attr| changed?(attr) }
40
- end
41
-
42
38
  def any_changes?
43
39
  attr_names.any? { |attr| changed?(attr) }
44
40
  end
@@ -113,5 +109,8 @@ module ActiveModel
113
109
 
114
110
  def original_value(*)
115
111
  end
112
+
113
+ def force_change(*)
114
+ end
116
115
  end
117
116
  end
@@ -22,12 +22,12 @@ module ActiveModel
22
22
  class LazyAttributeHash # :nodoc:
23
23
  delegate :transform_values, :each_key, :each_value, :fetch, :except, to: :materialize
24
24
 
25
- def initialize(types, values, additional_types, default_attributes)
25
+ def initialize(types, values, additional_types, default_attributes, delegate_hash = {})
26
26
  @types = types
27
27
  @values = values
28
28
  @additional_types = additional_types
29
29
  @materialized = false
30
- @delegate_hash = {}
30
+ @delegate_hash = delegate_hash
31
31
  @default_attributes = default_attributes
32
32
  end
33
33
 
@@ -76,15 +76,17 @@ module ActiveModel
76
76
  end
77
77
 
78
78
  def marshal_dump
79
- materialize
79
+ [@types, @values, @additional_types, @default_attributes, @delegate_hash]
80
80
  end
81
81
 
82
- def marshal_load(delegate_hash)
83
- @delegate_hash = delegate_hash
84
- @types = {}
85
- @values = {}
86
- @additional_types = {}
87
- @materialized = true
82
+ def marshal_load(values)
83
+ if values.is_a?(Hash)
84
+ empty_hash = {}.freeze
85
+ initialize(empty_hash, empty_hash, empty_hash, empty_hash, values)
86
+ @materialized = true
87
+ else
88
+ initialize(*values)
89
+ end
88
90
  end
89
91
 
90
92
  protected
@@ -66,6 +66,10 @@ module ActiveModel
66
66
  super
67
67
  end
68
68
 
69
+ def attributes
70
+ @attributes.to_hash
71
+ end
72
+
69
73
  private
70
74
 
71
75
  def write_attribute(attr_name, value)
@@ -3,7 +3,6 @@
3
3
  require "active_support/hash_with_indifferent_access"
4
4
  require "active_support/core_ext/object/duplicable"
5
5
  require "active_model/attribute_mutation_tracker"
6
- require "active_model/attribute_set"
7
6
 
8
7
  module ActiveModel
9
8
  # == Active \Model \Dirty
@@ -143,8 +142,11 @@ module ActiveModel
143
142
  end
144
143
 
145
144
  def changes_applied # :nodoc:
146
- _prepare_changes
145
+ unless defined?(@attributes)
146
+ @previously_changed = changes
147
+ end
147
148
  @mutations_before_last_save = mutations_from_database
149
+ @attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new
148
150
  forget_attribute_assignments
149
151
  @mutations_from_database = nil
150
152
  end
@@ -155,7 +157,7 @@ module ActiveModel
155
157
  # person.name = 'bob'
156
158
  # person.changed? # => true
157
159
  def changed?
158
- mutations_from_database.any_changes?
160
+ changed_attributes.present?
159
161
  end
160
162
 
161
163
  # Returns an array with the name of the attributes with unsaved changes.
@@ -164,24 +166,24 @@ module ActiveModel
164
166
  # person.name = 'bob'
165
167
  # person.changed # => ["name"]
166
168
  def changed
167
- mutations_from_database.changed_attribute_names
169
+ changed_attributes.keys
168
170
  end
169
171
 
170
172
  # Handles <tt>*_changed?</tt> for +method_missing+.
171
173
  def attribute_changed?(attr, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN) # :nodoc:
172
- !!mutations_from_database.changed?(attr) &&
174
+ !!changes_include?(attr) &&
173
175
  (to == OPTION_NOT_GIVEN || to == _read_attribute(attr)) &&
174
- (from == OPTION_NOT_GIVEN || from == attribute_was(attr))
176
+ (from == OPTION_NOT_GIVEN || from == changed_attributes[attr])
175
177
  end
176
178
 
177
179
  # Handles <tt>*_was</tt> for +method_missing+.
178
180
  def attribute_was(attr) # :nodoc:
179
- mutations_from_database.original_value(attr)
181
+ attribute_changed?(attr) ? changed_attributes[attr] : _read_attribute(attr)
180
182
  end
181
183
 
182
184
  # Handles <tt>*_previously_changed?</tt> for +method_missing+.
183
185
  def attribute_previously_changed?(attr) #:nodoc:
184
- mutations_before_last_save.changed?(attr)
186
+ previous_changes_include?(attr)
185
187
  end
186
188
 
187
189
  # Restore all previous data of the provided attributes.
@@ -191,12 +193,15 @@ module ActiveModel
191
193
 
192
194
  # Clears all dirty data: current changes and previous changes.
193
195
  def clear_changes_information
196
+ @previously_changed = ActiveSupport::HashWithIndifferentAccess.new
194
197
  @mutations_before_last_save = nil
198
+ @attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new
195
199
  forget_attribute_assignments
196
200
  @mutations_from_database = nil
197
201
  end
198
202
 
199
203
  def clear_attribute_changes(attr_names)
204
+ attributes_changed_by_setter.except!(*attr_names)
200
205
  attr_names.each do |attr_name|
201
206
  clear_attribute_change(attr_name)
202
207
  end
@@ -209,7 +214,13 @@ module ActiveModel
209
214
  # person.name = 'robert'
210
215
  # person.changed_attributes # => {"name" => "bob"}
211
216
  def changed_attributes
212
- mutations_from_database.changed_values.freeze
217
+ # This should only be set by methods which will call changed_attributes
218
+ # multiple times when it is known that the computed value cannot change.
219
+ if defined?(@cached_changed_attributes)
220
+ @cached_changed_attributes
221
+ else
222
+ attributes_changed_by_setter.reverse_merge(mutations_from_database.changed_values).freeze
223
+ end
213
224
  end
214
225
 
215
226
  # Returns a hash of changed attributes indicating their original
@@ -219,8 +230,9 @@ module ActiveModel
219
230
  # person.name = 'bob'
220
231
  # person.changes # => { "name" => ["bill", "bob"] }
221
232
  def changes
222
- _prepare_changes
223
- mutations_from_database.changes
233
+ cache_changed_attributes do
234
+ ActiveSupport::HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }]
235
+ end
224
236
  end
225
237
 
226
238
  # Returns a hash of attributes that were changed before the model was saved.
@@ -230,7 +242,8 @@ module ActiveModel
230
242
  # person.save
231
243
  # person.previous_changes # => {"name" => ["bob", "robert"]}
232
244
  def previous_changes
233
- mutations_before_last_save.changes
245
+ @previously_changed ||= ActiveSupport::HashWithIndifferentAccess.new
246
+ @previously_changed.merge(mutations_before_last_save.changes)
234
247
  end
235
248
 
236
249
  def attribute_changed_in_place?(attr_name) # :nodoc:
@@ -246,17 +259,11 @@ module ActiveModel
246
259
  unless defined?(@mutations_from_database)
247
260
  @mutations_from_database = nil
248
261
  end
249
-
250
- unless defined?(@attributes)
251
- @_pseudo_attributes = true
252
- @attributes = AttributeSet.new(
253
- Hash.new { |h, attr|
254
- h[attr] = Attribute.with_cast_value(attr, _clone_attribute(attr), Type.default_value)
255
- }
256
- )
262
+ @mutations_from_database ||= if defined?(@attributes)
263
+ ActiveModel::AttributeMutationTracker.new(@attributes)
264
+ else
265
+ NullMutationTracker.instance
257
266
  end
258
-
259
- @mutations_from_database ||= ActiveModel::AttributeMutationTracker.new(@attributes)
260
267
  end
261
268
 
262
269
  def forget_attribute_assignments
@@ -267,45 +274,68 @@ module ActiveModel
267
274
  @mutations_before_last_save ||= ActiveModel::NullMutationTracker.instance
268
275
  end
269
276
 
277
+ def cache_changed_attributes
278
+ @cached_changed_attributes = changed_attributes
279
+ yield
280
+ ensure
281
+ clear_changed_attributes_cache
282
+ end
283
+
284
+ def clear_changed_attributes_cache
285
+ remove_instance_variable(:@cached_changed_attributes) if defined?(@cached_changed_attributes)
286
+ end
287
+
288
+ # Returns +true+ if attr_name is changed, +false+ otherwise.
289
+ def changes_include?(attr_name)
290
+ attributes_changed_by_setter.include?(attr_name) || mutations_from_database.changed?(attr_name)
291
+ end
292
+ alias attribute_changed_by_setter? changes_include?
293
+
294
+ # Returns +true+ if attr_name were changed before the model was saved,
295
+ # +false+ otherwise.
296
+ def previous_changes_include?(attr_name)
297
+ previous_changes.include?(attr_name)
298
+ end
299
+
270
300
  # Handles <tt>*_change</tt> for +method_missing+.
271
301
  def attribute_change(attr)
272
- [attribute_was(attr), _read_attribute(attr)] if attribute_changed?(attr)
302
+ [changed_attributes[attr], _read_attribute(attr)] if attribute_changed?(attr)
273
303
  end
274
304
 
275
305
  # Handles <tt>*_previous_change</tt> for +method_missing+.
276
306
  def attribute_previous_change(attr)
277
- mutations_before_last_save.change_to_attribute(attr)
307
+ previous_changes[attr] if attribute_previously_changed?(attr)
278
308
  end
279
309
 
280
310
  # Handles <tt>*_will_change!</tt> for +method_missing+.
281
311
  def attribute_will_change!(attr)
282
- attr = attr.to_s
283
- mutations_from_database.force_change(attr).tap do
284
- @attributes[attr] if defined?(@_pseudo_attributes)
312
+ unless attribute_changed?(attr)
313
+ begin
314
+ value = _read_attribute(attr)
315
+ value = value.duplicable? ? value.clone : value
316
+ rescue TypeError, NoMethodError
317
+ end
318
+
319
+ set_attribute_was(attr, value)
285
320
  end
321
+ mutations_from_database.force_change(attr)
286
322
  end
287
323
 
288
324
  # Handles <tt>restore_*!</tt> for +method_missing+.
289
325
  def restore_attribute!(attr)
290
326
  if attribute_changed?(attr)
291
- __send__("#{attr}=", attribute_was(attr))
327
+ __send__("#{attr}=", changed_attributes[attr])
292
328
  clear_attribute_changes([attr])
293
329
  end
294
330
  end
295
331
 
296
- def _prepare_changes
297
- if defined?(@_pseudo_attributes)
298
- changed.each do |attr|
299
- @attributes.write_from_user(attr, _read_attribute(attr))
300
- end
301
- end
332
+ def attributes_changed_by_setter
333
+ @attributes_changed_by_setter ||= ActiveSupport::HashWithIndifferentAccess.new
302
334
  end
303
335
 
304
- def _clone_attribute(attr)
305
- value = _read_attribute(attr)
306
- value.duplicable? ? value.clone : value
307
- rescue TypeError, NoMethodError
308
- value
336
+ # Force an attribute to have a particular "before" value
337
+ def set_attribute_was(attr, old_value)
338
+ attributes_changed_by_setter[attr] = old_value
309
339
  end
310
340
  end
311
341
  end
@@ -10,7 +10,7 @@ module ActiveModel
10
10
  MAJOR = 5
11
11
  MINOR = 2
12
12
  TINY = 0
13
- PRE = "rc1"
13
+ PRE = "rc2"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -179,7 +179,7 @@ module ActiveModel
179
179
  return unless includes = options[:include]
180
180
 
181
181
  unless includes.is_a?(Hash)
182
- includes = Hash[Array(includes).map { |n| n.is_a?(Hash) ? n.to_a.first : [n, {}] }]
182
+ includes = Hash[Array(includes).flat_map { |n| n.is_a?(Hash) ? n.to_a : [[n, {}]] }]
183
183
  end
184
184
 
185
185
  includes.each do |association, opts|
@@ -42,7 +42,7 @@ module ActiveModel
42
42
  end
43
43
 
44
44
  def new_date(year, mon, mday)
45
- if year && year != 0
45
+ unless year.nil? || (year == 0 && mon == 0 && mday == 0)
46
46
  ::Date.new(year, mon, mday) rescue nil
47
47
  end
48
48
  end
@@ -28,14 +28,10 @@ module ActiveModel
28
28
  private
29
29
 
30
30
  def cast_value(value)
31
- return value unless value.is_a?(::String)
31
+ return apply_seconds_precision(value) unless value.is_a?(::String)
32
32
  return if value.empty?
33
33
 
34
- if value.start_with?("2000-01-01")
35
- dummy_time_value = value
36
- else
37
- dummy_time_value = "2000-01-01 #{value}"
38
- end
34
+ dummy_time_value = value.sub(/\A(\d\d\d\d-\d\d-\d\d |)/, "2000-01-01 ")
39
35
 
40
36
  fast_string_to_time(dummy_time_value) || begin
41
37
  time_hash = ::Date._parse(dummy_time_value)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activemodel
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.0.rc1
4
+ version: 5.2.0.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-30 00:00:00.000000000 Z
11
+ date: 2018-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 5.2.0.rc1
19
+ version: 5.2.0.rc2
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 5.2.0.rc1
26
+ version: 5.2.0.rc2
27
27
  description: A toolkit for building modeling frameworks like Active Record. Rich support
28
28
  for attributes, callbacks, validations, serialization, internationalization, and
29
29
  testing.
@@ -100,8 +100,8 @@ homepage: http://rubyonrails.org
100
100
  licenses:
101
101
  - MIT
102
102
  metadata:
103
- source_code_uri: https://github.com/rails/rails/tree/v5.2.0.rc1/activemodel
104
- changelog_uri: https://github.com/rails/rails/blob/v5.2.0.rc1/activemodel/CHANGELOG.md
103
+ source_code_uri: https://github.com/rails/rails/tree/v5.2.0.rc2/activemodel
104
+ changelog_uri: https://github.com/rails/rails/blob/v5.2.0.rc2/activemodel/CHANGELOG.md
105
105
  post_install_message:
106
106
  rdoc_options: []
107
107
  require_paths:
@@ -118,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
118
118
  version: 1.3.1
119
119
  requirements: []
120
120
  rubyforge_project:
121
- rubygems_version: 2.7.3
121
+ rubygems_version: 2.7.6
122
122
  signing_key:
123
123
  specification_version: 4
124
124
  summary: A toolkit for building modeling frameworks (part of Rails).