default_value_for 0.1.0 → 1.0.0

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.
Files changed (8) hide show
  1. data/README.rdoc +39 -24
  2. data/Rakefile +0 -21
  3. data/default_value_for.gemspec +10 -0
  4. data/init.rb +99 -1
  5. data/test.rb +14 -2
  6. metadata +24 -14
  7. data/.gitignore +0 -1
  8. 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 *not* duplicated
166
+ === Default values are duplicated
167
167
 
168
- The given default values are *not* duplicated when they are filled in, so if
169
- you mutate a value that was filled in with a default value, then it will affect
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 mutates the default value:
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 # => "John"
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 Book < ActiveRecord::Base
195
- belongs_to :author
196
-
197
- default_value_for :author do
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
- book1 = Book.new
203
- book1.author.name # => nil
204
- book1.author.name = "John"
217
+ author1 = Author.new
218
+ author1.useless_hash # => { :foo => [] }
219
+ author1.useless_hash[:foo] << 1
205
220
 
206
- book2 = Book.new
207
- book2.author.name # => nil
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 the same effect as passing the default value as the second argument to
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
- require 'lib/default_value_for.rb'
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 test_default_values_are_not_duplicated
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 world", user2.username)
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
- version: 0.1.0
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: 2009-11-20 00:00:00 +01:00
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
- - LICENSE.TXT
24
- - README.rdoc
28
+ extra_rdoc_files: []
29
+
25
30
  files:
26
- - .gitignore
31
+ - default_value_for.gemspec
32
+ - init.rb
27
33
  - LICENSE.TXT
28
- - README.rdoc
29
34
  - Rakefile
30
- - VERSION
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
- - --charset=UTF-8
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.5
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