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
|
# File 'lib/submodel/active_record.rb', line 13
def submodel(attr, klass, validation_options = {}, &block)
return unless self.table_exists?
column = columns_hash[attr.to_s]
augmented_klass = Class.new(klass) do
define_singleton_method :name do
klass.name
end
define_method :inspect do
attrs = Submodel.values(self).map { |k,v| "#{k}=#{v.inspect}" }.join(' ').presence
string = [klass.name, attrs].compact.join(' ')
"#<#{string}>"
end
alias_method :to_s, :inspect
define_method :blank? do
Submodel.values(self).blank?
end
define_method :== do |other|
hash = Submodel.values(self)
if other.is_a? klass
hash == Submodel.values(other)
elsif other.is_a? Hash
hash == other.stringify_keys
else
hash == other
end
end
end
if block_given?
augmented_klass.class_eval &block
end
serialize attr, Module.new {
define_singleton_method :load do |value|
if value.is_a? String
case column.type
when :hstore
value = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn.string_to_hstore(value)
when :json
value = JSON.parse(value)
else
value = YAML.load(value)
end
end
value.present? ? augmented_klass.new(value) : nil
end
define_singleton_method :dump do |object|
if hash = Submodel.values(object).presence
case column.type
when :hstore, :json then hash
else YAML.dump(hash)
end
else
nil
end
end
}
include Module.new {
define_method attr do
self[attr] ||= augmented_klass.new
end
define_method :"#{attr}=" do |value|
if value.nil?
self[attr] = nil
elsif value.is_a? klass
self[attr] = value.dup
else
self[attr] = augmented_klass.new(value)
end
end
alias_method :"#{attr}_attributes=", :"#{attr}="
}
validates_each(attr, validation_options) do |record, attribute, object|
if object.try(:invalid?)
record.errors.add(attribute, object.errors.full_messages.to_sentence.downcase)
end
end
end
|