default_value_for 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +39 -24
- data/Rakefile +0 -21
- data/default_value_for.gemspec +10 -0
- data/init.rb +99 -1
- data/test.rb +14 -2
- metadata +24 -14
- data/.gitignore +0 -1
- data/VERSION +0 -1
data/README.rdoc
CHANGED
@@ -163,11 +163,11 @@ It works with anything for which there's an assignment method:
|
|
163
163
|
user.age # => 20
|
164
164
|
user.instance_variable_get('@registering') # => true
|
165
165
|
|
166
|
-
=== Default values are
|
166
|
+
=== Default values are duplicated
|
167
167
|
|
168
|
-
The given default values are
|
169
|
-
you mutate a value that was filled in with a default value, then it will
|
170
|
-
all subsequent default values:
|
168
|
+
The given default values are duplicated when they are filled in, so if
|
169
|
+
you mutate a value that was filled in with a default value, then it will
|
170
|
+
not affect all subsequent default values:
|
171
171
|
|
172
172
|
class Author < ActiveRecord::Base
|
173
173
|
# This model only has a 'name' attribute.
|
@@ -182,33 +182,44 @@ all subsequent default values:
|
|
182
182
|
|
183
183
|
book1 = Book.new
|
184
184
|
book1.author.name # => nil
|
185
|
-
# This
|
185
|
+
# This does not mutate the default value:
|
186
186
|
book1.author.name = "John"
|
187
187
|
|
188
188
|
book2 = Book.new
|
189
|
-
book2.author.name # =>
|
189
|
+
book2.author.name # => nil
|
190
|
+
|
191
|
+
However the duplication is shallow. If you modify any objects that are
|
192
|
+
referenced by the default value then it will affect subsequent default values:
|
193
|
+
|
194
|
+
class Author < ActiveRecord::Base
|
195
|
+
attr_accessor :useless_hash
|
196
|
+
default_value_for :useless_hash, { :foo => [] }
|
197
|
+
end
|
198
|
+
|
199
|
+
author1 = Author.new
|
200
|
+
author1.useless_hash # => { :foo => [] }
|
201
|
+
# This mutates the referred array:
|
202
|
+
author1.useless_hash[:foo] << 1
|
203
|
+
|
204
|
+
author2 = Author.new
|
205
|
+
author2.useless_hash # => { :foo => [1] }
|
190
206
|
|
191
207
|
You can prevent this from happening by passing a block to +default_value_for+,
|
192
|
-
which returns a new object instance every time:
|
208
|
+
which returns a new object instance with fresh references every time:
|
193
209
|
|
194
|
-
class
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
Author.new
|
210
|
+
class Author < ActiveRecord::Base
|
211
|
+
attr_accessor :useless_hash
|
212
|
+
default_value_for :useless_hash do
|
213
|
+
{ :foo => [] }
|
199
214
|
end
|
200
215
|
end
|
201
216
|
|
202
|
-
|
203
|
-
|
204
|
-
|
217
|
+
author1 = Author.new
|
218
|
+
author1.useless_hash # => { :foo => [] }
|
219
|
+
author1.useless_hash[:foo] << 1
|
205
220
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
The main reason why default values are not duplicated is because not all
|
210
|
-
objects can be duplicated. For example, +Fixnum+ responds to +dup+, but calling
|
211
|
-
+dup+ on a Fixnum will raise an exception.
|
221
|
+
author2 = Author.new
|
222
|
+
author2.useless_hash # => { :foo => [] }
|
212
223
|
|
213
224
|
=== Caveats
|
214
225
|
|
@@ -249,16 +260,20 @@ can specify a default value in a migration as follows:
|
|
249
260
|
create_table :users do |t|
|
250
261
|
t.string :username, :null => false, :default => 'default username'
|
251
262
|
t.integer :age, :null => false, :default => 20
|
252
|
-
t.timestamp :last_seen, :null => false, :default => Time.now
|
253
263
|
end
|
254
264
|
|
255
|
-
This has
|
265
|
+
This has similar effects as passing the default value as the second argument to
|
256
266
|
+default_value_for+:
|
257
267
|
|
268
|
+
default_value_for(:username, 'default_username')
|
269
|
+
default_value_for(:age, 20)
|
270
|
+
|
271
|
+
Default values are filled in whether you use the schema defaults or the
|
272
|
+
default_value_for defaults:
|
273
|
+
|
258
274
|
user = User.new
|
259
275
|
user.username # => 'default username'
|
260
276
|
user.age # => 20
|
261
|
-
user.timestamp # => Mon Sep 22 18:31:47 +0200 2008
|
262
277
|
|
263
278
|
It's recommended that you use this over +default_value_for+ whenever possible.
|
264
279
|
|
data/Rakefile
CHANGED
@@ -1,24 +1,3 @@
|
|
1
|
-
begin
|
2
|
-
require 'rake'
|
3
|
-
rescue LoadError
|
4
|
-
require 'rubygems'
|
5
|
-
require 'rake'
|
6
|
-
end
|
7
|
-
|
8
|
-
begin
|
9
|
-
require 'jeweler'
|
10
|
-
Jeweler::Tasks.new do |gemspec|
|
11
|
-
gemspec.name = "default_value_for"
|
12
|
-
gemspec.summary = "Provides a way to specify default values for ActiveRecord models"
|
13
|
-
gemspec.description = "The default_value_for plugin allows one to define default values for ActiveRecord models in a declarative manner"
|
14
|
-
gemspec.email = "info@phusion.nl"
|
15
|
-
gemspec.homepage = "http://github.com/FooBarWidget/default_value_for"
|
16
|
-
gemspec.authors = ["Hongli Lai"]
|
17
|
-
end
|
18
|
-
rescue LoadError
|
19
|
-
puts "default_value_for not available. Install it with: sudo gem install default_value_for"
|
20
|
-
end
|
21
|
-
|
22
1
|
task :default => :test
|
23
2
|
|
24
3
|
desc "Run unit tests."
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = %q{default_value_for}
|
3
|
+
s.version = "1.0.0"
|
4
|
+
s.summary = %q{Provides a way to specify default values for ActiveRecord models}
|
5
|
+
s.description = %q{The default_value_for plugin allows one to define default values for ActiveRecord models in a declarative manner}
|
6
|
+
s.email = %q{info@phusion.nl}
|
7
|
+
s.homepage = %q{http://github.com/FooBarWidget/default_value_for}
|
8
|
+
s.authors = ["Hongli Lai"]
|
9
|
+
s.files = ['default_value_for.gemspec', 'init.rb', 'LICENSE.TXT', 'Rakefile', 'README.rdoc', 'test.rb']
|
10
|
+
end
|
data/init.rb
CHANGED
@@ -1 +1,99 @@
|
|
1
|
-
|
1
|
+
# Copyright (c) 2008 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 DefaultValueForPlugin
|
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
|
+
return @block.call(instance)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module ClassMethods
|
47
|
+
def default_value_for(attribute, value = nil, &block)
|
48
|
+
if !method_defined?(:initialize_with_defaults)
|
49
|
+
include(InstanceMethods)
|
50
|
+
alias_method_chain :initialize, :defaults
|
51
|
+
class_inheritable_accessor :_default_attribute_values
|
52
|
+
self._default_attribute_values = ActiveSupport::OrderedHash.new
|
53
|
+
end
|
54
|
+
if block_given?
|
55
|
+
container = BlockValueContainer.new(block)
|
56
|
+
else
|
57
|
+
container = NormalValueContainer.new(value)
|
58
|
+
end
|
59
|
+
_default_attribute_values[attribute.to_s] = container
|
60
|
+
end
|
61
|
+
|
62
|
+
def default_values(values)
|
63
|
+
values.each_pair do |key, value|
|
64
|
+
if value.kind_of? Proc
|
65
|
+
default_value_for(key, &value)
|
66
|
+
else
|
67
|
+
default_value_for(key, value)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
module InstanceMethods
|
74
|
+
def initialize_with_defaults(attrs = nil)
|
75
|
+
initialize_without_defaults(attrs) do
|
76
|
+
if attrs
|
77
|
+
stringified_attrs = attrs.stringify_keys
|
78
|
+
safe_attrs = if respond_to? :sanitize_for_mass_assignment
|
79
|
+
sanitize_for_mass_assignment(stringified_attrs)
|
80
|
+
else
|
81
|
+
remove_attributes_protected_from_mass_assignment(stringified_attrs)
|
82
|
+
end
|
83
|
+
safe_attribute_names = safe_attrs.keys.map do |x|
|
84
|
+
x.to_s
|
85
|
+
end
|
86
|
+
end
|
87
|
+
self.class._default_attribute_values.each do |attribute, container|
|
88
|
+
if safe_attribute_names.nil? || !safe_attribute_names.any? { |attr_name| attr_name =~ /^#{attribute}($|\()/ }
|
89
|
+
__send__("#{attribute}=", container.evaluate(self))
|
90
|
+
changed_attributes.delete(attribute)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
yield(self) if block_given?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
ActiveRecord::Base.extend(DefaultValueForPlugin::ClassMethods)
|
data/test.rb
CHANGED
@@ -255,7 +255,7 @@ class DefaultValuePluginTest < Test::Unit::TestCase
|
|
255
255
|
assert_equal(["type"], object.changed)
|
256
256
|
end
|
257
257
|
|
258
|
-
def
|
258
|
+
def test_default_values_are_duplicated
|
259
259
|
define_model_class do
|
260
260
|
set_table_name "users"
|
261
261
|
default_value_for :username, "hello"
|
@@ -263,7 +263,19 @@ class DefaultValuePluginTest < Test::Unit::TestCase
|
|
263
263
|
user1 = TestClass.new
|
264
264
|
user1.username << " world"
|
265
265
|
user2 = TestClass.new
|
266
|
-
assert_equal("hello
|
266
|
+
assert_equal("hello", user2.username)
|
267
|
+
end
|
268
|
+
|
269
|
+
def test_default_values_are_shallow_copied
|
270
|
+
define_model_class do
|
271
|
+
set_table_name "users"
|
272
|
+
attr_accessor :hash
|
273
|
+
default_value_for :hash, { 1 => [] }
|
274
|
+
end
|
275
|
+
user1 = TestClass.new
|
276
|
+
user1.hash[1] << 1
|
277
|
+
user2 = TestClass.new
|
278
|
+
assert_equal([1], user2.hash[1])
|
267
279
|
end
|
268
280
|
|
269
281
|
def test_constructor_does_not_affect_the_hash_passed_to_it
|
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: default_value_for
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Hongli Lai
|
@@ -9,7 +15,7 @@ autorequire:
|
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date:
|
18
|
+
date: 2010-08-29 00:00:00 +02:00
|
13
19
|
default_executable:
|
14
20
|
dependencies: []
|
15
21
|
|
@@ -19,42 +25,46 @@ executables: []
|
|
19
25
|
|
20
26
|
extensions: []
|
21
27
|
|
22
|
-
extra_rdoc_files:
|
23
|
-
|
24
|
-
- README.rdoc
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
25
30
|
files:
|
26
|
-
- .
|
31
|
+
- default_value_for.gemspec
|
32
|
+
- init.rb
|
27
33
|
- LICENSE.TXT
|
28
|
-
- README.rdoc
|
29
34
|
- Rakefile
|
30
|
-
-
|
31
|
-
- init.rb
|
35
|
+
- README.rdoc
|
32
36
|
- test.rb
|
33
37
|
has_rdoc: true
|
34
38
|
homepage: http://github.com/FooBarWidget/default_value_for
|
35
39
|
licenses: []
|
36
40
|
|
37
41
|
post_install_message:
|
38
|
-
rdoc_options:
|
39
|
-
|
42
|
+
rdoc_options: []
|
43
|
+
|
40
44
|
require_paths:
|
41
45
|
- lib
|
42
46
|
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
43
48
|
requirements:
|
44
49
|
- - ">="
|
45
50
|
- !ruby/object:Gem::Version
|
51
|
+
hash: 3
|
52
|
+
segments:
|
53
|
+
- 0
|
46
54
|
version: "0"
|
47
|
-
version:
|
48
55
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
49
57
|
requirements:
|
50
58
|
- - ">="
|
51
59
|
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
52
63
|
version: "0"
|
53
|
-
version:
|
54
64
|
requirements: []
|
55
65
|
|
56
66
|
rubyforge_project:
|
57
|
-
rubygems_version: 1.3.
|
67
|
+
rubygems_version: 1.3.7
|
58
68
|
signing_key:
|
59
69
|
specification_version: 3
|
60
70
|
summary: Provides a way to specify default values for ActiveRecord models
|
data/.gitignore
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
test.sqlite3
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.1.0
|