dm-core 0.9.5 → 0.9.6
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.
- 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
|