dm-core 0.9.5 → 0.9.6
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +3 -0
- data/lib/dm-core.rb +14 -20
- data/lib/dm-core/adapters.rb +18 -0
- data/lib/dm-core/adapters/abstract_adapter.rb +17 -10
- data/lib/dm-core/adapters/data_objects_adapter.rb +17 -22
- data/lib/dm-core/adapters/in_memory_adapter.rb +87 -0
- data/lib/dm-core/adapters/mysql_adapter.rb +1 -1
- data/lib/dm-core/adapters/postgres_adapter.rb +2 -2
- data/lib/dm-core/adapters/sqlite3_adapter.rb +1 -1
- data/lib/dm-core/associations.rb +3 -2
- data/lib/dm-core/associations/many_to_many.rb +3 -3
- data/lib/dm-core/associations/one_to_many.rb +10 -2
- data/lib/dm-core/associations/relationship.rb +20 -16
- data/lib/dm-core/auto_migrations.rb +5 -4
- data/lib/dm-core/collection.rb +10 -6
- data/lib/dm-core/dependency_queue.rb +2 -1
- data/lib/dm-core/identity_map.rb +3 -6
- data/lib/dm-core/model.rb +48 -27
- data/lib/dm-core/property.rb +57 -37
- data/lib/dm-core/property_set.rb +29 -22
- data/lib/dm-core/query.rb +57 -49
- data/lib/dm-core/repository.rb +3 -3
- data/lib/dm-core/resource.rb +17 -15
- data/lib/dm-core/scope.rb +7 -7
- data/lib/dm-core/support/kernel.rb +6 -2
- data/lib/dm-core/transaction.rb +7 -7
- data/lib/dm-core/version.rb +1 -1
- data/script/performance.rb +114 -22
- data/spec/integration/association_spec.rb +31 -2
- data/spec/integration/association_through_spec.rb +2 -0
- data/spec/integration/associations/many_to_many_spec.rb +152 -0
- data/spec/integration/associations/one_to_many_spec.rb +40 -3
- data/spec/integration/dependency_queue_spec.rb +0 -12
- data/spec/integration/postgres_adapter_spec.rb +1 -1
- data/spec/integration/property_spec.rb +3 -3
- data/spec/integration/query_spec.rb +39 -8
- data/spec/integration/resource_spec.rb +10 -6
- data/spec/integration/sti_spec.rb +22 -0
- data/spec/integration/strategic_eager_loading_spec.rb +21 -6
- data/spec/integration/type_spec.rb +1 -0
- data/spec/lib/model_loader.rb +10 -1
- data/spec/models/content.rb +16 -0
- data/spec/spec_helper.rb +4 -1
- data/spec/unit/adapters/data_objects_adapter_spec.rb +11 -11
- data/spec/unit/adapters/in_memory_adapter_spec.rb +98 -0
- data/spec/unit/associations/many_to_many_spec.rb +16 -1
- data/spec/unit/model_spec.rb +0 -16
- data/spec/unit/property_set_spec.rb +8 -1
- data/spec/unit/property_spec.rb +476 -240
- data/spec/unit/query_spec.rb +41 -0
- data/spec/unit/resource_spec.rb +75 -56
- data/tasks/ci.rb +4 -36
- data/tasks/dm.rb +3 -3
- metadata +5 -2
@@ -74,25 +74,29 @@ module DataMapper
|
|
74
74
|
|
75
75
|
# @api private
|
76
76
|
def get_children(parent, options = {}, finder = :all, *args)
|
77
|
-
|
78
|
-
|
77
|
+
parent_value = parent_key.get(parent)
|
78
|
+
bind_values = [ parent_value ]
|
79
79
|
|
80
80
|
with_repository(child_model) do |r|
|
81
81
|
parent_identity_map = parent.repository.identity_map(parent_model)
|
82
82
|
child_identity_map = r.identity_map(child_model)
|
83
83
|
|
84
|
-
query_values = parent_identity_map.keys
|
85
|
-
query_values.reject! { |k| child_identity_map[
|
84
|
+
query_values = parent_identity_map.keys
|
85
|
+
query_values.reject! { |k| child_identity_map[k] }
|
86
86
|
|
87
87
|
bind_values = query_values unless query_values.empty?
|
88
|
-
query = child_key.
|
88
|
+
query = child_key.zip(bind_values.transpose).to_hash
|
89
89
|
|
90
90
|
collection = child_model.send(finder, *(args.dup << @query.merge(options).merge(query)))
|
91
|
+
|
91
92
|
return collection unless collection.kind_of?(Collection) && collection.any?
|
92
93
|
|
93
|
-
grouped_collection =
|
94
|
+
grouped_collection = {}
|
94
95
|
collection.each do |resource|
|
95
|
-
|
96
|
+
child_value = child_key.get(resource)
|
97
|
+
parent_obj = parent_identity_map[child_value]
|
98
|
+
grouped_collection[parent_obj] ||= []
|
99
|
+
grouped_collection[parent_obj] << resource
|
96
100
|
end
|
97
101
|
|
98
102
|
association_accessor = "#{self.name}_association"
|
@@ -112,14 +116,14 @@ module DataMapper
|
|
112
116
|
parents_children = Collection.new(query)
|
113
117
|
children.each { |child| parents_children.send(:add, child) }
|
114
118
|
|
115
|
-
if parent_key.get(parent) ==
|
119
|
+
if parent_key.get(parent) == parent_value
|
116
120
|
ret = parents_children
|
117
121
|
else
|
118
122
|
association.instance_variable_set(:@children, parents_children)
|
119
123
|
end
|
120
124
|
end
|
121
125
|
|
122
|
-
ret || child_model.send(finder, *(args.dup << @query.merge(options).merge(child_key.zip(
|
126
|
+
ret || child_model.send(finder, *(args.dup << @query.merge(options).merge(child_key.zip([ parent_value ]).to_hash)))
|
123
127
|
end
|
124
128
|
end
|
125
129
|
|
@@ -129,14 +133,15 @@ module DataMapper
|
|
129
133
|
return nil unless child_value.nitems == child_value.size
|
130
134
|
|
131
135
|
with_repository(parent || parent_model) do
|
132
|
-
parent_identity_map = (parent || parent_model).repository.identity_map(parent_model)
|
133
|
-
child_identity_map = child.repository.identity_map(child_model)
|
136
|
+
parent_identity_map = (parent || parent_model).repository.identity_map(parent_model.base_model)
|
137
|
+
child_identity_map = child.repository.identity_map(child_model.base_model)
|
134
138
|
|
135
139
|
if parent = parent_identity_map[child_value]
|
136
140
|
return parent
|
137
141
|
end
|
138
142
|
|
139
|
-
children = child_identity_map.values
|
143
|
+
children = child_identity_map.values
|
144
|
+
children << child unless child_identity_map[child.key]
|
140
145
|
|
141
146
|
bind_values = children.map { |c| child_key.get(c) }.uniq
|
142
147
|
query_values = bind_values.reject { |k| parent_identity_map[k] }
|
@@ -151,21 +156,20 @@ module DataMapper
|
|
151
156
|
children.each do |c|
|
152
157
|
c.send(association_accessor).instance_variable_set(:@parent, collection.get(*child_key.get(c)))
|
153
158
|
end
|
154
|
-
|
155
159
|
child.send(association_accessor).instance_variable_get(:@parent)
|
156
160
|
end
|
157
161
|
end
|
158
162
|
end
|
159
163
|
|
160
164
|
# @api private
|
161
|
-
def with_repository(object = nil
|
165
|
+
def with_repository(object = nil)
|
162
166
|
other_model = object.model == child_model ? parent_model : child_model if object.respond_to?(:model)
|
163
167
|
other_model = object == child_model ? parent_model : child_model if object.kind_of?(DataMapper::Resource)
|
164
168
|
|
165
169
|
if other_model && other_model.repository == object.repository && object.repository.name != @repository_name
|
166
|
-
object.repository.scope(
|
170
|
+
object.repository.scope { |block_args| yield(*block_args) }
|
167
171
|
else
|
168
|
-
repository(@repository_name
|
172
|
+
repository(@repository_name) { |block_args| yield(*block_args) }
|
169
173
|
end
|
170
174
|
end
|
171
175
|
|
@@ -59,7 +59,7 @@ module DataMapper
|
|
59
59
|
# REPEAT: THIS IS DESTRUCTIVE
|
60
60
|
#
|
61
61
|
# @param Symbol repository_name the repository to be migrated
|
62
|
-
def auto_migrate!(repository_name =
|
62
|
+
def auto_migrate!(repository_name = self.repository_name)
|
63
63
|
auto_migrate_down!(repository_name)
|
64
64
|
auto_migrate_up!(repository_name)
|
65
65
|
end
|
@@ -71,7 +71,8 @@ module DataMapper
|
|
71
71
|
#
|
72
72
|
# @param Symbol repository_name the repository to be migrated
|
73
73
|
# @api private
|
74
|
-
def auto_migrate_down!(repository_name =
|
74
|
+
def auto_migrate_down!(repository_name = self.repository_name)
|
75
|
+
# repository_name ||= default_repository_name
|
75
76
|
if self.superclass != Object
|
76
77
|
self.superclass.auto_migrate!(repository_name)
|
77
78
|
else
|
@@ -86,7 +87,7 @@ module DataMapper
|
|
86
87
|
#
|
87
88
|
# @param Symbol repository_name the repository to be migrated
|
88
89
|
# @api private
|
89
|
-
def auto_migrate_up!(repository_name =
|
90
|
+
def auto_migrate_up!(repository_name = self.repository_name)
|
90
91
|
if self.superclass != Object
|
91
92
|
self.superclass.auto_migrate!(repository_name)
|
92
93
|
else
|
@@ -101,7 +102,7 @@ module DataMapper
|
|
101
102
|
# preserving data already in the data-store
|
102
103
|
#
|
103
104
|
# @param Symbol repository_name the repository to be migrated
|
104
|
-
def auto_upgrade!(repository_name =
|
105
|
+
def auto_upgrade!(repository_name = self.repository_name)
|
105
106
|
repository(repository_name) do |r|
|
106
107
|
r.adapter.upgrade_model_storage(r, self)
|
107
108
|
end
|
data/lib/dm-core/collection.rb
CHANGED
@@ -52,8 +52,9 @@ module DataMapper
|
|
52
52
|
def get(*key)
|
53
53
|
key = model.typecast_key(key)
|
54
54
|
if loaded?
|
55
|
-
#
|
56
|
-
|
55
|
+
# find indexed resource (create index first if it does not exist)
|
56
|
+
each {|r| @cache[r.key] = r } if @cache.empty?
|
57
|
+
@cache[key]
|
57
58
|
elsif query.limit || query.offset > 0
|
58
59
|
# current query is exclusive, find resource within the set
|
59
60
|
|
@@ -167,7 +168,7 @@ module DataMapper
|
|
167
168
|
end
|
168
169
|
|
169
170
|
##
|
170
|
-
# Simulates Array#at and returns the
|
171
|
+
# Simulates Array#at and returns the entry at that index.
|
171
172
|
# Also accepts negative indexes and appropriate reverses
|
172
173
|
# the order of the query
|
173
174
|
#
|
@@ -295,7 +296,7 @@ module DataMapper
|
|
295
296
|
# @see Array#delete
|
296
297
|
#
|
297
298
|
# @api public
|
298
|
-
def delete(resource
|
299
|
+
def delete(resource)
|
299
300
|
orphan_resource(super)
|
300
301
|
end
|
301
302
|
|
@@ -515,6 +516,7 @@ module DataMapper
|
|
515
516
|
|
516
517
|
@query = query
|
517
518
|
@key_properties = model.key(repository.name)
|
519
|
+
@cache = {}
|
518
520
|
|
519
521
|
super()
|
520
522
|
|
@@ -533,6 +535,7 @@ module DataMapper
|
|
533
535
|
def relate_resource(resource)
|
534
536
|
return unless resource
|
535
537
|
resource.collection = self
|
538
|
+
@cache[resource.key] = resource
|
536
539
|
resource
|
537
540
|
end
|
538
541
|
|
@@ -540,7 +543,8 @@ module DataMapper
|
|
540
543
|
# @api private
|
541
544
|
def orphan_resource(resource)
|
542
545
|
return unless resource
|
543
|
-
resource.collection = nil if resource.collection == self
|
546
|
+
resource.collection = nil if resource.collection.object_id == self.object_id
|
547
|
+
@cache.delete(resource.key)
|
544
548
|
resource
|
545
549
|
end
|
546
550
|
|
@@ -626,7 +630,7 @@ module DataMapper
|
|
626
630
|
|
627
631
|
query.update(
|
628
632
|
:fields => klass.properties(repository.name).defaults,
|
629
|
-
:links => self.query.links
|
633
|
+
:links => [ relationship ] + self.query.links
|
630
634
|
)
|
631
635
|
|
632
636
|
klass.all(query, &block)
|
@@ -6,10 +6,11 @@ module DataMapper
|
|
6
6
|
#
|
7
7
|
class DependencyQueue
|
8
8
|
def initialize
|
9
|
-
@dependencies =
|
9
|
+
@dependencies = {}
|
10
10
|
end
|
11
11
|
|
12
12
|
def add(class_name, &callback)
|
13
|
+
@dependencies[class_name] ||= []
|
13
14
|
@dependencies[class_name] << callback
|
14
15
|
resolve!
|
15
16
|
end
|
data/lib/dm-core/identity_map.rb
CHANGED
@@ -5,7 +5,7 @@ module DataMapper
|
|
5
5
|
class IdentityMap
|
6
6
|
# Get a resource from the IdentityMap
|
7
7
|
def get(key)
|
8
|
-
@cache[key]
|
8
|
+
@cache[key] || (@second_level_cache && @second_level_cache.get(key))
|
9
9
|
end
|
10
10
|
|
11
11
|
alias [] get
|
@@ -27,11 +27,8 @@ module DataMapper
|
|
27
27
|
private
|
28
28
|
|
29
29
|
def initialize(second_level_cache = nil)
|
30
|
-
@cache =
|
31
|
-
|
32
|
-
else
|
33
|
-
Hash.new
|
34
|
-
end
|
30
|
+
@cache = {}
|
31
|
+
@second_level_cache = second_level_cache
|
35
32
|
end
|
36
33
|
|
37
34
|
def cache
|
data/lib/dm-core/model.rb
CHANGED
@@ -29,15 +29,15 @@ module DataMapper
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def self.extended(model)
|
32
|
-
model.instance_variable_set(:@storage_names,
|
33
|
-
model.instance_variable_set(:@properties,
|
34
|
-
model.instance_variable_set(:@field_naming_conventions,
|
32
|
+
model.instance_variable_set(:@storage_names, {})
|
33
|
+
model.instance_variable_set(:@properties, {})
|
34
|
+
model.instance_variable_set(:@field_naming_conventions, {})
|
35
35
|
extra_extensions.each { |extension| model.extend(extension) }
|
36
36
|
end
|
37
37
|
|
38
38
|
def inherited(target)
|
39
39
|
target.instance_variable_set(:@storage_names, @storage_names.dup)
|
40
|
-
target.instance_variable_set(:@properties,
|
40
|
+
target.instance_variable_set(:@properties, {})
|
41
41
|
target.instance_variable_set(:@base_model, self.base_model)
|
42
42
|
target.instance_variable_set(:@paranoid_properties, @paranoid_properties)
|
43
43
|
target.instance_variable_set(:@field_naming_conventions, @field_naming_conventions.dup)
|
@@ -58,12 +58,13 @@ module DataMapper
|
|
58
58
|
end
|
59
59
|
|
60
60
|
if @relationships
|
61
|
-
duped_relationships =
|
61
|
+
duped_relationships = {}
|
62
62
|
@relationships.each do |repository_name,relationships|
|
63
63
|
relationships.each do |name, relationship|
|
64
64
|
dup = relationship.dup
|
65
65
|
dup.instance_variable_set(:@child_model, target) if dup.instance_variable_get(:@child_model) == self
|
66
66
|
dup.instance_variable_set(:@parent_model, target) if dup.instance_variable_get(:@parent_model) == self
|
67
|
+
duped_relationships[repository_name] ||= {}
|
67
68
|
duped_relationships[repository_name][name] = dup
|
68
69
|
end
|
69
70
|
end
|
@@ -102,13 +103,17 @@ module DataMapper
|
|
102
103
|
# if given a block, otherwise the requested repository.
|
103
104
|
#-
|
104
105
|
# @api public
|
105
|
-
def repository(name = nil
|
106
|
+
def repository(name = nil)
|
106
107
|
#
|
107
108
|
# There has been a couple of different strategies here, but me (zond) and dkubb are at least
|
108
109
|
# united in the concept of explicitness over implicitness. That is - the explicit wish of the
|
109
110
|
# caller (+name+) should be given more priority than the implicit wish of the caller (Repository.context.last).
|
110
111
|
#
|
111
|
-
|
112
|
+
if block_given?
|
113
|
+
DataMapper.repository(name || repository_name) { |*block_args| yield(*block_args) }
|
114
|
+
else
|
115
|
+
DataMapper.repository(name || repository_name)
|
116
|
+
end
|
112
117
|
end
|
113
118
|
|
114
119
|
##
|
@@ -116,7 +121,7 @@ module DataMapper
|
|
116
121
|
#
|
117
122
|
# @return <String> the storage name (IE table name, for database stores) associated with this resource in the given repository
|
118
123
|
def storage_name(repository_name = default_repository_name)
|
119
|
-
@storage_names[repository_name]
|
124
|
+
@storage_names[repository_name] ||= repository(repository_name).adapter.resource_naming_convention.call(base_model.send(:default_storage_name))
|
120
125
|
end
|
121
126
|
|
122
127
|
##
|
@@ -130,9 +135,9 @@ module DataMapper
|
|
130
135
|
##
|
131
136
|
# The field naming conventions for this resource across all repositories.
|
132
137
|
#
|
133
|
-
# @return <
|
134
|
-
def
|
135
|
-
@field_naming_conventions
|
138
|
+
# @return <String> The naming convention for the given repository
|
139
|
+
def field_naming_convention(repository_name = default_storage_name)
|
140
|
+
@field_naming_conventions[repository_name] ||= repository(repository_name).adapter.field_naming_convention
|
136
141
|
end
|
137
142
|
|
138
143
|
##
|
@@ -148,14 +153,15 @@ module DataMapper
|
|
148
153
|
create_property_getter(property)
|
149
154
|
create_property_setter(property)
|
150
155
|
|
151
|
-
|
156
|
+
properties(repository_name)[property.name] = property
|
157
|
+
@_valid_relations = false
|
152
158
|
|
153
159
|
# Add property to the other mappings as well if this is for the default
|
154
160
|
# repository.
|
155
161
|
if repository_name == default_repository_name
|
156
162
|
@properties.each_pair do |repository_name, properties|
|
157
163
|
next if repository_name == default_repository_name
|
158
|
-
properties << property
|
164
|
+
properties << property unless properties.has_property?(property.name)
|
159
165
|
end
|
160
166
|
end
|
161
167
|
|
@@ -168,7 +174,7 @@ module DataMapper
|
|
168
174
|
context = :default if context == true
|
169
175
|
|
170
176
|
Array(context).each do |item|
|
171
|
-
|
177
|
+
properties(repository_name).lazy_context(item) << name
|
172
178
|
end
|
173
179
|
end
|
174
180
|
|
@@ -190,7 +196,25 @@ module DataMapper
|
|
190
196
|
end
|
191
197
|
|
192
198
|
def properties(repository_name = default_repository_name)
|
193
|
-
|
199
|
+
# We need to check whether all relations are already set up.
|
200
|
+
# If this isn't the case, we try to reload them here
|
201
|
+
if !@_valid_relations && respond_to?(:many_to_one_relationships)
|
202
|
+
@_valid_relations = true
|
203
|
+
begin
|
204
|
+
many_to_one_relationships.each do |r|
|
205
|
+
r.child_key
|
206
|
+
end
|
207
|
+
rescue NameError
|
208
|
+
# Apparently not all relations are loaded,
|
209
|
+
# so we will try again later on
|
210
|
+
@_valid_relations = false
|
211
|
+
end
|
212
|
+
end
|
213
|
+
@properties[repository_name] ||= repository_name == Repository.default_name ? PropertySet.new : properties(Repository.default_name).dup
|
214
|
+
end
|
215
|
+
|
216
|
+
def eager_properties(repository_name = default_repository_name)
|
217
|
+
properties(repository_name).defaults
|
194
218
|
end
|
195
219
|
|
196
220
|
# @api private
|
@@ -207,15 +231,12 @@ module DataMapper
|
|
207
231
|
end
|
208
232
|
|
209
233
|
def key(repository_name = default_repository_name)
|
210
|
-
|
211
|
-
end
|
212
|
-
|
213
|
-
def inheritance_property(repository_name = default_repository_name)
|
214
|
-
@properties[repository_name].inheritance_property
|
234
|
+
properties(repository_name).key
|
215
235
|
end
|
216
236
|
|
217
|
-
def default_order
|
218
|
-
@default_order ||=
|
237
|
+
def default_order(repository_name = default_repository_name)
|
238
|
+
@default_order ||= {}
|
239
|
+
@default_order[repository_name] ||= key(repository_name).map { |property| Query::Direction.new(property) }
|
219
240
|
end
|
220
241
|
|
221
242
|
def get(*key)
|
@@ -288,8 +309,8 @@ module DataMapper
|
|
288
309
|
# TODO SPEC
|
289
310
|
def copy(source, destination, query = {})
|
290
311
|
repository(destination) do
|
291
|
-
repository(source).read_many(query).each do |resource|
|
292
|
-
self.create(resource)
|
312
|
+
repository(source).read_many(scoped_query(query)).each do |resource|
|
313
|
+
self.create(resource.attributes)
|
293
314
|
end
|
294
315
|
end
|
295
316
|
end
|
@@ -300,7 +321,7 @@ module DataMapper
|
|
300
321
|
repository = query.repository
|
301
322
|
model = self
|
302
323
|
|
303
|
-
if inheritance_property_index = query.inheritance_property_index
|
324
|
+
if inheritance_property_index = query.inheritance_property_index
|
304
325
|
model = values.at(inheritance_property_index) || model
|
305
326
|
end
|
306
327
|
|
@@ -447,8 +468,8 @@ module DataMapper
|
|
447
468
|
# @api public
|
448
469
|
#
|
449
470
|
# TODO: move to dm-more/dm-transactions
|
450
|
-
def transaction
|
451
|
-
DataMapper::Transaction.new(self
|
471
|
+
def transaction
|
472
|
+
DataMapper::Transaction.new(self) { |block_args| yield(*block_args) }
|
452
473
|
end
|
453
474
|
end # module Transaction
|
454
475
|
|
data/lib/dm-core/property.rb
CHANGED
@@ -220,6 +220,21 @@ module DataMapper
|
|
220
220
|
# dm-more bundle. For more information about validations, check the
|
221
221
|
# documentation for dm-validations.
|
222
222
|
#
|
223
|
+
# == Default Values
|
224
|
+
# To set a default for a property, use the <tt>:default</tt> key. The
|
225
|
+
# property will be set to the value associated with that key the first time
|
226
|
+
# it is accessed, or when the resource is saved if it hasn't been set with
|
227
|
+
# another value already. This value can be a static value, such as 'hello'
|
228
|
+
# but it can also be a proc that will be evaluated when the property is read
|
229
|
+
# before its value has been set. The property is set to the return of the
|
230
|
+
# proc. The proc is passed two values, the resource the property is being set
|
231
|
+
# for and the property itself.
|
232
|
+
#
|
233
|
+
# property :display_name, String, :default => { |r, p| r.login }
|
234
|
+
#
|
235
|
+
# Word of warning. Don't try to read the value of the property you're setting
|
236
|
+
# the default for in the proc. An infinite loop will ensue.
|
237
|
+
#
|
223
238
|
# == Embedded Values
|
224
239
|
# As an alternative to extraneous has_one relationships, consider using an
|
225
240
|
# EmbeddedValue.
|
@@ -287,7 +302,7 @@ module DataMapper
|
|
287
302
|
# -
|
288
303
|
# @api semi-public
|
289
304
|
def field(repository_name = nil)
|
290
|
-
@field || fields[repository_name
|
305
|
+
@field || @fields[repository_name] ||= self.model.field_naming_convention(repository_name).call(self)
|
291
306
|
end
|
292
307
|
|
293
308
|
def unique
|
@@ -372,20 +387,15 @@ module DataMapper
|
|
372
387
|
#-
|
373
388
|
# @api private
|
374
389
|
def get(resource)
|
375
|
-
|
376
|
-
|
377
|
-
lazy_load(resource) unless new_record || resource.attribute_loaded?(name)
|
390
|
+
lazy_load(resource)
|
378
391
|
|
379
392
|
value = get!(resource)
|
380
393
|
|
381
|
-
|
382
|
-
when :hash
|
383
|
-
resource.original_values[name] = value.dup.hash unless resource.original_values.has_key?(name) rescue value.hash
|
384
|
-
when :get
|
385
|
-
resource.original_values[name] = value.dup unless resource.original_values.has_key?(name) rescue value
|
386
|
-
end
|
394
|
+
set_original_value(resource, value)
|
387
395
|
|
388
|
-
|
396
|
+
# [YK] Why did we previously care whether options[:default] is nil.
|
397
|
+
# The default value of nil will be applied either way
|
398
|
+
if value.nil? && resource.new_record? && !resource.attribute_loaded?(name)
|
389
399
|
value = default_for(resource)
|
390
400
|
set(resource, value)
|
391
401
|
end
|
@@ -397,21 +407,32 @@ module DataMapper
|
|
397
407
|
resource.instance_variable_get(instance_variable_name)
|
398
408
|
end
|
399
409
|
|
410
|
+
def set_original_value(resource, val)
|
411
|
+
unless resource.original_values.key?(name)
|
412
|
+
val = val.try_dup
|
413
|
+
val = val.hash if track == :hash
|
414
|
+
resource.original_values[name] = val
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
400
418
|
# Provides a standardized setter method for the property
|
401
419
|
#
|
402
420
|
# @raise <ArgumentError> "+resource+ should be a DataMapper::Resource, but was ...."
|
403
421
|
#-
|
404
422
|
# @api private
|
405
423
|
def set(resource, value)
|
406
|
-
|
424
|
+
# [YK] We previously checked for new_record? here, but lazy loading
|
425
|
+
# is blocked anyway if we're in a new record by by
|
426
|
+
# Resource#reload_attributes. This may eventually be useful for
|
427
|
+
# optimizing, but let's (a) benchmark it first, and (b) do
|
428
|
+
# whatever refactoring is necessary, which will benefit from the
|
429
|
+
# centralize checking
|
430
|
+
lazy_load(resource)
|
431
|
+
|
407
432
|
new_value = typecast(value)
|
408
433
|
old_value = get!(resource)
|
409
434
|
|
410
|
-
|
411
|
-
# to the old value, and the old value was defined
|
412
|
-
return if new_value == old_value && resource.attribute_loaded?(name)
|
413
|
-
|
414
|
-
resource.original_values[name] = old_value unless resource.original_values.has_key?(name)
|
435
|
+
set_original_value(resource, old_value)
|
415
436
|
|
416
437
|
set!(resource, new_value)
|
417
438
|
end
|
@@ -424,14 +445,13 @@ module DataMapper
|
|
424
445
|
#-
|
425
446
|
# @api private
|
426
447
|
def lazy_load(resource)
|
427
|
-
#
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
end
|
448
|
+
# It is faster to bail out at at a new_record? rather than to process
|
449
|
+
# which properties would be loaded and then not load them.
|
450
|
+
return if resource.new_record? || resource.attribute_loaded?(name)
|
451
|
+
# If we're trying to load a lazy property, load it. Otherwise, lazy-load
|
452
|
+
# any properties that should be eager-loaded but were not included
|
453
|
+
# in the original :fields list
|
454
|
+
contexts = lazy? ? name : model.eager_properties(resource.repository.name)
|
435
455
|
resource.send(:lazy_load, contexts)
|
436
456
|
end
|
437
457
|
|
@@ -457,14 +477,13 @@ module DataMapper
|
|
457
477
|
# these two alternatives:
|
458
478
|
# * Integer(value) rescue nil
|
459
479
|
# * Integer(value_to_s =~ /(\d+)/ ? $1 : value_to_s) rescue nil
|
480
|
+
#
|
481
|
+
# [YK] The previous implementation used a rescue. Why use a rescue
|
482
|
+
# when the list of cases where a valid string other than "0" could
|
483
|
+
# produce 0 is known?
|
460
484
|
value_to_i = value.to_i
|
461
|
-
if value_to_i == 0
|
462
|
-
|
463
|
-
begin
|
464
|
-
Integer(value_to_s =~ /^(\d+)/ ? $1 : value_to_s)
|
465
|
-
rescue ArgumentError
|
466
|
-
nil
|
467
|
-
end
|
485
|
+
if value_to_i == 0
|
486
|
+
value.to_s =~ /^(0x|0b)?0+/ ? 0 : nil
|
468
487
|
else
|
469
488
|
value_to_i
|
470
489
|
end
|
@@ -485,6 +504,10 @@ module DataMapper
|
|
485
504
|
@default.respond_to?(:call) ? @default.call(resource, self) : @default
|
486
505
|
end
|
487
506
|
|
507
|
+
def value(val)
|
508
|
+
custom? ? self.type.dump(val, self) : val
|
509
|
+
end
|
510
|
+
|
488
511
|
def inspect
|
489
512
|
"#<Property:#{@model}:#{@name}>"
|
490
513
|
end
|
@@ -535,6 +558,7 @@ module DataMapper
|
|
535
558
|
@index = @options.fetch(:index, false)
|
536
559
|
@unique_index = @options.fetch(:unique_index, false)
|
537
560
|
@lazy = @options.fetch(:lazy, @type.respond_to?(:lazy) ? @type.lazy : false) && !@key
|
561
|
+
@fields = {}
|
538
562
|
|
539
563
|
@track = @options.fetch(:track) do
|
540
564
|
if @custom && @type.respond_to?(:track) && @type.track
|
@@ -575,10 +599,6 @@ module DataMapper
|
|
575
599
|
@model.property_serialization_setup(self) if @model.respond_to?(:property_serialization_setup)
|
576
600
|
end
|
577
601
|
|
578
|
-
def fields
|
579
|
-
@fields ||= Hash.new { |h,k| h[k] = self.model.field_naming_conventions[k].call(self) }
|
580
|
-
end
|
581
|
-
|
582
602
|
def determine_visibility # :nodoc:
|
583
603
|
@reader_visibility = @options[:reader] || @options[:accessor] || :public
|
584
604
|
@writer_visibility = @options[:writer] || @options[:accessor] || :public
|
@@ -615,7 +635,7 @@ module DataMapper
|
|
615
635
|
def typecast_hash_to_datetime(hash)
|
616
636
|
args = extract_time_args_from_hash(hash, :year, :month, :day, :hour, :min, :sec)
|
617
637
|
DateTime.new(*args)
|
618
|
-
rescue ArgumentError
|
638
|
+
rescue ArgumentError => e
|
619
639
|
t = typecast_hash_to_time(hash)
|
620
640
|
DateTime.new(t.year, t.month, t.day, t.hour, t.min, t.sec)
|
621
641
|
end
|