gitlab-default_value_for 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c5c1b646ba7c10f4c7ddd6efb0b1c3014b534a4883b88a0e420c0ab559c3657b
4
+ data.tar.gz: 2b92a9ff4f14621d358c9f46923ee265d3babe1862d9c190faa5fa2c679a85d1
5
+ SHA512:
6
+ metadata.gz: 760b46b966f08f131801be9c11d9270d4f393cc96a67d5e7e50f476699a44f79cb53ba85bc22582b0c51b598ef08aa2a331e8c7512b418cab88a807b06c3b15f
7
+ data.tar.gz: a3143b0bbb044e9bb44354fce5b6c0f68f1ae6a9680701d67ae21d8bd758ab72fc712a049ff825f20164ddf4cb59e80aecdbb644b48ba5883153a8b2e7de8bd5
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008, 2009, 2010 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.
@@ -0,0 +1,558 @@
1
+ # Introduction
2
+
3
+ The default_value_for plugin allows one to define default values for ActiveRecord
4
+ models in a declarative manner. For example:
5
+
6
+ ```ruby
7
+ class User < ActiveRecord::Base
8
+ default_value_for :name, "(no name)"
9
+ default_value_for :last_seen do
10
+ Time.now
11
+ end
12
+ end
13
+
14
+ u = User.new
15
+ u.name # => "(no name)"
16
+ u.last_seen # => Mon Sep 22 17:28:38 +0200 2008
17
+ ```
18
+
19
+ *Note*: critics might be interested in the "When (not) to use default_value_for?" section. Please read on.
20
+
21
+ ## Installation
22
+
23
+ ### Rails 3.2 - 4.2 / Ruby 1.9.3 and higher
24
+
25
+ The current version of default_value_for (3.0.x) is compatible with Rails 3.2 or higher, and Ruby 1.9.3 and higher.
26
+
27
+ Add it to your Gemfile:
28
+
29
+ ```ruby
30
+ gem "default_value_for", "~> 3.0.0"
31
+ ```
32
+
33
+ This gem is signed using PGP with the Phusion Software Signing key: http://www.phusion.nl/about/gpg. That key in turn is signed by the rubygems-openpgp Certificate Authority: http://www.rubygems-openpgp-ca.org/.
34
+
35
+ You can verify the authenticity of the gem by following The Complete Guide to Verifying Gems with rubygems-openpgp: http://www.rubygems-openpgp-ca.org/blog/the-complete-guide-to-verifying-gems-with-rubygems-openpgp.html
36
+
37
+ ## Rails 3.0 - 3.1 / Ruby 1.9.3 and lower
38
+
39
+ To use default_value_for with older versions of Ruby and Rails, you must use the previous stable release, 2.0.3. This version works with Rails 3.0, 3.1, and 3.2; and Ruby 1.8.7 and higher. It **does not** work with Rails 4.
40
+
41
+ ```ruby
42
+ gem "default_value_for", "~> 2.0.3"
43
+ ```
44
+
45
+ ### Rails 2
46
+
47
+ To use default_value_for with Rails 2.x you must use an older version:
48
+
49
+ ```shell
50
+ ./script/plugin install git://github.com/FooBarWidget/default_value_for.git -r release-1.0.7
51
+ ```
52
+
53
+ ## The default_value_for method
54
+
55
+ The `default_value_for` method is available in all ActiveRecord model classes.
56
+
57
+ The first argument is the name of the attribute for which a default value should
58
+ be set. This may either be a Symbol or a String.
59
+
60
+ The default value itself may either be passed as the second argument:
61
+
62
+ ```ruby
63
+ default_value_for :age, 20
64
+ ```
65
+
66
+ ...or it may be passed as the return value of a block:
67
+
68
+ ```ruby
69
+ default_value_for :age do
70
+ if today_is_sunday?
71
+ 20
72
+ else
73
+ 30
74
+ end
75
+ end
76
+ ```
77
+
78
+ If you pass a value argument, then the default value is static and never changes. However, if you pass a block, then the default value is retrieved by calling the block. This block is called not once, but every time a new record is instantiated and default values need to be filled in.
79
+
80
+ The latter form is especially useful if your model has a UUID column. One can generate a new, random UUID for every newly instantiated record:
81
+
82
+ ```ruby
83
+ class User < ActiveRecord::Base
84
+ default_value_for :uuid do
85
+ UuidGenerator.new.generate_uuid
86
+ end
87
+ end
88
+
89
+ User.new.uuid # => "51d6d6846f1d1b5c9a...."
90
+ User.new.uuid # => "ede292289e3484cb88...."
91
+ ```
92
+
93
+ Note that record is passed to the block as an argument, in case you need it for whatever reason:
94
+
95
+ ```ruby
96
+ class User < ActiveRecord::Base
97
+ default_value_for :uuid do |x|
98
+ x # <--- a User object
99
+ UuidGenerator.new.generate_uuid
100
+ end
101
+ end
102
+ ```
103
+
104
+ ## default_value_for options
105
+
106
+ * allows_nil (default: true) - Sets explicitly passed nil values if option is set to true.
107
+
108
+ You can pass this options hash as 2nd parameter and have to pass the default value through the :value option in this case e.g.:
109
+
110
+ ```ruby
111
+ default_value_for :age, :value => 20, :allows_nil => false
112
+ ```
113
+
114
+ You can still pass the default value through a block:
115
+
116
+ ```ruby
117
+ default_value_for :uuid, :allows_nil => false do
118
+ UuidGenerator.new.generate_uuid
119
+ end
120
+ ````
121
+
122
+ ## The default_values method
123
+
124
+ As a shortcut, you can use +default_values+ to set multiple default values at once.
125
+
126
+ ```ruby
127
+ default_values :age => 20,
128
+ :uuid => lambda { UuidGenerator.new.generate_uuid }
129
+ ```
130
+
131
+ If you like to override default_value_for options for each attribute you can do so:
132
+
133
+ ```ruby
134
+ default_values :age => { :value => 20 },
135
+ :uuid => { :value => lambda { UuidGenerator.new.generate_uuid }, :allows_nil => false }
136
+ ```
137
+
138
+ The difference is purely aesthetic. If you have lots of default values which are constants or constructed with one-line blocks, +default_values+ may look nicer. If you have default values constructed by longer blocks, `default_value_for` suit you better. Feel free to mix and match.
139
+
140
+ As a side note, due to specifics of Ruby's parser, you cannot say,
141
+
142
+ ```ruby
143
+ default_value_for :uuid { UuidGenerator.new.generate_uuid }
144
+ ```
145
+
146
+ because it will not parse. One needs to write
147
+
148
+ ```ruby
149
+ default_value_for(:uuid) { UuidGenerator.new.generate_uuid }
150
+ ```
151
+
152
+ instead. This is in part the inspiration for the +default_values+ syntax.
153
+
154
+ ## Rules
155
+
156
+ ### Instantiation of new record
157
+
158
+ Upon instantiating a new record, the declared default values are filled into
159
+ the record. You've already seen this in the above examples.
160
+
161
+ ### Retrieval of existing record
162
+
163
+ Upon retrieving an existing record in the following case, the declared default values are _not_ filled into the record. Consider the example with the UUID:
164
+
165
+ ```ruby
166
+ user = User.create
167
+ user.uuid # => "529c91b8bbd3e..."
168
+
169
+ user = User.find(user.id)
170
+ # UUID remains unchanged because it's retrieved from the database!
171
+ user.uuid # => "529c91b8bbd3e..."
172
+ ```
173
+
174
+ But when the declared default value is set to not allow nil and nil is passed the default values will be set on retrieval.
175
+ Consider this example:
176
+
177
+ ```ruby
178
+ default_value_for(:number, :allows_nil => false) { 123 }
179
+
180
+ user = User.create
181
+
182
+ # manual SQL by-passing active record and the default value for gem logic through ActiveRecord's after_initialize callback
183
+ user.update_attribute(:number, nil)
184
+
185
+ # declared default value should be set
186
+ User.find(user.id).number # => 123 # = declared default value
187
+ ```
188
+
189
+ ### Mass-assignment
190
+
191
+ If a certain attribute is being assigned via the model constructor's
192
+ mass-assignment argument, that the default value for that attribute will _not_
193
+ be filled in:
194
+
195
+ ```ruby
196
+ user = User.new(:uuid => "hello")
197
+ user.uuid # => "hello"
198
+ ```
199
+
200
+ However, if that attribute is protected by +attr_protected+ or +attr_accessible+,
201
+ then it will be filled in:
202
+
203
+ ```ruby
204
+ class User < ActiveRecord::Base
205
+ default_value_for :name, 'Joe'
206
+ attr_protected :name
207
+ end
208
+
209
+ user = User.new(:name => "Jane")
210
+ user.name # => "Joe"
211
+
212
+ # the without protection option will work as expected
213
+ user = User.new({:name => "Jane"}, :without_protection => true)
214
+ user.name # => "Jane"
215
+ ```
216
+
217
+ Explicitly set nil values for accessible attributes will be accepted:
218
+
219
+ ```ruby
220
+ class User < ActiveRecord::Base
221
+ default_value_for :name, 'Joe'
222
+ end
223
+
224
+ user = User(:name => nil)
225
+ user.name # => nil
226
+
227
+ ... unless the accessible attribute is set to not allowing nil:
228
+
229
+ class User < ActiveRecord::Base
230
+ default_value_for :name, 'Joe', :allows_nil => false
231
+ end
232
+
233
+ user = User(:name => nil)
234
+ user.name # => "Joe"
235
+ ```
236
+
237
+ ### Inheritance
238
+
239
+ Inheritance works as expected. All default values are inherited by the child
240
+ class:
241
+
242
+ ```ruby
243
+ class User < ActiveRecord::Base
244
+ default_value_for :name, 'Joe'
245
+ end
246
+
247
+ class SuperUser < User
248
+ end
249
+
250
+ SuperUser.new.name # => "Joe"
251
+ ```
252
+
253
+ ### Attributes that aren't database columns
254
+
255
+ `default_value_for` also works with attributes that aren't database columns.
256
+ It works with anything for which there's an assignment method:
257
+
258
+ ```ruby
259
+ # Suppose that your 'users' table only has a 'name' column.
260
+ class User < ActiveRecord::Base
261
+ default_value_for :name, 'Joe'
262
+ default_value_for :age, 20
263
+ default_value_for :registering, true
264
+
265
+ attr_accessor :age
266
+
267
+ def registering=(value)
268
+ @registering = true
269
+ end
270
+ end
271
+
272
+ user = User.new
273
+ user.age # => 20
274
+ user.instance_variable_get('@registering') # => true
275
+ ```
276
+
277
+ ### Default values are duplicated
278
+
279
+ The given default values are duplicated when they are filled in, so if you mutate a value that was filled in with a default value, then it will not affect all subsequent default values:
280
+
281
+ ```ruby
282
+ class Author < ActiveRecord::Base
283
+ # This model only has a 'name' attribute.
284
+ end
285
+
286
+ class Book < ActiveRecord::Base
287
+ belongs_to :author
288
+
289
+ # By default, a Book belongs to a new, unsaved author.
290
+ default_value_for :author, Author.new
291
+ end
292
+
293
+ book1 = Book.new
294
+ book1.author.name # => nil
295
+ # This does not mutate the default value:
296
+ book1.author.name = "John"
297
+
298
+ book2 = Book.new
299
+ book2.author.name # => nil
300
+ ```
301
+
302
+ However the duplication is shallow. If you modify any objects that are referenced by the default value then it will affect subsequent default values:
303
+
304
+ ```ruby
305
+ class Author < ActiveRecord::Base
306
+ attr_accessor :useless_hash
307
+ default_value_for :useless_hash, { :foo => [] }
308
+ end
309
+
310
+ author1 = Author.new
311
+ author1.useless_hash # => { :foo => [] }
312
+ # This mutates the referred array:
313
+ author1.useless_hash[:foo] << 1
314
+
315
+ author2 = Author.new
316
+ author2.useless_hash # => { :foo => [1] }
317
+ ```
318
+
319
+ You can prevent this from happening by passing a block to `default_value_for`, which returns a new object instance with fresh references every time:
320
+
321
+ ```ruby
322
+ class Author < ActiveRecord::Base
323
+ attr_accessor :useless_hash
324
+ default_value_for :useless_hash do
325
+ { :foo => [] }
326
+ end
327
+ end
328
+
329
+ author1 = Author.new
330
+ author1.useless_hash # => { :foo => [] }
331
+ author1.useless_hash[:foo] << 1
332
+
333
+ author2 = Author.new
334
+ author2.useless_hash # => { :foo => [] }
335
+ ```
336
+
337
+ ### Caveats
338
+
339
+ A conflict can occur if your model class overrides the 'initialize' method, because this plugin overrides 'initialize' as well to do its job.
340
+
341
+ ```ruby
342
+ class User < ActiveRecord::Base
343
+ def initialize # <-- this constructor causes problems
344
+ super(:name => 'Name cannot be changed in constructor')
345
+ end
346
+ end
347
+ ```
348
+
349
+ We recommend you to alias chain your initialize method in models where you use `default_value_for`:
350
+
351
+ ```ruby
352
+ class User < ActiveRecord::Base
353
+ default_value_for :age, 20
354
+
355
+ def initialize_with_my_app
356
+ initialize_without_my_app(:name => 'Name cannot be changed in constructor')
357
+ end
358
+
359
+ alias_method_chain :initialize, :my_app
360
+ end
361
+ ```
362
+
363
+ Also, stick with the following rules:
364
+
365
+ * There is no need to +alias_method_chain+ your initialize method in models that don't use `default_value_for`.
366
+
367
+ * Make sure that +alias_method_chain+ is called *after* the last `default_value_for` occurrence.
368
+
369
+ If your default value is accidentally similar to default_value_for's options hash wrap your default value like this:
370
+
371
+ ```ruby
372
+ default_value_for :attribute_name, :value => { :value => 123, :other_value => 1234 }
373
+ ```
374
+
375
+ ## When (not) to use default_value_for?
376
+
377
+ You can also specify default values in the database schema. For example, you can specify a default value in a migration as follows:
378
+
379
+ ```ruby
380
+ create_table :users do |t|
381
+ t.string :username, :null => false, :default => 'default username'
382
+ t.integer :age, :null => false, :default => 20
383
+ end
384
+ ```
385
+
386
+ This has similar effects as passing the default value as the second argument to `default_value_for`:
387
+
388
+ ```ruby
389
+ default_value_for(:username, 'default_username')
390
+ default_value_for(:age, 20)
391
+ ```
392
+
393
+ Default values are filled in whether you use the schema defaults or the default_value_for defaults:
394
+
395
+ ```ruby
396
+ user = User.new
397
+ user.username # => 'default username'
398
+ user.age # => 20
399
+ ```
400
+
401
+ It's recommended that you use this over `default_value_for` whenever possible.
402
+
403
+ However, it's not possible to specify a schema default for serialized columns. With `default_value_for`, you can:
404
+
405
+ ```ruby
406
+ class User < ActiveRecord::Base
407
+ serialize :color
408
+ default_value_for :color, [255, 0, 0]
409
+ end
410
+ ```
411
+
412
+ And if schema defaults don't provide the flexibility that you need, then `default_value_for` is the perfect choice. For example, with `default_value_for` you could specify a per-environment default:
413
+
414
+ ```ruby
415
+ class User < ActiveRecord::Base
416
+ if Rails.env ## "development"
417
+ default_value_for :is_admin, true
418
+ end
419
+ end
420
+ ```
421
+
422
+ Or, as you've seen in an earlier example, you can use `default_value_for` to generate a default random UUID:
423
+
424
+ ```ruby
425
+ class User < ActiveRecord::Base
426
+ default_value_for :uuid do
427
+ UuidGenerator.new.generate_uuid
428
+ end
429
+ end
430
+ ```
431
+
432
+ Or you could use it to generate a timestamp that's relative to the time at which the record is instantiated:
433
+
434
+ ```ruby
435
+ class User < ActiveRecord::Base
436
+ default_value_for :account_expires_at do
437
+ 3.years.from_now
438
+ end
439
+ end
440
+
441
+ User.new.account_expires_at # => Mon Sep 22 18:43:42 +0200 2008
442
+ sleep(2)
443
+ User.new.account_expires_at # => Mon Sep 22 18:43:44 +0200 2008
444
+ ```
445
+
446
+ Finally, it's also possible to specify a default via an association:
447
+
448
+ ```ruby
449
+ # Has columns: 'name' and 'default_price'
450
+ class SuperMarket < ActiveRecord::Base
451
+ has_many :products
452
+ end
453
+
454
+ # Has columns: 'name' and 'price'
455
+ class Product < ActiveRecord::Base
456
+ belongs_to :super_market
457
+
458
+ default_value_for :price do |product|
459
+ product.super_market.default_price
460
+ end
461
+ end
462
+
463
+ super_market = SuperMarket.create(:name => 'Albert Zwijn', :default_price => 100)
464
+ soap = super_market.products.create(:name => 'Soap')
465
+ soap.price # => 100
466
+ ```
467
+
468
+ ### What about before_validate/before_save?
469
+
470
+ True, +before_validate+ and +before_save+ does what we want if we're only interested in filling in a default before saving. However, if one wants to be able to access the default value even before saving, then be prepared to write a lot of code. Suppose that we want to be able to access a new record's UUID, even before it's saved. We could end up with the following code:
471
+
472
+ ```ruby
473
+ # In the controller
474
+ def create
475
+ @user = User.new(params[:user])
476
+ @user.generate_uuid
477
+ email_report_to_admin("#{@user.username} with UUID #{@user.uuid} created.")
478
+ @user.save!
479
+ end
480
+
481
+ # Model
482
+ class User < ActiveRecord::Base
483
+ before_save :generate_uuid_if_necessary
484
+
485
+ def generate_uuid
486
+ self.uuid = ...
487
+ end
488
+
489
+ private
490
+ def generate_uuid_if_necessary
491
+ if uuid.blank?
492
+ generate_uuid
493
+ end
494
+ end
495
+ end
496
+ ```
497
+
498
+ The need to manually call +generate_uuid+ here is ugly, and one can easily forget to do that. Can we do better? Let's see:
499
+
500
+ ```ruby
501
+ # Controller
502
+ def create
503
+ @user = User.new(params[:user])
504
+ email_report_to_admin("#{@user.username} with UUID #{@user.uuid} created.")
505
+ @user.save!
506
+ end
507
+
508
+ # Model
509
+ class User < ActiveRecord::Base
510
+ before_save :generate_uuid_if_necessary
511
+
512
+ def uuid
513
+ value = read_attribute('uuid')
514
+ if !value
515
+ value = generate_uuid
516
+ write_attribute('uuid', value)
517
+ end
518
+ value
519
+ end
520
+
521
+ # We need to override this too, otherwise User.new.attributes won't return
522
+ # a default UUID value. I've never tested with User.create() so maybe we
523
+ # need to override even more things.
524
+ def attributes
525
+ uuid
526
+ super
527
+ end
528
+
529
+ private
530
+ def generate_uuid_if_necessary
531
+ uuid # Reader method automatically generates UUID if it doesn't exist
532
+ end
533
+ end
534
+ ```
535
+
536
+ That's an awful lot of code. Using `default_value_for` is easier, don't you think?
537
+
538
+ ### What about other plugins?
539
+
540
+ I've only been able to find 2 similar plugins:
541
+
542
+ * Default Value: http://agilewebdevelopment.com/plugins/default_value
543
+
544
+ * ActiveRecord Defaults: http://agilewebdevelopment.com/plugins/activerecord_defaults
545
+
546
+ 'Default Value' appears to be unmaintained; its SVN link is broken. This leaves only 'ActiveRecord Defaults'. However, it is semantically dubious, which leaves it wide open for corner cases. For example, it is not clearly specified what ActiveRecord Defaults will do when attributes are protected by +attr_protected+ or +attr_accessible+. It is also not clearly specified what one is supposed to do if one needs a custom +initialize+ method in the model.
547
+
548
+ I've taken my time to thoroughly document default_value_for's behavior.
549
+
550
+ ## Credits
551
+
552
+ I've wanted such functionality for a while now and it baffled me that ActiveRecord doesn't provide a clean way for me to specify default values. After reading http://groups.google.com/group/rubyonrails-core/browse_thread/thread/b509a2fe2b62ac5/3e8243fa1954a935, it became clear that someone needs to write a plugin. This is the result.
553
+
554
+ Thanks to Pratik Naik for providing the initial code snippet on which this plugin is based on: http://m.onkey.org/2007/7/24/how-to-set-default-values-in-your-model
555
+
556
+ Thanks to Matthew Draper for Rails 5 support.
557
+
558
+ Thanks to Norman Clarke and Tom Mango for Rails 4 support.