forked from FooBarWidget/default_value_for
/
default_value_for.rb
112 lines (104 loc) · 3.28 KB
/
default_value_for.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# Copyright (c) 2008, 2009, 2010, 2011 Phusion
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
module DefaultValueFor
class NormalValueContainer
def initialize(value)
@value = value
end
def evaluate(instance)
if @value.duplicable?
return @value.dup
else
return @value
end
end
end
class BlockValueContainer
def initialize(block)
@block = block
end
def evaluate(instance)
if @block.arity == 0
return @block.call
else
return @block.call(instance)
end
end
end
module ClassMethods
def default_value_for(attribute, value = nil, &block)
if !method_defined?(:initialize_with_defaults)
include(InstanceMethods)
alias_method_chain :initialize, :defaults
if respond_to?(:class_attribute)
class_attribute :_default_attribute_values
else
class_inheritable_accessor :_default_attribute_values
end
self._default_attribute_values = ActiveSupport::OrderedHash.new
end
if block_given?
container = BlockValueContainer.new(block)
else
container = NormalValueContainer.new(value)
end
_default_attribute_values[attribute.to_s] = container
end
def default_values(values)
values.each_pair do |key, value|
if value.kind_of? Proc
default_value_for(key, &value)
else
default_value_for(key, value)
end
end
end
end
module InstanceMethods
def initialize_with_defaults(attrs = nil, *args)
initialize_without_defaults(attrs, *args) do
if attrs
stringified_attrs = attrs.stringify_keys
safe_attrs = if respond_to? :sanitize_for_mass_assignment
sanitize_for_mass_assignment(stringified_attrs)
else
remove_attributes_protected_from_mass_assignment(stringified_attrs)
end
safe_attribute_names = safe_attrs.keys.map do |x|
x.to_s
end
end
self.class._default_attribute_values.each do |attribute, container|
if safe_attribute_names.nil? || !safe_attribute_names.any? { |attr_name| attr_name =~ /^#{attribute}($|\()/ }
__send__("#{attribute}=", container.evaluate(self))
changed_attributes.delete(attribute)
end
end
yield(self) if block_given?
end
end
end
end
if defined?(Rails::Railtie)
require 'default_value_for/railtie'
else
# Rails 2 initialization
ActiveRecord::Base.extend(DefaultValueFor::ClassMethods)
end