dm-core 1.0.2 → 1.1.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +28 -94
- data/LICENSE +1 -1
- data/README.rdoc +44 -11
- data/Rakefile +1 -7
- data/VERSION +1 -1
- data/dm-core.gemspec +398 -299
- data/lib/dm-core.rb +23 -13
- data/lib/dm-core/adapters/abstract_adapter.rb +1 -1
- data/lib/dm-core/associations/many_to_many.rb +1 -3
- data/lib/dm-core/associations/many_to_one.rb +54 -36
- data/lib/dm-core/associations/one_to_many.rb +1 -2
- data/lib/dm-core/associations/relationship.rb +11 -2
- data/lib/dm-core/collection.rb +3 -7
- data/lib/dm-core/core_ext/symbol.rb +1 -1
- data/lib/dm-core/identity_map.rb +0 -5
- data/lib/dm-core/model.rb +11 -21
- data/lib/dm-core/model/property.rb +43 -58
- data/lib/dm-core/model/relationship.rb +49 -44
- data/lib/dm-core/property.rb +106 -130
- data/lib/dm-core/property/date_time.rb +1 -3
- data/lib/dm-core/property/decimal.rb +11 -7
- data/lib/dm-core/property/integer.rb +2 -2
- data/lib/dm-core/property/lookup.rb +3 -16
- data/lib/dm-core/property/numeric.rb +3 -3
- data/lib/dm-core/property/object.rb +2 -11
- data/lib/dm-core/property/string.rb +1 -1
- data/lib/dm-core/property_set.rb +34 -54
- data/lib/dm-core/query.rb +85 -56
- data/lib/dm-core/query/conditions/comparison.rb +3 -6
- data/lib/dm-core/query/direction.rb +0 -4
- data/lib/dm-core/query/path.rb +22 -6
- data/lib/dm-core/relationship_set.rb +74 -0
- data/lib/dm-core/resource.rb +21 -32
- data/lib/dm-core/resource/state.rb +3 -4
- data/lib/dm-core/spec/lib/spec_helper.rb +1 -4
- data/lib/dm-core/spec/setup.rb +12 -5
- data/lib/dm-core/spec/shared/public/property_spec.rb +35 -21
- data/lib/dm-core/spec/shared/resource_spec.rb +1 -1
- data/lib/dm-core/spec/shared/semipublic/property_spec.rb +9 -9
- data/lib/dm-core/spec/shared/semipublic/query/conditions/abstract_comparison_spec.rb +261 -0
- data/lib/dm-core/support/deprecate.rb +1 -1
- data/lib/dm-core/support/descendant_set.rb +12 -5
- data/lib/dm-core/support/ordered_set.rb +382 -0
- data/lib/dm-core/support/subject_set.rb +252 -0
- data/lib/dm-core/version.rb +1 -1
- data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +1 -2
- data/spec/public/associations/many_to_many_spec.rb +11 -9
- data/spec/public/associations/many_to_one_spec.rb +1 -1
- data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +1 -1
- data/spec/public/associations/many_to_one_with_custom_fk_spec.rb +2 -2
- data/spec/public/associations/one_to_many_spec.rb +6 -5
- data/spec/public/associations/one_to_one_spec.rb +1 -1
- data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +1 -1
- data/spec/public/collection_spec.rb +7 -6
- data/spec/public/finalize_spec.rb +1 -1
- data/spec/public/model/hook_spec.rb +4 -3
- data/spec/public/model/property_spec.rb +9 -3
- data/spec/public/model/relationship_spec.rb +2 -4
- data/spec/public/model_spec.rb +1 -1
- data/spec/public/property/binary_spec.rb +1 -1
- data/spec/public/property/boolean_spec.rb +1 -1
- data/spec/public/property/class_spec.rb +1 -1
- data/spec/public/property/date_spec.rb +1 -1
- data/spec/public/property/date_time_spec.rb +1 -1
- data/spec/public/property/decimal_spec.rb +7 -6
- data/spec/public/property/discriminator_spec.rb +1 -1
- data/spec/public/property/float_spec.rb +1 -1
- data/spec/public/property/integer_spec.rb +1 -1
- data/spec/public/property/object_spec.rb +2 -2
- data/spec/public/property/serial_spec.rb +1 -1
- data/spec/public/property/string_spec.rb +1 -1
- data/spec/public/property/text_spec.rb +6 -3
- data/spec/public/property/time_spec.rb +1 -1
- data/spec/public/property_spec.rb +13 -11
- data/spec/public/resource_spec.rb +43 -1
- data/spec/public/sel_spec.rb +1 -1
- data/spec/public/setup_spec.rb +1 -1
- data/spec/public/shared/collection_shared_spec.rb +6 -1
- data/spec/semipublic/adapters/abstract_adapter_spec.rb +1 -1
- data/spec/semipublic/adapters/in_memory_adapter_spec.rb +1 -1
- data/spec/semipublic/associations/many_to_many_spec.rb +1 -1
- data/spec/semipublic/associations/many_to_one_spec.rb +1 -1
- data/spec/semipublic/associations/one_to_many_spec.rb +1 -1
- data/spec/semipublic/associations/one_to_one_spec.rb +1 -1
- data/spec/semipublic/associations/relationship_spec.rb +1 -1
- data/spec/semipublic/associations_spec.rb +1 -1
- data/spec/semipublic/collection_spec.rb +1 -1
- data/spec/semipublic/model_spec.rb +1 -1
- data/spec/semipublic/property/binary_spec.rb +1 -1
- data/spec/semipublic/property/boolean_spec.rb +1 -1
- data/spec/semipublic/property/class_spec.rb +1 -1
- data/spec/semipublic/property/date_spec.rb +1 -1
- data/spec/semipublic/property/date_time_spec.rb +1 -1
- data/spec/semipublic/property/decimal_spec.rb +6 -5
- data/spec/semipublic/property/discriminator_spec.rb +1 -1
- data/spec/semipublic/property/float_spec.rb +1 -1
- data/spec/semipublic/property/integer_spec.rb +1 -1
- data/spec/semipublic/property/lookup_spec.rb +8 -5
- data/spec/semipublic/property/serial_spec.rb +1 -1
- data/spec/semipublic/property/string_spec.rb +1 -1
- data/spec/semipublic/property/text_spec.rb +1 -1
- data/spec/semipublic/property/time_spec.rb +1 -1
- data/spec/semipublic/property_spec.rb +32 -7
- data/spec/semipublic/query/conditions/comparison_spec.rb +1 -264
- data/spec/semipublic/query/conditions/operation_spec.rb +1 -2
- data/spec/semipublic/query/path_spec.rb +27 -1
- data/spec/semipublic/query_spec.rb +87 -36
- data/spec/semipublic/resource/state/clean_spec.rb +1 -2
- data/spec/semipublic/resource/state/deleted_spec.rb +1 -2
- data/spec/semipublic/resource/state/dirty_spec.rb +1 -2
- data/spec/semipublic/resource/state/immutable_spec.rb +1 -2
- data/spec/semipublic/resource/state/transient_spec.rb +7 -2
- data/spec/semipublic/resource/state_spec.rb +1 -1
- data/spec/semipublic/resource_spec.rb +1 -1
- data/spec/unit/array_spec.rb +1 -0
- data/spec/unit/data_mapper/ordered_set/append_spec.rb +26 -0
- data/spec/unit/data_mapper/ordered_set/clear_spec.rb +24 -0
- data/spec/unit/data_mapper/ordered_set/delete_spec.rb +28 -0
- data/spec/unit/data_mapper/ordered_set/each_spec.rb +19 -0
- data/spec/unit/data_mapper/ordered_set/empty_spec.rb +20 -0
- data/spec/unit/data_mapper/ordered_set/entries_spec.rb +22 -0
- data/spec/unit/data_mapper/ordered_set/eql_spec.rb +51 -0
- data/spec/unit/data_mapper/ordered_set/equal_value_spec.rb +84 -0
- data/spec/unit/data_mapper/ordered_set/hash_spec.rb +12 -0
- data/spec/unit/data_mapper/ordered_set/include_spec.rb +23 -0
- data/spec/unit/data_mapper/ordered_set/index_spec.rb +28 -0
- data/spec/unit/data_mapper/ordered_set/initialize_spec.rb +32 -0
- data/spec/unit/data_mapper/ordered_set/merge_spec.rb +36 -0
- data/spec/unit/data_mapper/ordered_set/shared/append_spec.rb +24 -0
- data/spec/unit/data_mapper/ordered_set/shared/clear_spec.rb +9 -0
- data/spec/unit/data_mapper/ordered_set/shared/delete_spec.rb +25 -0
- data/spec/unit/data_mapper/ordered_set/shared/each_spec.rb +17 -0
- data/spec/unit/data_mapper/ordered_set/shared/empty_spec.rb +9 -0
- data/spec/unit/data_mapper/ordered_set/shared/entries_spec.rb +9 -0
- data/spec/unit/data_mapper/ordered_set/shared/include_spec.rb +9 -0
- data/spec/unit/data_mapper/ordered_set/shared/index_spec.rb +13 -0
- data/spec/unit/data_mapper/ordered_set/shared/initialize_spec.rb +28 -0
- data/spec/unit/data_mapper/ordered_set/shared/merge_spec.rb +28 -0
- data/spec/unit/data_mapper/ordered_set/shared/size_spec.rb +13 -0
- data/spec/unit/data_mapper/ordered_set/shared/to_ary_spec.rb +11 -0
- data/spec/unit/data_mapper/ordered_set/size_spec.rb +27 -0
- data/spec/unit/data_mapper/ordered_set/to_ary_spec.rb +23 -0
- data/spec/unit/data_mapper/subject_set/append_spec.rb +47 -0
- data/spec/unit/data_mapper/subject_set/clear_spec.rb +34 -0
- data/spec/unit/data_mapper/subject_set/delete_spec.rb +40 -0
- data/spec/unit/data_mapper/subject_set/each_spec.rb +30 -0
- data/spec/unit/data_mapper/subject_set/empty_spec.rb +31 -0
- data/spec/unit/data_mapper/subject_set/entries_spec.rb +31 -0
- data/spec/unit/data_mapper/subject_set/get_spec.rb +34 -0
- data/spec/unit/data_mapper/subject_set/include_spec.rb +32 -0
- data/spec/unit/data_mapper/subject_set/named_spec.rb +33 -0
- data/spec/unit/data_mapper/subject_set/shared/append_spec.rb +18 -0
- data/spec/unit/data_mapper/subject_set/shared/clear_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/delete_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/each_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/empty_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/entries_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/get_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/include_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/named_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/size_spec.rb +13 -0
- data/spec/unit/data_mapper/subject_set/shared/to_ary_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/values_at_spec.rb +44 -0
- data/spec/unit/data_mapper/subject_set/size_spec.rb +42 -0
- data/spec/unit/data_mapper/subject_set/to_ary_spec.rb +34 -0
- data/spec/unit/data_mapper/subject_set/values_at_spec.rb +57 -0
- data/spec/unit/hash_spec.rb +2 -1
- data/spec/unit/hook_spec.rb +1 -0
- data/spec/unit/lazy_array_spec.rb +2 -1
- data/spec/unit/module_spec.rb +2 -1
- data/spec/unit/object_spec.rb +1 -0
- data/spec/unit/try_dup_spec.rb +1 -0
- data/tasks/spec.rake +0 -3
- metadata +149 -52
- data/.gitignore +0 -37
- data/lib/dm-core/type.rb +0 -216
- data/lib/dm-core/types/boolean.rb +0 -9
- data/lib/dm-core/types/decimal.rb +0 -9
- data/lib/dm-core/types/discriminator.rb +0 -50
- data/lib/dm-core/types/object.rb +0 -25
- data/lib/dm-core/types/serial.rb +0 -11
- data/lib/dm-core/types/text.rb +0 -11
- data/tasks/local_gemfile.rake +0 -16
- data/tasks/metrics.rake +0 -37
@@ -1,6 +1,8 @@
|
|
1
1
|
# TODO: update Model#respond_to? to return true if method_method missing
|
2
2
|
# would handle the message
|
3
3
|
|
4
|
+
require 'dm-core/relationship_set'
|
5
|
+
|
4
6
|
module DataMapper
|
5
7
|
module Model
|
6
8
|
module Relationship
|
@@ -31,7 +33,7 @@ module DataMapper
|
|
31
33
|
|
32
34
|
@relationships.each do |repository_name, relationships|
|
33
35
|
model_relationships = model.relationships(repository_name)
|
34
|
-
relationships.each { |
|
36
|
+
relationships.each { |relationship| model_relationships << relationship }
|
35
37
|
end
|
36
38
|
|
37
39
|
super
|
@@ -42,7 +44,7 @@ module DataMapper
|
|
42
44
|
#
|
43
45
|
# @param [Symbol] repository_name
|
44
46
|
# Name of the repository for which relationships set is returned
|
45
|
-
# @return [
|
47
|
+
# @return [RelationshipSet] relationships set for given repository
|
46
48
|
#
|
47
49
|
# @api semipublic
|
48
50
|
def relationships(repository_name = default_repository_name)
|
@@ -53,7 +55,7 @@ module DataMapper
|
|
53
55
|
default_repository_name = self.default_repository_name
|
54
56
|
|
55
57
|
@relationships[repository_name] ||= if repository_name == default_repository_name
|
56
|
-
|
58
|
+
RelationshipSet.new
|
57
59
|
else
|
58
60
|
relationships(default_repository_name).dup
|
59
61
|
end
|
@@ -124,10 +126,12 @@ module DataMapper
|
|
124
126
|
Associations::OneToOne::Relationship
|
125
127
|
end
|
126
128
|
|
127
|
-
relationship =
|
129
|
+
relationship = klass.new(name, model, self, options)
|
130
|
+
|
131
|
+
relationships(repository_name) << relationship
|
128
132
|
|
129
133
|
descendants.each do |descendant|
|
130
|
-
descendant.relationships(repository_name)
|
134
|
+
descendant.relationships(repository_name) << relationship
|
131
135
|
end
|
132
136
|
|
133
137
|
create_relationship_reader(relationship)
|
@@ -161,16 +165,13 @@ module DataMapper
|
|
161
165
|
options = extract_options(args)
|
162
166
|
|
163
167
|
if options.key?(:through)
|
164
|
-
|
165
|
-
|
168
|
+
raise "#{model_name}#belongs_to with :through is deprecated, use 'has 1, :#{name}, #{options.inspect}' in #{model_name} instead (#{caller.first})"
|
169
|
+
elsif options.key?(:model) && model
|
170
|
+
raise ArgumentError, 'should not specify options[:model] if passing the model in the third argument'
|
166
171
|
end
|
167
172
|
|
168
173
|
assert_valid_options(options)
|
169
174
|
|
170
|
-
if options.key?(:model) && model
|
171
|
-
raise ArgumentError, 'should not specify options[:model] if passing the model in the third argument'
|
172
|
-
end
|
173
|
-
|
174
175
|
model ||= options.delete(:model)
|
175
176
|
|
176
177
|
repository_name = repository.name
|
@@ -179,10 +180,12 @@ module DataMapper
|
|
179
180
|
options[:child_repository_name] = repository_name
|
180
181
|
options[:parent_repository_name] = options.delete(:repository)
|
181
182
|
|
182
|
-
relationship =
|
183
|
+
relationship = Associations::ManyToOne::Relationship.new(name, self, model, options)
|
184
|
+
|
185
|
+
relationships(repository_name) << relationship
|
183
186
|
|
184
187
|
descendants.each do |descendant|
|
185
|
-
descendant.relationships(repository_name)
|
188
|
+
descendant.relationships(repository_name) << relationship
|
186
189
|
end
|
187
190
|
|
188
191
|
create_relationship_reader(relationship)
|
@@ -253,8 +256,6 @@ module DataMapper
|
|
253
256
|
# TODO: update to match Query#assert_valid_options
|
254
257
|
# - perform options normalization elsewhere
|
255
258
|
|
256
|
-
caller_method = caller[1]
|
257
|
-
|
258
259
|
if options.key?(:min) && options.key?(:max)
|
259
260
|
min = options[:min]
|
260
261
|
max = options[:max]
|
@@ -278,15 +279,9 @@ module DataMapper
|
|
278
279
|
end
|
279
280
|
|
280
281
|
if options.key?(:class_name)
|
281
|
-
options[:class_name]
|
282
|
-
|
283
|
-
options[:
|
284
|
-
end
|
285
|
-
|
286
|
-
if options.key?(:remote_name)
|
287
|
-
options[:remote_name] = options[:remote_name].to_sym
|
288
|
-
warn "+options[:remote_name]+ is deprecated, use :via instead (#{caller_method})"
|
289
|
-
options[:via] = options.delete(:remote_name)
|
282
|
+
raise "+options[:class_name]+ is deprecated, use :model instead (#{caller[1]})"
|
283
|
+
elsif options.key?(:remote_name)
|
284
|
+
raise "+options[:remote_name]+ is deprecated, use :via instead (#{caller[1]})"
|
290
285
|
end
|
291
286
|
|
292
287
|
if options.key?(:through)
|
@@ -313,6 +308,20 @@ module DataMapper
|
|
313
308
|
end
|
314
309
|
end
|
315
310
|
|
311
|
+
# Defines the anonymous module that is used to add relationships.
|
312
|
+
# Using a single module here prevents having a very large number
|
313
|
+
# of anonymous modules, where each property has their own module.
|
314
|
+
# @api private
|
315
|
+
def relationship_module
|
316
|
+
@relationship_module ||= begin
|
317
|
+
mod = Module.new
|
318
|
+
class_eval do
|
319
|
+
include mod
|
320
|
+
end
|
321
|
+
mod
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
316
325
|
# Dynamically defines reader method
|
317
326
|
#
|
318
327
|
# @api private
|
@@ -324,18 +333,16 @@ module DataMapper
|
|
324
333
|
|
325
334
|
reader_visibility = relationship.reader_visibility
|
326
335
|
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
persisted_state.get(relationships[#{name.inspect}], query)
|
338
|
-
end
|
336
|
+
relationship_module.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
337
|
+
#{reader_visibility}
|
338
|
+
def #{reader_name}(query = nil)
|
339
|
+
# TODO: when no query is passed in, return the results from
|
340
|
+
# the ivar directly. This will require that the ivar
|
341
|
+
# actually hold the resource/collection, and in the case
|
342
|
+
# of 1:1, the underlying collection is hidden in a
|
343
|
+
# private ivar, and the resource is in a known ivar
|
344
|
+
|
345
|
+
persisted_state.get(relationships[#{name.inspect}], query)
|
339
346
|
end
|
340
347
|
RUBY
|
341
348
|
end
|
@@ -351,14 +358,12 @@ module DataMapper
|
|
351
358
|
|
352
359
|
writer_visibility = relationship.writer_visibility
|
353
360
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
persisted_state.get(relationship)
|
361
|
-
end
|
361
|
+
relationship_module.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
362
|
+
#{writer_visibility}
|
363
|
+
def #{writer_name}(target)
|
364
|
+
relationship = relationships[#{name.inspect}]
|
365
|
+
self.persisted_state = persisted_state.set(relationship, target)
|
366
|
+
persisted_state.get(relationship)
|
362
367
|
end
|
363
368
|
RUBY
|
364
369
|
end
|
data/lib/dm-core/property.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'dm-core/resource'
|
2
|
+
require 'dm-core/query'
|
3
|
+
|
1
4
|
module DataMapper
|
2
5
|
# = Properties
|
3
6
|
# Properties for a model are not derived from a database structure, but
|
@@ -17,8 +20,7 @@ module DataMapper
|
|
17
20
|
# developers on your team to take a model-centric view of development.
|
18
21
|
# * it provides the ability to use Ruby's access control functions.
|
19
22
|
# * and, because DataMapper only cares about properties explicitly defined
|
20
|
-
#
|
21
|
-
# your models, DataMapper plays well with legacy databases, and shares
|
23
|
+
# in your models, DataMapper plays well with legacy databases, and shares
|
22
24
|
# databases easily with other applications.
|
23
25
|
#
|
24
26
|
# == Declaring Properties
|
@@ -30,27 +32,23 @@ module DataMapper
|
|
30
32
|
# include DataMapper::Resource
|
31
33
|
#
|
32
34
|
# property :title, String, :required => true # Cannot be null
|
33
|
-
# property :publish, Boolean, :default => false
|
35
|
+
# property :publish, Boolean, :default => false # Default value for new records is false
|
34
36
|
# end
|
35
37
|
#
|
36
38
|
# By default, DataMapper supports the following primitive (Ruby) types
|
37
|
-
# also called core
|
39
|
+
# also called core properties:
|
38
40
|
#
|
39
41
|
# * Boolean
|
40
|
-
# *
|
41
|
-
# *
|
42
|
+
# * Class (datastore primitive is the same as String. Used for Inheritance)
|
43
|
+
# * Date
|
44
|
+
# * DateTime
|
45
|
+
# * Decimal
|
42
46
|
# * Float
|
43
47
|
# * Integer
|
44
|
-
# * BigDecimal
|
45
|
-
# * DateTime
|
46
|
-
# * Date
|
47
|
-
# * Time
|
48
48
|
# * Object (marshalled out during serialization)
|
49
|
-
# *
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
# For more information about available Types, see DataMapper::Type
|
49
|
+
# * String (default length is 50)
|
50
|
+
# * Text (limit of 65k characters by default)
|
51
|
+
# * Time
|
54
52
|
#
|
55
53
|
# == Limiting Access
|
56
54
|
# Property access control is uses the same terminology Ruby does. Properties
|
@@ -205,9 +203,8 @@ module DataMapper
|
|
205
203
|
#
|
206
204
|
# end
|
207
205
|
#
|
208
|
-
# This functionality is available with the dm-validations gem
|
209
|
-
#
|
210
|
-
# documentation for dm-validations.
|
206
|
+
# This functionality is available with the dm-validations gem. For more information
|
207
|
+
# about validations, check the documentation for dm-validations.
|
211
208
|
#
|
212
209
|
# == Default Values
|
213
210
|
# To set a default for a property, use the <tt>:default</tt> key. The
|
@@ -224,7 +221,7 @@ module DataMapper
|
|
224
221
|
# Word of warning. Don't try to read the value of the property you're setting
|
225
222
|
# the default for in the proc. An infinite loop will ensue.
|
226
223
|
#
|
227
|
-
# == Embedded Values
|
224
|
+
# == Embedded Values (not implemented yet)
|
228
225
|
# As an alternative to extraneous has_one relationships, consider using an
|
229
226
|
# EmbeddedValue.
|
230
227
|
#
|
@@ -248,8 +245,6 @@ module DataMapper
|
|
248
245
|
#
|
249
246
|
# :key name of the key associated with this property.
|
250
247
|
#
|
251
|
-
# :serial if true, field value is auto incrementing
|
252
|
-
#
|
253
248
|
# :field field in the data-store which the property corresponds to
|
254
249
|
#
|
255
250
|
# :length string field length
|
@@ -278,6 +273,31 @@ module DataMapper
|
|
278
273
|
# All other keys you pass to +property+ method are stored and available
|
279
274
|
# as options[:extra_keys].
|
280
275
|
#
|
276
|
+
# == Overriding default Property options
|
277
|
+
#
|
278
|
+
# There is the ability to reconfigure a Property and it's subclasses by explicitly
|
279
|
+
# setting a value in the Property, eg:
|
280
|
+
#
|
281
|
+
# # set all String properties to have a default length of 255
|
282
|
+
# DataMapper::Property::String.length(255)
|
283
|
+
#
|
284
|
+
# # set all Boolean properties to not allow nil (force true or false)
|
285
|
+
# DataMapper::Property::Boolean.allow_nil(false)
|
286
|
+
#
|
287
|
+
# # set all properties to be required by default
|
288
|
+
# DataMapper::Property.required(true)
|
289
|
+
#
|
290
|
+
# # turn off auto-validation for all properties by default
|
291
|
+
# DataMapper::Property.auto_validation(false)
|
292
|
+
#
|
293
|
+
# # set all mutator methods to be private by default
|
294
|
+
# DataMapper::Property.writer(false)
|
295
|
+
#
|
296
|
+
# Please note that this has no effect when a subclass has explicitly
|
297
|
+
# defined it's own option. For example, setting the String length to
|
298
|
+
# 255 will not affect the Text property even though it inherits from
|
299
|
+
# String, because it sets it's own default length to 65535.
|
300
|
+
#
|
281
301
|
# == Misc. Notes
|
282
302
|
# * Properties declared as strings will default to a length of 50, rather than
|
283
303
|
# 255 (typical max varchar column size). To overload the default, pass
|
@@ -291,12 +311,8 @@ module DataMapper
|
|
291
311
|
module PassThroughLoadDump
|
292
312
|
# @api semipublic
|
293
313
|
def load(value)
|
294
|
-
|
295
|
-
|
296
|
-
typecast(value)
|
297
|
-
else
|
298
|
-
value
|
299
|
-
end
|
314
|
+
return if value.nil?
|
315
|
+
typecast(value)
|
300
316
|
end
|
301
317
|
|
302
318
|
# Stub instance method for dumping
|
@@ -307,24 +323,15 @@ module DataMapper
|
|
307
323
|
#
|
308
324
|
# @api semipublic
|
309
325
|
def dump(value)
|
310
|
-
|
311
|
-
type.dump(value, self)
|
312
|
-
else
|
313
|
-
value
|
314
|
-
end
|
326
|
+
value
|
315
327
|
end
|
316
328
|
end
|
317
329
|
|
318
330
|
include DataMapper::Assertions
|
319
331
|
include Subject
|
320
332
|
extend Chainable
|
321
|
-
extend Deprecate
|
322
333
|
extend Equalizer
|
323
334
|
|
324
|
-
deprecate :unique, :unique?
|
325
|
-
deprecate :nullable?, :allow_nil?
|
326
|
-
deprecate :value, :dump
|
327
|
-
|
328
335
|
equalize :model, :name
|
329
336
|
|
330
337
|
PRIMITIVES = [
|
@@ -349,8 +356,11 @@ module DataMapper
|
|
349
356
|
# Possible :visibility option values
|
350
357
|
VISIBILITY_OPTIONS = [ :public, :protected, :private ].to_set.freeze
|
351
358
|
|
359
|
+
# Invalid property names
|
360
|
+
INVALID_NAMES = (Resource.instance_methods + Resource.private_instance_methods + Query::OPTIONS.to_a).map { |name| name.to_s }.freeze
|
361
|
+
|
352
362
|
attr_reader :primitive, :model, :name, :instance_variable_name,
|
353
|
-
:
|
363
|
+
:reader_visibility, :writer_visibility, :options,
|
354
364
|
:default, :repository_name, :allow_nil, :allow_blank, :required
|
355
365
|
|
356
366
|
class << self
|
@@ -360,30 +370,19 @@ module DataMapper
|
|
360
370
|
|
361
371
|
# @api semipublic
|
362
372
|
def determine_class(type)
|
363
|
-
if type < DataMapper::Property::Object
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
name = DataMapper::Inflector.demodulize(type.name)
|
368
|
-
klass = find_class(name)
|
369
|
-
|
370
|
-
if !klass && type < DataMapper::Type
|
371
|
-
klass = find_class(type.primitive.name)
|
372
|
-
end
|
373
|
+
return type if type < DataMapper::Property::Object
|
374
|
+
find_class(DataMapper::Inflector.demodulize(type.name))
|
375
|
+
end
|
373
376
|
|
374
|
-
|
377
|
+
# @api private
|
378
|
+
def demodulized_names
|
379
|
+
@demodulized_names ||= {}
|
375
380
|
end
|
376
381
|
|
377
382
|
# @api semipublic
|
378
383
|
def find_class(name)
|
379
|
-
klass
|
380
|
-
|
381
|
-
end
|
382
|
-
|
383
|
-
if !klass && const_defined?(name)
|
384
|
-
klass = const_get(name)
|
385
|
-
end
|
386
|
-
|
384
|
+
klass = demodulized_names[name]
|
385
|
+
klass ||= const_get(name) if const_defined?(name)
|
387
386
|
klass
|
388
387
|
end
|
389
388
|
|
@@ -394,8 +393,28 @@ module DataMapper
|
|
394
393
|
|
395
394
|
# @api private
|
396
395
|
def inherited(descendant)
|
396
|
+
# Descendants is a tree rooted in DataMapper::Property that tracks
|
397
|
+
# inheritance. We pre-calculate each comparison value (demodulized
|
398
|
+
# class name) to achieve a Hash[]-time lookup, rather than walk the
|
399
|
+
# entire descendant tree and calculate names on-demand (expensive,
|
400
|
+
# redundant).
|
401
|
+
#
|
402
|
+
# Since the algorithm relegates property class name lookups to a flat
|
403
|
+
# namespace, we need to ensure properties defined outside of DM don't
|
404
|
+
# override built-ins (Serial, String, etc) by merely defining a property
|
405
|
+
# of a same name. We avoid this by only ever adding to the lookup
|
406
|
+
# table. Given that DM loads its own property classes first, we can
|
407
|
+
# assume that their names are "reserved" when added to the table.
|
408
|
+
#
|
409
|
+
# External property authors who want to provide "replacements" for
|
410
|
+
# builtins (e.g. in a non-DM-supported adapter) should follow the
|
411
|
+
# convention of wrapping those properties in a module, and include'ing
|
412
|
+
# the module on the model class directly. This bypasses the DM-hooked
|
413
|
+
# const_missing lookup that would normally check this table.
|
397
414
|
descendants << descendant
|
398
415
|
|
416
|
+
Property.demodulized_names[DataMapper::Inflector.demodulize(descendant.name)] ||= descendant
|
417
|
+
|
399
418
|
# inherit accepted options
|
400
419
|
descendant.accepted_options.concat(accepted_options)
|
401
420
|
|
@@ -415,13 +434,15 @@ module DataMapper
|
|
415
434
|
# create methods for each new option
|
416
435
|
args.each do |property_option|
|
417
436
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
418
|
-
def self.#{property_option}(value = Undefined)
|
419
|
-
return @#{property_option} if value.equal?(Undefined)
|
420
|
-
descendants.each do |descendant|
|
421
|
-
descendant
|
422
|
-
|
423
|
-
|
424
|
-
|
437
|
+
def self.#{property_option}(value = Undefined) # def self.unique(value = Undefined)
|
438
|
+
return @#{property_option} if value.equal?(Undefined) # return @unique if value.equal?(Undefined)
|
439
|
+
descendants.each do |descendant| # descendants.each do |descendant|
|
440
|
+
unless descendant.instance_variable_defined?(:@#{property_option}) # unless descendant.instance_variable_defined?(:@unique)
|
441
|
+
descendant.#{property_option}(value) # descendant.unique(value)
|
442
|
+
end # end
|
443
|
+
end # end
|
444
|
+
@#{property_option} = value # @unique = value
|
445
|
+
end # end
|
425
446
|
RUBY
|
426
447
|
end
|
427
448
|
|
@@ -431,20 +452,19 @@ module DataMapper
|
|
431
452
|
# @api private
|
432
453
|
def nullable(*args)
|
433
454
|
# :required is preferable to :allow_nil, but :nullable maps precisely to :allow_nil
|
434
|
-
|
435
|
-
allow_nil(*args)
|
455
|
+
raise "#nullable is deprecated, use #required instead (#{caller.first})"
|
436
456
|
end
|
437
457
|
|
438
|
-
# Gives all the options set on this
|
458
|
+
# Gives all the options set on this property
|
439
459
|
#
|
440
|
-
# @return [Hash] with all options and their values set on this
|
460
|
+
# @return [Hash] with all options and their values set on this property
|
441
461
|
#
|
442
462
|
# @api public
|
443
463
|
def options
|
444
464
|
options = {}
|
445
465
|
accepted_options.each do |method|
|
446
466
|
value = send(method)
|
447
|
-
options[method] =
|
467
|
+
options[method] = value unless value.nil?
|
448
468
|
end
|
449
469
|
options
|
450
470
|
end
|
@@ -452,9 +472,9 @@ module DataMapper
|
|
452
472
|
|
453
473
|
accept_options :primitive, *Property::OPTIONS
|
454
474
|
|
455
|
-
# A hook to allow
|
456
|
-
# Implementations are not supposed to modify the state of the
|
457
|
-
# should produce no side-effects on the
|
475
|
+
# A hook to allow properties to extend or modify the model it's bound to.
|
476
|
+
# Implementations are not supposed to modify the state of the property
|
477
|
+
# class, and should produce no side-effects on the property instance.
|
458
478
|
def bind
|
459
479
|
# no op
|
460
480
|
end
|
@@ -464,21 +484,14 @@ module DataMapper
|
|
464
484
|
# @return [String] name of field in data-store
|
465
485
|
#
|
466
486
|
# @api semipublic
|
467
|
-
def field(repository_name =
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
unless repository_name.equal?(Undefined)
|
472
|
-
warn "Passing in +repository_name+ to #{klass}#field is deprecated (#{caller[0]})"
|
473
|
-
|
474
|
-
if repository_name != self_repository_name
|
475
|
-
raise ArgumentError, "Mismatching +repository_name+ with #{klass}#repository_name (#{repository_name.inspect} != #{self_repository_name.inspect})"
|
476
|
-
end
|
487
|
+
def field(repository_name = nil)
|
488
|
+
if repository_name
|
489
|
+
raise "Passing in +repository_name+ to #{self.class}#field is deprecated (#{caller.first})"
|
477
490
|
end
|
478
491
|
|
479
492
|
# defer setting the field with the adapter specific naming
|
480
493
|
# conventions until after the adapter has been setup
|
481
|
-
@field ||= model.field_naming_convention(
|
494
|
+
@field ||= model.field_naming_convention(self.repository_name).call(self).freeze
|
482
495
|
end
|
483
496
|
|
484
497
|
# Returns true if property is unique. Serial properties and keys
|
@@ -492,19 +505,6 @@ module DataMapper
|
|
492
505
|
!!@unique
|
493
506
|
end
|
494
507
|
|
495
|
-
# Returns the hash of the property name
|
496
|
-
#
|
497
|
-
# This is necessary to allow comparisons between different properties
|
498
|
-
# in different models, having the same base model
|
499
|
-
#
|
500
|
-
# @return [Integer]
|
501
|
-
# the property name hash
|
502
|
-
#
|
503
|
-
# @api semipublic
|
504
|
-
def hash
|
505
|
-
name.hash
|
506
|
-
end
|
507
|
-
|
508
508
|
# Returns index name if property has index.
|
509
509
|
#
|
510
510
|
# @return [Boolean, Symbol, Array]
|
@@ -514,9 +514,7 @@ module DataMapper
|
|
514
514
|
# returns false if the property does not belong to any indexes
|
515
515
|
#
|
516
516
|
# @api public
|
517
|
-
|
518
|
-
@index
|
519
|
-
end
|
517
|
+
attr_reader :index
|
520
518
|
|
521
519
|
# Returns true if property has unique index. Serial properties and
|
522
520
|
# keys are unique by default.
|
@@ -528,9 +526,7 @@ module DataMapper
|
|
528
526
|
# returns false if the property does not belong to any indexes
|
529
527
|
#
|
530
528
|
# @api public
|
531
|
-
|
532
|
-
@unique_index
|
533
|
-
end
|
529
|
+
attr_reader :unique_index
|
534
530
|
|
535
531
|
# Returns whether or not the property is to be lazy-loaded
|
536
532
|
#
|
@@ -592,16 +588,6 @@ module DataMapper
|
|
592
588
|
@allow_blank
|
593
589
|
end
|
594
590
|
|
595
|
-
# Returns whether or not the property is custom (not provided by dm-core)
|
596
|
-
#
|
597
|
-
# @return [Boolean]
|
598
|
-
# whether or not the property is custom
|
599
|
-
#
|
600
|
-
# @api public
|
601
|
-
def custom?
|
602
|
-
@custom
|
603
|
-
end
|
604
|
-
|
605
591
|
# Standardized reader method for the property
|
606
592
|
#
|
607
593
|
# @param [Resource] resource
|
@@ -702,9 +688,7 @@ module DataMapper
|
|
702
688
|
|
703
689
|
# @api semipublic
|
704
690
|
def typecast(value)
|
705
|
-
if
|
706
|
-
@type.typecast(value, self)
|
707
|
-
elsif value.nil? || primitive?(value)
|
691
|
+
if value.nil? || primitive?(value)
|
708
692
|
value
|
709
693
|
elsif respond_to?(:typecast_to_primitive)
|
710
694
|
typecast_to_primitive(value)
|
@@ -754,7 +738,7 @@ module DataMapper
|
|
754
738
|
end
|
755
739
|
|
756
740
|
chainable do
|
757
|
-
def self.new(model, name, options = {}
|
741
|
+
def self.new(model, name, options = {})
|
758
742
|
super
|
759
743
|
end
|
760
744
|
end
|
@@ -762,23 +746,16 @@ module DataMapper
|
|
762
746
|
protected
|
763
747
|
|
764
748
|
# @api semipublic
|
765
|
-
def initialize(model, name, options = {}
|
749
|
+
def initialize(model, name, options = {})
|
766
750
|
options = options.to_hash.dup
|
767
751
|
|
768
|
-
if
|
769
|
-
|
770
|
-
@type = type
|
771
|
-
end
|
772
|
-
|
773
|
-
reserved_method_names = DataMapper::Resource.instance_methods + DataMapper::Resource.private_instance_methods
|
774
|
-
if reserved_method_names.map { |m| m.to_s }.include?(name.to_s)
|
775
|
-
raise ArgumentError, "+name+ was #{name.inspect}, which cannot be used as a property name since it collides with an existing method"
|
752
|
+
if INVALID_NAMES.include?(name.to_s)
|
753
|
+
raise ArgumentError, "+name+ was #{name.inspect}, which cannot be used as a property name since it collides with an existing method or a query option"
|
776
754
|
end
|
777
755
|
|
778
756
|
assert_valid_options(options)
|
779
757
|
|
780
758
|
predefined_options = self.class.options
|
781
|
-
predefined_options.merge!(@type.options) if @type
|
782
759
|
|
783
760
|
@repository_name = model.repository_name
|
784
761
|
@model = model
|
@@ -786,8 +763,7 @@ module DataMapper
|
|
786
763
|
@options = predefined_options.merge(options).freeze
|
787
764
|
@instance_variable_name = "@#{@name}".freeze
|
788
765
|
|
789
|
-
@primitive = self.class.primitive
|
790
|
-
@custom = !@type.nil?
|
766
|
+
@primitive = self.class.primitive
|
791
767
|
@field = @options[:field].freeze
|
792
768
|
@default = @options[:default]
|
793
769
|
|
@@ -803,7 +779,7 @@ module DataMapper
|
|
803
779
|
|
804
780
|
determine_visibility
|
805
781
|
|
806
|
-
|
782
|
+
bind
|
807
783
|
end
|
808
784
|
|
809
785
|
# @api private
|
@@ -831,7 +807,7 @@ module DataMapper
|
|
831
807
|
raise ArgumentError, "options[:#{key}] must be either true or false"
|
832
808
|
end
|
833
809
|
|
834
|
-
if key == :required && (keys
|
810
|
+
if key == :required && (keys.include?(:allow_nil) || keys.include?(:allow_blank))
|
835
811
|
raise ArgumentError, 'options[:required] cannot be mixed with :allow_nil or :allow_blank'
|
836
812
|
end
|
837
813
|
|