ta_default_value_for 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2008, 2009, 2010, 2011 Phusion
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module DefaultValueFor
22
+ def self.initialize_railtie
23
+ ActiveSupport.on_load :active_record do
24
+ DefaultValueFor.initialize_active_record_extensions
25
+ end
26
+ end
27
+
28
+ def self.initialize_active_record_extensions
29
+ ActiveRecord::Base.extend(DefaultValueFor::ClassMethods)
30
+ end
31
+
32
+ class Railtie < Rails::Railtie
33
+ initializer 'default_value_for.insert_into_active_record' do
34
+ DefaultValueFor.initialize_railtie
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,201 @@
1
+ # Copyright (c) 2008-2012 Phusion
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module DefaultValueFor
22
+ class NormalValueContainer
23
+ def initialize(value)
24
+ @value = value
25
+ end
26
+
27
+ def evaluate(instance)
28
+ if @value.duplicable?
29
+ return @value.dup
30
+ else
31
+ return @value
32
+ end
33
+ end
34
+ end
35
+
36
+ class BlockValueContainer
37
+ def initialize(block)
38
+ @block = block
39
+ end
40
+
41
+ def evaluate(instance)
42
+ if @block.arity == 0
43
+ return @block.call
44
+ else
45
+ return @block.call(instance)
46
+ end
47
+ end
48
+ end
49
+
50
+ module ClassMethods
51
+ # Declares a default value for the given attribute.
52
+ #
53
+ # Sets the default value to the given options parameter unless the given options equal { :value => ... }
54
+ #
55
+ # The <tt>options</tt> can be used to specify the following things:
56
+ # * <tt>value</tt> - Sets the default value.
57
+ # * <tt>allows_nil (default: true)</tt> - Sets explicitly passed nil values if option is set to true.
58
+ def default_value_for(attribute, options = {}, &block)
59
+ value = options
60
+ allows_nil = true
61
+
62
+ if options.is_a?(Hash)
63
+ opts = options.stringify_keys
64
+ value = opts.fetch('value', options)
65
+ allows_nil = opts.fetch('allows_nil', true)
66
+ end
67
+
68
+ if !method_defined?(:set_default_values)
69
+ include(InstanceMethods)
70
+
71
+ after_initialize :set_default_values
72
+
73
+ class_attribute :_default_attribute_values
74
+ class_attribute :_default_attribute_values_not_allowing_nil
75
+
76
+ extend(DelayedClassMethods)
77
+ init_hash = true
78
+ else
79
+ init_hash = !singleton_methods(false).include?(:_default_attribute_values)
80
+ end
81
+
82
+ if init_hash
83
+ self._default_attribute_values = {}
84
+ self._default_attribute_values_not_allowing_nil = []
85
+ end
86
+
87
+ if block_given?
88
+ container = BlockValueContainer.new(block)
89
+ else
90
+ container = NormalValueContainer.new(value)
91
+ end
92
+ _default_attribute_values[attribute.to_s] = container
93
+ _default_attribute_values_not_allowing_nil << attribute.to_s unless allows_nil
94
+ end
95
+
96
+ def default_values(values)
97
+ values.each_pair do |key, options|
98
+ options = options.stringify_keys if options.is_a?(Hash)
99
+
100
+ value = options.is_a?(Hash) && options.has_key?('value') ? options['value'] : options
101
+
102
+ if value.kind_of? Proc
103
+ default_value_for(key, options.is_a?(Hash) ? options : {}, &value)
104
+ else
105
+ default_value_for(key, options)
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ module DelayedClassMethods
112
+ def _all_default_attribute_values
113
+ return _default_attribute_values unless superclass.respond_to?(:_default_attribute_values)
114
+ superclass._all_default_attribute_values.merge(_default_attribute_values)
115
+ end
116
+
117
+ def _all_default_attribute_values_not_allowing_nil
118
+ return _default_attribute_values_not_allowing_nil unless superclass.respond_to?(:_default_attribute_values_not_allowing_nil)
119
+ result = superclass._all_default_attribute_values_not_allowing_nil + _default_attribute_values_not_allowing_nil
120
+ result.uniq!
121
+ result
122
+ end
123
+ end
124
+
125
+ module InstanceMethods
126
+ def initialize(attributes = nil, options = {})
127
+ attributes = attributes.to_h if attributes.respond_to?(:to_h)
128
+ @initialization_attributes = attributes.is_a?(Hash) ? attributes.stringify_keys : {}
129
+
130
+ unless options[:without_protection]
131
+ if respond_to?(:mass_assignment_options, true) && options.has_key?(:as)
132
+ @initialization_attributes = sanitize_for_mass_assignment(@initialization_attributes, options[:as])
133
+ elsif respond_to?(:sanitize_for_mass_assignment, true)
134
+ @initialization_attributes = sanitize_for_mass_assignment(@initialization_attributes)
135
+ else
136
+ @initialization_attributes = remove_attributes_protected_from_mass_assignment(@initialization_attributes)
137
+ end
138
+ end
139
+
140
+ if self.class.respond_to? :protected_attributes
141
+ super(attributes, options)
142
+ else
143
+ super(attributes)
144
+ end
145
+ end
146
+
147
+ def attributes_for_create(attribute_names)
148
+ attribute_names += self.class._all_default_attribute_values.keys.map(&:to_s).find_all { |name|
149
+ self.class.columns_hash.key?(name)
150
+ }
151
+ super
152
+ end
153
+
154
+ def set_default_values
155
+ self.class._all_default_attribute_values.each do |attribute, container|
156
+ next unless new_record? || self.class._all_default_attribute_values_not_allowing_nil.include?(attribute)
157
+
158
+ connection_default_value_defined = new_record? && respond_to?("#{attribute}_changed?") && !__send__("#{attribute}_changed?")
159
+
160
+ attribute_blank = if attributes.has_key?(attribute)
161
+ column = self.class.columns_hash[attribute]
162
+ if column && column.type == :boolean
163
+ attributes[attribute].nil?
164
+ else
165
+ attributes[attribute].blank?
166
+ end
167
+ elsif respond_to?(attribute)
168
+ send(attribute).nil?
169
+ else
170
+ instance_variable_get("@#{attribute}").nil?
171
+ end
172
+ next unless connection_default_value_defined || attribute_blank
173
+
174
+ # allow explicitly setting nil through allow nil option
175
+ next if @initialization_attributes.is_a?(Hash) &&
176
+ (
177
+ @initialization_attributes.has_key?(attribute) ||
178
+ (
179
+ @initialization_attributes.has_key?("#{attribute}_attributes") &&
180
+ nested_attributes_options.stringify_keys[attribute]
181
+ )
182
+ ) &&
183
+ !self.class._all_default_attribute_values_not_allowing_nil.include?(attribute)
184
+
185
+ __send__("#{attribute}=", container.evaluate(self))
186
+ if respond_to?(:clear_attribute_changes, true)
187
+ clear_attribute_changes [attribute] if has_attribute?(attribute)
188
+ else
189
+ changed_attributes.delete(attribute)
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
195
+
196
+ if defined?(Rails::Railtie)
197
+ require 'default_value_for/railtie'
198
+ else
199
+ # For anybody is using AS and AR without Railties, i.e. Padrino.
200
+ ActiveRecord::Base.extend(DefaultValueFor::ClassMethods)
201
+ end
data/test.rb ADDED
@@ -0,0 +1,461 @@
1
+ # Copyright (c) 2008-2012 Phusion
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'bundler/setup'
22
+ require 'minitest/autorun'
23
+ require 'minitest/around/unit'
24
+ require 'active_record'
25
+ require 'action_pack'
26
+
27
+ if ActiveSupport::VERSION::MAJOR == 3
28
+ require 'active_support/core_ext/logger'
29
+ end
30
+
31
+ if ActionPack::VERSION::MAJOR > 3
32
+ require 'action_controller'
33
+ end
34
+
35
+ # Handle an edge-case when using Arel 5 (i.e. Rails <= 4.1) with Ruby >= 2.4:
36
+ # See: https://github.com/rails/arel/commit/dc85a6e9c74942945ad696f5da4d82490a85b865
37
+ # See: https://stackoverflow.com/a/51481088
38
+ rails_match_data = RUBY_VERSION.match(/\A(?<major>\d+).(?<minor>\d+)/)
39
+ rails_2_4_or_newer = rails_match_data[:major].to_i > 2 || (rails_match_data[:major].to_i == 2 && rails_match_data[:minor].to_i >= 4)
40
+ arel_match_data = Arel::VERSION.match(/\A(?<major>\d+).(?<minor>\d+)/)
41
+ arel_older_than_7_1 = arel_match_data[:major].to_i < 7 || (arel_match_data[:major].to_i == 7 && arel_match_data[:minor].to_i < 1)
42
+
43
+ if rails_2_4_or_newer && arel_older_than_7_1
44
+ module Arel
45
+ module Visitors
46
+ class DepthFirst < Arel::Visitors::Visitor
47
+ alias :visit_Integer :terminal
48
+ end
49
+
50
+ class Dot < Arel::Visitors::Visitor
51
+ alias :visit_Integer :visit_String
52
+ end
53
+
54
+ # The super class for ToSql changed with Arel 6
55
+ # See: https://github.com/rails/arel/commit/a6a7c75ff486657909e20e2f48764136caa5e87e#diff-3538aead5b80677372eea0e903ff728eR7
56
+ class ToSql < (Arel::VERSION[/\A\d+/].to_i >= 6 ? Arel::Visitors::Reduce : Arel::Visitors::Visitor)
57
+ alias :visit_Integer :literal
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ begin
64
+ TestCaseClass = MiniTest::Test
65
+ rescue NameError
66
+ TestCaseClass = MiniTest::Unit::TestCase
67
+ end
68
+
69
+ require 'default_value_for'
70
+
71
+ puts "\nTesting with Active Record version #{ActiveRecord::VERSION::STRING}"
72
+ puts "\nTesting with Action Pack version #{ActionPack::VERSION::STRING}\n\n"
73
+
74
+ ActiveRecord::Base.default_timezone = :local
75
+ ActiveRecord::Base.logger = Logger.new(STDERR)
76
+ ActiveRecord::Base.logger.level = Logger::WARN
77
+
78
+ ActiveRecord::Base.establish_connection(
79
+ :adapter => RUBY_PLATFORM == 'java' ? 'jdbcsqlite3' : 'sqlite3',
80
+ :database => ':memory:'
81
+ )
82
+
83
+ ActiveRecord::Base.connection.create_table(:users, :force => true) do |t|
84
+ t.string :username
85
+ t.integer :default_number
86
+ t.text :settings
87
+ end
88
+
89
+ ActiveRecord::Base.connection.create_table(:books, :force => true) do |t|
90
+ t.string :type
91
+ t.integer :number
92
+ t.integer :count, :null => false, :default => 1
93
+ t.integer :user_id
94
+ t.timestamp :timestamp
95
+ t.text :stuff
96
+ t.boolean :flag
97
+ end
98
+
99
+ if defined?(Rails::Railtie)
100
+ DefaultValueFor.initialize_railtie
101
+ DefaultValueFor.initialize_active_record_extensions
102
+ end
103
+
104
+ class DefaultValuePluginTest < TestCaseClass
105
+ def around
106
+ Object.const_set(:User, Class.new(ActiveRecord::Base))
107
+ Object.const_set(:Book, Class.new(ActiveRecord::Base))
108
+ Object.const_set(:Novel, Class.new(Book))
109
+ User.has_many :books
110
+ Book.belongs_to :user
111
+
112
+ ActiveRecord::Base.transaction do
113
+ yield
114
+ raise ActiveRecord::Rollback
115
+ end
116
+ ensure
117
+ Object.send(:remove_const, :User)
118
+ Object.send(:remove_const, :Book)
119
+ Object.send(:remove_const, :Novel)
120
+ ActiveSupport::Dependencies.clear unless ActiveSupport::VERSION::MAJOR > 6
121
+ end
122
+
123
+ def test_default_value_on_attribute_methods
124
+ Book.class_eval do
125
+ serialize :stuff
126
+ default_value_for :color, :green
127
+ def color; (self.stuff || {})[:color]; end
128
+ def color=(val)
129
+ self.stuff ||= {}
130
+ self.stuff[:color] = val
131
+ end
132
+ end
133
+ assert_equal :green, Book.create.color
134
+ end
135
+
136
+ def test_default_value_can_be_passed_as_argument
137
+ Book.default_value_for(:number, 1234)
138
+ assert_equal 1234, Book.new.number
139
+ end
140
+
141
+ def test_default_value_can_be_passed_as_block
142
+ Book.default_value_for(:number) { 1234 }
143
+ assert_equal 1234, Book.new.number
144
+ end
145
+
146
+ def test_works_with_create
147
+ Book.default_value_for :number, 1234
148
+
149
+ object = Book.create
150
+ refute_nil Book.find_by_number(1234)
151
+
152
+ # allows nil for existing records
153
+ object.update_attribute(:number, nil)
154
+ assert_nil Book.find_by_number(1234)
155
+ assert_nil Book.find(object.id).number
156
+ end
157
+
158
+ def test_does_not_allow_nil_sets_default_value_on_existing_nils
159
+ Book.default_value_for(:number, :allows_nil => false) { 1234 }
160
+ object = Book.create
161
+ object.update_attribute(:number, nil)
162
+ assert_nil Book.find_by_number(1234)
163
+ assert_equal 1234, Book.find(object.id).number
164
+ end
165
+
166
+ def test_overwrites_db_default
167
+ Book.default_value_for :count, 1234
168
+ assert_equal 1234, Book.new.count
169
+ end
170
+
171
+ def test_doesnt_overwrite_values_provided_by_mass_assignment
172
+ Book.default_value_for :number, 1234
173
+ assert_equal 1, Book.new(:number => 1, :count => 2).number
174
+ end
175
+
176
+ def test_doesnt_overwrite_values_provided_by_multiparameter_assignment
177
+ Book.default_value_for :timestamp, Time.mktime(2000, 1, 1)
178
+ timestamp = Time.mktime(2009, 1, 1)
179
+ object = Book.new('timestamp(1i)' => '2009', 'timestamp(2i)' => '1', 'timestamp(3i)' => '1')
180
+ assert_equal timestamp, object.timestamp
181
+ end
182
+
183
+ def test_doesnt_overwrite_values_provided_by_constructor_block
184
+ Book.default_value_for :number, 1234
185
+ object = Book.new do |x|
186
+ x.number = 1
187
+ x.count = 2
188
+ end
189
+ assert_equal 1, object.number
190
+ end
191
+
192
+ def test_doesnt_overwrite_explicitly_provided_nil_values_in_mass_assignment
193
+ Book.default_value_for :number, 1234
194
+ assert_nil Book.new(:number => nil).number
195
+ end
196
+
197
+ def test_overwrites_explicitly_provided_nil_values_in_mass_assignment
198
+ Book.default_value_for :number, :value => 1234, :allows_nil => false
199
+ assert_equal 1234, Book.new(:number => nil).number
200
+ end
201
+
202
+ def test_default_values_are_inherited
203
+ Book.default_value_for :number, 1234
204
+ assert_equal 1234, Novel.new.number
205
+ end
206
+
207
+ def test_default_values_in_superclass_are_saved_in_subclass
208
+ Book.default_value_for :number, 1234
209
+ Novel.default_value_for :flag, true
210
+ object = Novel.create!
211
+ assert_equal object.id, Novel.find_by_number(1234).id
212
+ assert_equal object.id, Novel.find_by_flag(true).id
213
+ end
214
+
215
+ def test_default_values_in_subclass
216
+ Novel.default_value_for :number, 5678
217
+ assert_equal 5678, Novel.new.number
218
+ assert_nil Book.new.number
219
+ end
220
+
221
+ def test_multiple_default_values_in_subclass_with_default_values_in_parent_class
222
+ Book.class_eval do
223
+ default_value_for :other_number, nil
224
+ attr_accessor :other_number
225
+ end
226
+ Novel.default_value_for :number, 5678
227
+
228
+ # Ensure second call in this class doesn't reset _default_attribute_values,
229
+ # and also doesn't consider the parent class' _default_attribute_values when
230
+ # making that check.
231
+ Novel.default_value_for :user_id, 9999
232
+
233
+ object = Novel.new
234
+ assert_nil object.other_number
235
+ assert_equal 5678, object.number
236
+ assert_equal 9999, object.user_id
237
+ end
238
+
239
+ def test_override_default_values_in_subclass
240
+ Book.default_value_for :number, 1234
241
+ Novel.default_value_for :number, 5678
242
+ assert_equal 5678, Novel.new.number
243
+ assert_equal 1234, Book.new.number
244
+ end
245
+
246
+ def test_default_values_in_subclass_do_not_affect_parent_class
247
+ Book.default_value_for :number, 1234
248
+ Novel.class_eval do
249
+ default_value_for :hello, "hi"
250
+ attr_accessor :hello
251
+ end
252
+
253
+ assert Book.new
254
+ assert !Book._default_attribute_values.include?(:hello)
255
+ end
256
+
257
+ def test_doesnt_set_default_on_saved_records
258
+ Book.create(:number => 9876)
259
+ Book.default_value_for :number, 1234
260
+ assert_equal 9876, Book.first.number
261
+ end
262
+
263
+ def test_also_works_on_attributes_that_arent_database_columns
264
+ Book.class_eval do
265
+ default_value_for :hello, "hi"
266
+ attr_accessor :hello
267
+ end
268
+ assert_equal 'hi', Book.new.hello
269
+ end
270
+
271
+ def test_works_on_attributes_that_only_have_writers
272
+ Book.class_eval do
273
+ default_value_for :hello, "hi"
274
+ attr_writer :hello
275
+ end
276
+ assert_equal 'hi', Book.new.instance_variable_get('@hello')
277
+ end
278
+
279
+ def test_doesnt_conflict_with_overrided_initialize_method_in_model_class
280
+ Book.class_eval do
281
+ def initialize(attrs = {})
282
+ @initialized = true
283
+ super(:count => 5678)
284
+ end
285
+
286
+ default_value_for :number, 1234
287
+ end
288
+ object = Book.new
289
+ assert_equal 1234, object.number
290
+ assert_equal 5678, object.count
291
+ assert object.instance_variable_get('@initialized')
292
+ end
293
+
294
+ def test_model_instance_is_passed_to_the_given_block
295
+ instance = nil
296
+ Book.default_value_for :number do |n|
297
+ instance = n
298
+ end
299
+ object = Book.new
300
+ assert_same object.object_id, instance.object_id
301
+ end
302
+
303
+ def test_can_specify_default_value_via_association
304
+ user = User.create(:username => 'Kanako', :default_number => 123)
305
+ Book.default_value_for :number do |n|
306
+ n.user.default_number
307
+ end
308
+ assert_equal 123, user.books.create!.number
309
+ end
310
+
311
+ def test_default_values
312
+ Book.default_values({
313
+ :type => "normal",
314
+ :number => lambda { 10 + 5 },
315
+ :timestamp => lambda {|_| Time.now }
316
+ })
317
+
318
+ object = Book.new
319
+ assert_equal("normal", object.type)
320
+ assert_equal(15, object.number)
321
+ end
322
+
323
+ def test_default_value_order
324
+ Book.default_value_for :count, 5
325
+ Book.default_value_for :number do |this|
326
+ this.count * 2
327
+ end
328
+ object = Book.new
329
+ assert_equal(5, object.count)
330
+ assert_equal(10, object.number)
331
+ end
332
+
333
+ def test_attributes_with_default_values_are_not_marked_as_changed
334
+ Book.default_value_for :count, 5
335
+ Book.default_value_for :number, 2
336
+
337
+ object = Book.new
338
+ assert(!object.changed?)
339
+ assert_equal([], object.changed)
340
+
341
+ object.type = "foo"
342
+ assert(object.changed?)
343
+ assert_equal(["type"], object.changed)
344
+ end
345
+
346
+ def test_default_values_are_duplicated
347
+ User.default_value_for :username, "hello"
348
+ user1 = User.new
349
+ user1.username << " world"
350
+ user2 = User.new
351
+ assert_equal("hello", user2.username)
352
+ end
353
+
354
+ def test_default_values_are_shallow_copied
355
+ User.class_eval do
356
+ attr_accessor :hash
357
+ default_value_for :hash, { 1 => [] }
358
+ end
359
+ user1 = User.new
360
+ user1.hash[1] << 1
361
+ user2 = User.new
362
+ assert_equal([1], user2.hash[1])
363
+ end
364
+
365
+ def test_constructor_does_not_affect_the_hash_passed_to_it
366
+ Book.default_value_for :count, 5
367
+ options = { :count => 5, :user_id => 1 }
368
+ options_dup = options.dup
369
+ Book.new(options)
370
+ assert_equal(options_dup, options)
371
+ end
372
+
373
+ def test_subclass_find
374
+ Book.default_value_for :number, 5678
375
+ n = Novel.create
376
+ assert Novel.find(n.id)
377
+ end
378
+
379
+ def test_does_not_see_false_as_blank_at_boolean_columns_for_existing_records
380
+ Book.default_value_for(:flag, :allows_nil => false) { true }
381
+
382
+ object = Book.create
383
+
384
+ # allows nil for existing records
385
+ object.update_attribute(:flag, false)
386
+ assert_equal false, Book.find(object.id).flag
387
+ end
388
+
389
+ def test_works_with_nested_attributes
390
+ User.accepts_nested_attributes_for :books
391
+ User.default_value_for :books do
392
+ [Book.create!(:number => 0)]
393
+ end
394
+
395
+ user = User.create! :books_attributes => [{:number => 1}]
396
+ assert_equal 1, Book.all.first.number
397
+ end
398
+
399
+ def test_works_with_stored_attribute_accessors_when_initializing_value_that_does_not_allow_nil
400
+ User.store :settings, :accessors => :bio
401
+ User.default_value_for :bio, :value => 'None given', :allows_nil => false
402
+
403
+ user = User.create!(:bio => 'This is a bio')
404
+ assert_equal 'This is a bio', user.bio
405
+ end
406
+
407
+ if ActionPack::VERSION::MAJOR > 3
408
+ def test_doesnt_overwrite_explicitly_provided_nil_values_in_mass_assignment_with_action_controller_parameters
409
+ Book.default_value_for :number, 1234
410
+
411
+ assert_nil Book.new(ActionController::Parameters.new(:number => nil).permit!).number
412
+ end
413
+
414
+ def test_overwrites_explicitly_provided_nil_values_in_mass_assignment_with_action_controller_parameters
415
+ Book.default_value_for :number, :value => 1234, :allows_nil => false
416
+
417
+ assert_equal 1234, Book.new(ActionController::Parameters.new(:number => nil).permit!).number
418
+ end
419
+
420
+ def test_works_with_nested_attributes_with_action_controller_parameters
421
+ User.accepts_nested_attributes_for :books
422
+ User.default_value_for :books do
423
+ [Book.create!(:number => 0)]
424
+ end
425
+
426
+ user = User.create!(ActionController::Parameters.new(:books_attributes => [{:number => 1}]).permit!)
427
+ assert_equal 1, Book.all.first.number
428
+ end
429
+
430
+ def test_works_with_stored_attribute_accessors_when_initializing_value_that_does_not_allow_nil_with_action_controller_parameters
431
+ User.store :settings, :accessors => :bio
432
+ User.default_value_for :bio, :value => 'None given', :allows_nil => false
433
+
434
+ user = User.create!(ActionController::Parameters.new(:bio => 'This is a bio').permit!)
435
+ assert_equal 'This is a bio', user.bio
436
+ end
437
+ end
438
+
439
+ if ActiveRecord::VERSION::MAJOR == 3
440
+ def test_constructor_ignores_forbidden_mass_assignment_attributes
441
+ Book.class_eval do
442
+ default_value_for :number, 1234
443
+ attr_protected :number
444
+ end
445
+ object = Book.new(:number => 5678, :count => 987)
446
+ assert_equal 1234, object.number
447
+ assert_equal 987, object.count
448
+ end
449
+
450
+ def test_constructor_respects_without_protection_option
451
+ Book.class_eval do
452
+ default_value_for :number, 1234
453
+ attr_protected :number
454
+ end
455
+
456
+ object = Book.create!({:number => 5678, :count => 987}, :without_protection => true)
457
+ assert_equal 5678, object.number
458
+ assert_equal 987, object.count
459
+ end
460
+ end
461
+ end