datamapper-dm-core 0.9.11 → 0.10.0
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/.autotest +17 -14
- data/.gitignore +3 -1
- data/FAQ +6 -5
- data/History.txt +5 -39
- data/Manifest.txt +67 -76
- data/QUICKLINKS +1 -1
- data/README.txt +21 -15
- data/Rakefile +16 -15
- data/SPECS +2 -29
- data/TODO +1 -1
- data/dm-core.gemspec +11 -15
- data/lib/dm-core/adapters/abstract_adapter.rb +182 -185
- data/lib/dm-core/adapters/data_objects_adapter.rb +482 -534
- data/lib/dm-core/adapters/in_memory_adapter.rb +90 -69
- data/lib/dm-core/adapters/mysql_adapter.rb +22 -115
- data/lib/dm-core/adapters/oracle_adapter.rb +249 -0
- data/lib/dm-core/adapters/postgres_adapter.rb +7 -173
- data/lib/dm-core/adapters/sqlite3_adapter.rb +4 -97
- data/lib/dm-core/adapters/yaml_adapter.rb +116 -0
- data/lib/dm-core/adapters.rb +135 -16
- data/lib/dm-core/associations/many_to_many.rb +372 -90
- data/lib/dm-core/associations/many_to_one.rb +220 -73
- data/lib/dm-core/associations/one_to_many.rb +319 -255
- data/lib/dm-core/associations/one_to_one.rb +66 -53
- data/lib/dm-core/associations/relationship.rb +560 -158
- data/lib/dm-core/collection.rb +1104 -381
- data/lib/dm-core/core_ext/kernel.rb +12 -0
- data/lib/dm-core/core_ext/symbol.rb +10 -0
- data/lib/dm-core/identity_map.rb +4 -34
- data/lib/dm-core/migrations.rb +1283 -0
- data/lib/dm-core/model/descendant_set.rb +81 -0
- data/lib/dm-core/model/hook.rb +45 -0
- data/lib/dm-core/model/is.rb +32 -0
- data/lib/dm-core/model/property.rb +248 -0
- data/lib/dm-core/model/relationship.rb +335 -0
- data/lib/dm-core/model/scope.rb +90 -0
- data/lib/dm-core/model.rb +570 -369
- data/lib/dm-core/property.rb +753 -280
- data/lib/dm-core/property_set.rb +141 -98
- data/lib/dm-core/query/conditions/comparison.rb +814 -0
- data/lib/dm-core/query/conditions/operation.rb +247 -0
- data/lib/dm-core/query/direction.rb +43 -0
- data/lib/dm-core/query/operator.rb +42 -0
- data/lib/dm-core/query/path.rb +102 -0
- data/lib/dm-core/query/sort.rb +45 -0
- data/lib/dm-core/query.rb +974 -492
- data/lib/dm-core/repository.rb +147 -107
- data/lib/dm-core/resource.rb +644 -429
- data/lib/dm-core/spec/adapter_shared_spec.rb +294 -0
- data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +106 -0
- data/lib/dm-core/support/chainable.rb +20 -0
- data/lib/dm-core/support/deprecate.rb +12 -0
- data/lib/dm-core/support/equalizer.rb +23 -0
- data/lib/dm-core/support/logger.rb +13 -0
- data/lib/dm-core/{naming_conventions.rb → support/naming_conventions.rb} +6 -6
- data/lib/dm-core/transaction.rb +333 -92
- data/lib/dm-core/type.rb +98 -60
- data/lib/dm-core/types/boolean.rb +1 -1
- data/lib/dm-core/types/discriminator.rb +34 -20
- data/lib/dm-core/types/object.rb +7 -4
- data/lib/dm-core/types/paranoid_boolean.rb +11 -9
- data/lib/dm-core/types/paranoid_datetime.rb +11 -9
- data/lib/dm-core/types/serial.rb +3 -3
- data/lib/dm-core/types/text.rb +3 -4
- data/lib/dm-core/version.rb +1 -1
- data/lib/dm-core.rb +106 -110
- data/script/performance.rb +102 -109
- data/script/profile.rb +169 -38
- data/spec/lib/adapter_helpers.rb +105 -0
- data/spec/lib/collection_helpers.rb +18 -0
- data/spec/lib/counter_adapter.rb +34 -0
- data/spec/lib/pending_helpers.rb +27 -0
- data/spec/lib/rspec_immediate_feedback_formatter.rb +53 -0
- data/spec/public/associations/many_to_many_spec.rb +193 -0
- data/spec/public/associations/many_to_one_spec.rb +73 -0
- data/spec/public/associations/one_to_many_spec.rb +77 -0
- data/spec/public/associations/one_to_one_spec.rb +156 -0
- data/spec/public/collection_spec.rb +65 -0
- data/spec/public/model/relationship_spec.rb +924 -0
- data/spec/public/model_spec.rb +159 -0
- data/spec/public/property_spec.rb +829 -0
- data/spec/public/resource_spec.rb +71 -0
- data/spec/public/sel_spec.rb +44 -0
- data/spec/public/setup_spec.rb +145 -0
- data/spec/public/shared/association_collection_shared_spec.rb +317 -0
- data/spec/public/shared/collection_shared_spec.rb +1723 -0
- data/spec/public/shared/finder_shared_spec.rb +1619 -0
- data/spec/public/shared/resource_shared_spec.rb +924 -0
- data/spec/public/shared/sel_shared_spec.rb +112 -0
- data/spec/public/transaction_spec.rb +129 -0
- data/spec/public/types/discriminator_spec.rb +130 -0
- data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
- data/spec/semipublic/adapters/in_memory_adapter_spec.rb +12 -0
- data/spec/semipublic/adapters/mysql_adapter_spec.rb +17 -0
- data/spec/semipublic/adapters/oracle_adapter_spec.rb +194 -0
- data/spec/semipublic/adapters/postgres_adapter_spec.rb +17 -0
- data/spec/semipublic/adapters/sqlite3_adapter_spec.rb +17 -0
- data/spec/semipublic/adapters/yaml_adapter_spec.rb +12 -0
- data/spec/semipublic/associations/many_to_one_spec.rb +53 -0
- data/spec/semipublic/associations/relationship_spec.rb +194 -0
- data/spec/semipublic/associations_spec.rb +177 -0
- data/spec/semipublic/collection_spec.rb +142 -0
- data/spec/semipublic/property_spec.rb +61 -0
- data/spec/semipublic/query/conditions_spec.rb +528 -0
- data/spec/semipublic/query/path_spec.rb +443 -0
- data/spec/semipublic/query_spec.rb +2626 -0
- data/spec/semipublic/resource_spec.rb +47 -0
- data/spec/semipublic/shared/resource_shared_spec.rb +126 -0
- data/spec/spec.opts +3 -1
- data/spec/spec_helper.rb +80 -57
- data/tasks/ci.rb +19 -31
- data/tasks/dm.rb +43 -48
- data/tasks/doc.rb +8 -11
- data/tasks/gemspec.rb +5 -5
- data/tasks/hoe.rb +15 -16
- data/tasks/install.rb +8 -10
- metadata +72 -93
- data/lib/dm-core/associations/relationship_chain.rb +0 -81
- data/lib/dm-core/associations.rb +0 -207
- data/lib/dm-core/auto_migrations.rb +0 -105
- data/lib/dm-core/dependency_queue.rb +0 -32
- data/lib/dm-core/hook.rb +0 -11
- data/lib/dm-core/is.rb +0 -16
- data/lib/dm-core/logger.rb +0 -232
- data/lib/dm-core/migrations/destructive_migrations.rb +0 -17
- data/lib/dm-core/migrator.rb +0 -29
- data/lib/dm-core/scope.rb +0 -58
- data/lib/dm-core/support/array.rb +0 -13
- data/lib/dm-core/support/assertions.rb +0 -8
- data/lib/dm-core/support/errors.rb +0 -23
- data/lib/dm-core/support/kernel.rb +0 -11
- data/lib/dm-core/support/symbol.rb +0 -41
- data/lib/dm-core/support.rb +0 -7
- data/lib/dm-core/type_map.rb +0 -80
- data/lib/dm-core/types.rb +0 -19
- data/script/all +0 -4
- data/spec/integration/association_spec.rb +0 -1382
- data/spec/integration/association_through_spec.rb +0 -203
- data/spec/integration/associations/many_to_many_spec.rb +0 -449
- data/spec/integration/associations/many_to_one_spec.rb +0 -163
- data/spec/integration/associations/one_to_many_spec.rb +0 -188
- data/spec/integration/auto_migrations_spec.rb +0 -413
- data/spec/integration/collection_spec.rb +0 -1073
- data/spec/integration/data_objects_adapter_spec.rb +0 -32
- data/spec/integration/dependency_queue_spec.rb +0 -46
- data/spec/integration/model_spec.rb +0 -197
- data/spec/integration/mysql_adapter_spec.rb +0 -85
- data/spec/integration/postgres_adapter_spec.rb +0 -731
- data/spec/integration/property_spec.rb +0 -253
- data/spec/integration/query_spec.rb +0 -514
- data/spec/integration/repository_spec.rb +0 -61
- data/spec/integration/resource_spec.rb +0 -513
- data/spec/integration/sqlite3_adapter_spec.rb +0 -352
- data/spec/integration/sti_spec.rb +0 -273
- data/spec/integration/strategic_eager_loading_spec.rb +0 -156
- data/spec/integration/transaction_spec.rb +0 -75
- data/spec/integration/type_spec.rb +0 -275
- data/spec/lib/logging_helper.rb +0 -18
- data/spec/lib/mock_adapter.rb +0 -27
- data/spec/lib/model_loader.rb +0 -100
- data/spec/lib/publicize_methods.rb +0 -28
- data/spec/models/content.rb +0 -16
- data/spec/models/vehicles.rb +0 -34
- data/spec/models/zoo.rb +0 -48
- data/spec/unit/adapters/abstract_adapter_spec.rb +0 -133
- data/spec/unit/adapters/adapter_shared_spec.rb +0 -15
- data/spec/unit/adapters/data_objects_adapter_spec.rb +0 -632
- data/spec/unit/adapters/in_memory_adapter_spec.rb +0 -98
- data/spec/unit/adapters/postgres_adapter_spec.rb +0 -133
- data/spec/unit/associations/many_to_many_spec.rb +0 -32
- data/spec/unit/associations/many_to_one_spec.rb +0 -159
- data/spec/unit/associations/one_to_many_spec.rb +0 -393
- data/spec/unit/associations/one_to_one_spec.rb +0 -7
- data/spec/unit/associations/relationship_spec.rb +0 -71
- data/spec/unit/associations_spec.rb +0 -242
- data/spec/unit/auto_migrations_spec.rb +0 -111
- data/spec/unit/collection_spec.rb +0 -182
- data/spec/unit/data_mapper_spec.rb +0 -35
- data/spec/unit/identity_map_spec.rb +0 -126
- data/spec/unit/is_spec.rb +0 -80
- data/spec/unit/migrator_spec.rb +0 -33
- data/spec/unit/model_spec.rb +0 -321
- data/spec/unit/naming_conventions_spec.rb +0 -36
- data/spec/unit/property_set_spec.rb +0 -90
- data/spec/unit/property_spec.rb +0 -753
- data/spec/unit/query_spec.rb +0 -571
- data/spec/unit/repository_spec.rb +0 -93
- data/spec/unit/resource_spec.rb +0 -649
- data/spec/unit/scope_spec.rb +0 -142
- data/spec/unit/transaction_spec.rb +0 -493
- data/spec/unit/type_map_spec.rb +0 -114
- data/spec/unit/type_spec.rb +0 -119
data/lib/dm-core/model.rb
CHANGED
|
@@ -1,316 +1,421 @@
|
|
|
1
|
-
|
|
1
|
+
# TODO: add Model#create!, Model#update, Model#update!, Model#destroy and Model#destroy!
|
|
2
2
|
|
|
3
3
|
module DataMapper
|
|
4
4
|
module Model
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
#
|
|
8
|
-
# included.
|
|
5
|
+
extend Chainable
|
|
6
|
+
|
|
7
|
+
# Creates a new Model class with default_storage_name +storage_name+
|
|
9
8
|
#
|
|
10
|
-
#
|
|
11
|
-
# still retaining a self.extended method.
|
|
9
|
+
# If a block is passed, it will be eval'd in the context of the new Model
|
|
12
10
|
#
|
|
13
|
-
# @param [
|
|
14
|
-
#
|
|
11
|
+
# @param [Proc] block
|
|
12
|
+
# a block that will be eval'd in the context of the new Model class
|
|
15
13
|
#
|
|
16
|
-
# @return [
|
|
17
|
-
#
|
|
18
|
-
#-
|
|
19
|
-
# @api public
|
|
14
|
+
# @return [Model]
|
|
15
|
+
# the newly created Model class
|
|
20
16
|
#
|
|
21
|
-
#
|
|
22
|
-
def self.
|
|
23
|
-
|
|
24
|
-
true
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def self.extra_extensions
|
|
28
|
-
@extra_extensions ||= []
|
|
29
|
-
end
|
|
30
|
-
|
|
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, {})
|
|
35
|
-
extra_extensions.each { |extension| model.extend(extension) }
|
|
36
|
-
end
|
|
17
|
+
# @api semipublic
|
|
18
|
+
def self.new(storage_name = nil, &block)
|
|
19
|
+
model = Class.new
|
|
37
20
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
target.instance_variable_set(:@properties, {})
|
|
41
|
-
target.instance_variable_set(:@base_model, self.base_model)
|
|
42
|
-
target.instance_variable_set(:@paranoid_properties, @paranoid_properties)
|
|
43
|
-
target.instance_variable_set(:@field_naming_conventions, @field_naming_conventions.dup)
|
|
21
|
+
model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
22
|
+
include DataMapper::Resource
|
|
44
23
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
validators.each { |validator| target.validators.context(context) << validator }
|
|
24
|
+
def self.name
|
|
25
|
+
to_s
|
|
48
26
|
end
|
|
49
|
-
|
|
27
|
+
RUBY
|
|
50
28
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
29
|
+
if storage_name
|
|
30
|
+
warn "Passing in +storage_name+ to #{name}.new is deprecated (#{caller[0]})"
|
|
31
|
+
model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
32
|
+
def self.default_storage_name
|
|
33
|
+
#{Extlib::Inflection.classify(storage_name).inspect}.freeze
|
|
56
34
|
end
|
|
57
|
-
|
|
35
|
+
RUBY
|
|
58
36
|
end
|
|
59
37
|
|
|
60
|
-
if
|
|
61
|
-
duped_relationships = {}
|
|
62
|
-
@relationships.each do |repository_name,relationships|
|
|
63
|
-
relationships.each do |name, relationship|
|
|
64
|
-
dup = relationship.dup
|
|
65
|
-
dup.instance_variable_set(:@child_model, target) if dup.instance_variable_get(:@child_model) == self
|
|
66
|
-
dup.instance_variable_set(:@parent_model, target) if dup.instance_variable_get(:@parent_model) == self
|
|
67
|
-
duped_relationships[repository_name] ||= {}
|
|
68
|
-
duped_relationships[repository_name][name] = dup
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
target.instance_variable_set(:@relationships, duped_relationships)
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def self.new(storage_name, &block)
|
|
76
|
-
model = Class.new
|
|
77
|
-
model.send(:include, Resource)
|
|
78
|
-
model.class_eval <<-EOS, __FILE__, __LINE__
|
|
79
|
-
def self.default_storage_name
|
|
80
|
-
#{Extlib::Inflection.classify(storage_name).inspect}
|
|
81
|
-
end
|
|
82
|
-
EOS
|
|
83
|
-
model.instance_eval(&block) if block_given?
|
|
38
|
+
model.instance_eval(&block) if block
|
|
84
39
|
model
|
|
85
40
|
end
|
|
86
41
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
42
|
+
# Return all models that extend the Model module
|
|
43
|
+
#
|
|
44
|
+
# class Foo
|
|
45
|
+
# include DataMapper::Resource
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# DataMapper::Model.descendants.first #=> Foo
|
|
49
|
+
#
|
|
50
|
+
# @return [DescendantSet]
|
|
51
|
+
# Set containing the descendant models
|
|
52
|
+
#
|
|
53
|
+
# @api private
|
|
54
|
+
def self.descendants
|
|
55
|
+
@descendants ||= DescendantSet.new
|
|
93
56
|
end
|
|
94
57
|
|
|
95
|
-
|
|
96
|
-
# Get the repository with a given name, or the default one for the current
|
|
97
|
-
# context, or the default one for this class.
|
|
58
|
+
# Return all models that inherit from a Model
|
|
98
59
|
#
|
|
99
|
-
#
|
|
100
|
-
#
|
|
60
|
+
# class Foo
|
|
61
|
+
# include DataMapper::Resource
|
|
62
|
+
# end
|
|
101
63
|
#
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
|
|
105
|
-
#
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
|
117
|
-
end
|
|
64
|
+
# class Bar < Foo
|
|
65
|
+
# end
|
|
66
|
+
#
|
|
67
|
+
# Foo.descendants.first #=> Bar
|
|
68
|
+
#
|
|
69
|
+
# @return [Set]
|
|
70
|
+
# Set containing the descendant classes
|
|
71
|
+
#
|
|
72
|
+
# @api private
|
|
73
|
+
attr_reader :descendants
|
|
118
74
|
|
|
119
|
-
|
|
120
|
-
# the name of the storage recepticle for this resource. IE. table name, for database stores
|
|
75
|
+
# Appends a module for inclusion into the model class after Resource.
|
|
121
76
|
#
|
|
122
|
-
#
|
|
123
|
-
|
|
124
|
-
|
|
77
|
+
# This is a useful way to extend Resource while still retaining a
|
|
78
|
+
# self.included method.
|
|
79
|
+
#
|
|
80
|
+
# @param [Module] inclusions
|
|
81
|
+
# the module that is to be appended to the module after Resource
|
|
82
|
+
#
|
|
83
|
+
# @return [Boolean]
|
|
84
|
+
# true if the inclusions have been successfully appended to the list
|
|
85
|
+
#
|
|
86
|
+
# @api semipublic
|
|
87
|
+
def self.append_inclusions(*inclusions)
|
|
88
|
+
extra_inclusions.concat inclusions
|
|
89
|
+
true
|
|
125
90
|
end
|
|
126
91
|
|
|
127
|
-
|
|
128
|
-
# the names of the storage recepticles for this resource across all repositories
|
|
92
|
+
# The current registered extra inclusions
|
|
129
93
|
#
|
|
130
|
-
# @return
|
|
131
|
-
|
|
132
|
-
|
|
94
|
+
# @return [Set]
|
|
95
|
+
#
|
|
96
|
+
# @api private
|
|
97
|
+
def self.extra_inclusions
|
|
98
|
+
@extra_inclusions ||= []
|
|
133
99
|
end
|
|
134
100
|
|
|
135
|
-
|
|
136
|
-
# The field naming conventions for this resource across all repositories.
|
|
101
|
+
# Extends the model with this module after Resource has been included.
|
|
137
102
|
#
|
|
138
|
-
#
|
|
139
|
-
|
|
140
|
-
|
|
103
|
+
# This is a useful way to extend Model while still retaining a self.extended method.
|
|
104
|
+
#
|
|
105
|
+
# @param [Module] extensions
|
|
106
|
+
# List of modules that will extend the model after it is extended by Model
|
|
107
|
+
#
|
|
108
|
+
# @return [Boolean]
|
|
109
|
+
# whether or not the inclusions have been successfully appended to the list
|
|
110
|
+
#
|
|
111
|
+
# @api semipublic
|
|
112
|
+
def self.append_extensions(*extensions)
|
|
113
|
+
extra_extensions.concat extensions
|
|
114
|
+
true
|
|
141
115
|
end
|
|
142
116
|
|
|
143
|
-
|
|
144
|
-
# defines a property on the resource
|
|
117
|
+
# The current registered extra extensions
|
|
145
118
|
#
|
|
146
|
-
# @
|
|
147
|
-
#
|
|
148
|
-
# @
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
create_property_getter(property)
|
|
154
|
-
create_property_setter(property)
|
|
155
|
-
|
|
156
|
-
properties(repository_name)[property.name] = property
|
|
157
|
-
@_valid_relations = false
|
|
119
|
+
# @return [Set]
|
|
120
|
+
#
|
|
121
|
+
# @api private
|
|
122
|
+
def self.extra_extensions
|
|
123
|
+
@extra_extensions ||= []
|
|
124
|
+
end
|
|
158
125
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
next if repository_name == default_repository_name
|
|
164
|
-
properties << property unless properties.has_property?(property.name)
|
|
165
|
-
end
|
|
166
|
-
end
|
|
126
|
+
# TODO: document
|
|
127
|
+
# @api private
|
|
128
|
+
def self.extended(model)
|
|
129
|
+
descendants << model
|
|
167
130
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
context = options.fetch(:lazy, :default)
|
|
174
|
-
context = :default if context == true
|
|
131
|
+
model.instance_variable_set(:@valid, false)
|
|
132
|
+
model.instance_variable_set(:@base_model, model)
|
|
133
|
+
model.instance_variable_set(:@storage_names, {})
|
|
134
|
+
model.instance_variable_set(:@default_order, {})
|
|
135
|
+
model.instance_variable_set(:@descendants, descendants.class.new(model, descendants))
|
|
175
136
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
end
|
|
137
|
+
extra_extensions.each { |mod| model.extend(mod) }
|
|
138
|
+
extra_inclusions.each { |mod| model.send(:include, mod) }
|
|
139
|
+
end
|
|
180
140
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
descendants
|
|
186
|
-
|
|
187
|
-
|
|
141
|
+
# TODO: document
|
|
142
|
+
# @api private
|
|
143
|
+
chainable do
|
|
144
|
+
def inherited(model)
|
|
145
|
+
descendants << model
|
|
146
|
+
|
|
147
|
+
model.instance_variable_set(:@valid, false)
|
|
148
|
+
model.instance_variable_set(:@base_model, base_model)
|
|
149
|
+
model.instance_variable_set(:@storage_names, @storage_names.dup)
|
|
150
|
+
model.instance_variable_set(:@default_order, @default_order.dup)
|
|
151
|
+
model.instance_variable_set(:@descendants, descendants.class.new(model, descendants))
|
|
152
|
+
|
|
153
|
+
# TODO: move this into dm-validations
|
|
154
|
+
if respond_to?(:validators)
|
|
155
|
+
validators.contexts.each do |context, validators|
|
|
156
|
+
model.validators.context(context).concat(validators)
|
|
157
|
+
end
|
|
188
158
|
end
|
|
189
159
|
end
|
|
190
|
-
|
|
191
|
-
property
|
|
192
160
|
end
|
|
193
161
|
|
|
194
|
-
|
|
195
|
-
|
|
162
|
+
# Gets the name of the storage receptacle for this resource in the given
|
|
163
|
+
# Repository (ie., table name, for database stores).
|
|
164
|
+
#
|
|
165
|
+
# @return [String]
|
|
166
|
+
# the storage name (ie., table name, for database stores) associated with
|
|
167
|
+
# this resource in the given repository
|
|
168
|
+
#
|
|
169
|
+
# @api public
|
|
170
|
+
def storage_name(repository_name = default_repository_name)
|
|
171
|
+
storage_names[repository_name] ||= repository(repository_name).adapter.resource_naming_convention.call(default_storage_name).freeze
|
|
196
172
|
end
|
|
197
173
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
|
174
|
+
# the names of the storage receptacles for this resource across all repositories
|
|
175
|
+
#
|
|
176
|
+
# @return [Hash(Symbol => String)]
|
|
177
|
+
# All available names of storage recepticles
|
|
178
|
+
#
|
|
179
|
+
# @api public
|
|
180
|
+
def storage_names
|
|
181
|
+
@storage_names
|
|
214
182
|
end
|
|
215
183
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
184
|
+
# Grab a single record by its key. Supports natural and composite key
|
|
185
|
+
# lookups as well.
|
|
186
|
+
#
|
|
187
|
+
# Zoo.get(1) # get the zoo with primary key of 1.
|
|
188
|
+
# Zoo.get!(1) # Or get! if you want an ObjectNotFoundError on failure
|
|
189
|
+
# Zoo.get('DFW') # wow, support for natural primary keys
|
|
190
|
+
# Zoo.get('Metro', 'DFW') # more wow, composite key look-up
|
|
191
|
+
#
|
|
192
|
+
# @param [Object] *key
|
|
193
|
+
# The primary key or keys to use for lookup
|
|
194
|
+
#
|
|
195
|
+
# @return [Resource,NilClass]
|
|
196
|
+
# A single model that was found
|
|
197
|
+
# If no instance was found matching +key+
|
|
198
|
+
#
|
|
199
|
+
# @api public
|
|
200
|
+
def get(*key)
|
|
201
|
+
repository = self.repository
|
|
202
|
+
key = self.key(repository.name).typecast(key)
|
|
219
203
|
|
|
220
|
-
|
|
221
|
-
def properties_with_subclasses(repository_name = default_repository_name)
|
|
222
|
-
properties = PropertySet.new
|
|
223
|
-
([ self ].to_set + (respond_to?(:descendants) ? descendants : [])).each do |model|
|
|
224
|
-
model.relationships(repository_name).each_value { |relationship| relationship.child_key }
|
|
225
|
-
model.many_to_one_relationships.each do |relationship| relationship.child_key end
|
|
226
|
-
model.properties(repository_name).each do |property|
|
|
227
|
-
properties << property unless properties.has_property?(property.name)
|
|
228
|
-
end
|
|
229
|
-
end
|
|
230
|
-
properties
|
|
204
|
+
repository.identity_map(self)[key] || first(key_conditions(repository, key))
|
|
231
205
|
end
|
|
232
206
|
|
|
233
|
-
|
|
234
|
-
|
|
207
|
+
# Grab a single record just like #get, but raise an ObjectNotFoundError
|
|
208
|
+
# if the record doesn't exist.
|
|
209
|
+
#
|
|
210
|
+
# @param [Object] *key
|
|
211
|
+
# The primary key or keys to use for lookup
|
|
212
|
+
# @return [Resource]
|
|
213
|
+
# A single model that was found
|
|
214
|
+
# @raise [ObjectNotFoundError]
|
|
215
|
+
# The record was not found
|
|
216
|
+
#
|
|
217
|
+
# @api public
|
|
218
|
+
def get!(*key)
|
|
219
|
+
get(*key) || raise(ObjectNotFoundError, "Could not find #{self.name} with key #{key.inspect}")
|
|
235
220
|
end
|
|
236
221
|
|
|
237
|
-
def
|
|
238
|
-
|
|
222
|
+
def [](*args)
|
|
223
|
+
all[*args]
|
|
239
224
|
end
|
|
240
225
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
226
|
+
alias slice []
|
|
227
|
+
|
|
228
|
+
def at(*args)
|
|
229
|
+
all.at(*args)
|
|
244
230
|
end
|
|
245
231
|
|
|
246
|
-
def
|
|
247
|
-
|
|
248
|
-
repository.identity_map(self).get(key) || first(to_query(repository, key))
|
|
232
|
+
def reverse
|
|
233
|
+
all.reverse
|
|
249
234
|
end
|
|
250
235
|
|
|
251
|
-
|
|
252
|
-
|
|
236
|
+
# TODO: spec this
|
|
237
|
+
def entries
|
|
238
|
+
all.entries
|
|
253
239
|
end
|
|
254
240
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
241
|
+
alias to_a entries
|
|
242
|
+
|
|
243
|
+
# Find a set of records matching an optional set of conditions. Additionally,
|
|
244
|
+
# specify the order that the records are return.
|
|
245
|
+
#
|
|
246
|
+
# Zoo.all # all zoos
|
|
247
|
+
# Zoo.all(:open => true) # all zoos that are open
|
|
248
|
+
# Zoo.all(:opened_on => start..end) # all zoos that opened on a date in the date-range
|
|
249
|
+
# Zoo.all(:order => [ :tiger_count.desc ]) # Ordered by tiger_count
|
|
250
|
+
#
|
|
251
|
+
# @param [Hash] query
|
|
252
|
+
# A hash describing the conditions and order for the query
|
|
253
|
+
# @return [Collection]
|
|
254
|
+
# A set of records found matching the conditions in +query+
|
|
255
|
+
# @see Collection
|
|
256
|
+
#
|
|
257
|
+
# @api public
|
|
258
|
+
def all(query = nil)
|
|
259
|
+
if query.nil? || (query.kind_of?(Hash) && query.empty?)
|
|
260
|
+
# TODO: after adding Enumerable methods to Model, try to return self here
|
|
261
|
+
new_collection(self.query.dup)
|
|
262
|
+
else
|
|
263
|
+
new_collection(scoped_query(query))
|
|
264
|
+
end
|
|
258
265
|
end
|
|
259
266
|
|
|
267
|
+
# Return the first Resource or the first N Resources for the Model with an optional query
|
|
268
|
+
#
|
|
269
|
+
# When there are no arguments, return the first Resource in the
|
|
270
|
+
# Model. When the first argument is an Integer, return a
|
|
271
|
+
# Collection containing the first N Resources. When the last
|
|
272
|
+
# (optional) argument is a Hash scope the results to the query.
|
|
273
|
+
#
|
|
274
|
+
# @param [Integer] limit (optional)
|
|
275
|
+
# limit the returned Collection to a specific number of entries
|
|
276
|
+
# @param [Hash] query (optional)
|
|
277
|
+
# scope the returned Resource or Collection to the supplied query
|
|
278
|
+
#
|
|
279
|
+
# @return [Resource, Collection]
|
|
280
|
+
# The first resource in the entries of this collection,
|
|
281
|
+
# or a new collection whose query has been merged
|
|
282
|
+
#
|
|
283
|
+
# @api public
|
|
260
284
|
def first(*args)
|
|
261
|
-
|
|
262
|
-
|
|
285
|
+
last_arg = args.last
|
|
286
|
+
|
|
287
|
+
limit = args.first if args.first.kind_of?(Integer)
|
|
288
|
+
with_query = last_arg.respond_to?(:merge) && !last_arg.blank?
|
|
289
|
+
|
|
290
|
+
query = with_query ? last_arg : {}
|
|
263
291
|
|
|
264
|
-
if
|
|
265
|
-
query.
|
|
292
|
+
query = if query.kind_of?(Query)
|
|
293
|
+
query.slice(0, limit || 1)
|
|
266
294
|
else
|
|
267
|
-
query.
|
|
295
|
+
offset = query.fetch(:offset, 0)
|
|
296
|
+
query = query.except(:offset)
|
|
297
|
+
scoped_query(query).slice(offset, limit || 1)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
if limit
|
|
301
|
+
all(query)
|
|
302
|
+
else
|
|
303
|
+
query.repository.read(query).first
|
|
268
304
|
end
|
|
269
305
|
end
|
|
270
306
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
307
|
+
# Return the last Resource or the last N Resources for the Model with an optional query
|
|
308
|
+
#
|
|
309
|
+
# When there are no arguments, return the last Resource for the
|
|
310
|
+
# Model. When the first argument is an Integer, return a
|
|
311
|
+
# Collection containing the last N Resources. When the last
|
|
312
|
+
# (optional) argument is a Hash scope the results to the query.
|
|
313
|
+
#
|
|
314
|
+
# @param [Integer] limit (optional)
|
|
315
|
+
# limit the returned Collection to a specific number of entries
|
|
316
|
+
# @param [Hash] query (optional)
|
|
317
|
+
# scope the returned Resource or Collection to the supplied query
|
|
318
|
+
#
|
|
319
|
+
# @return [Resource, Collection]
|
|
320
|
+
# The last resource in the entries of this collection,
|
|
321
|
+
# or a new collection whose query has been merged
|
|
322
|
+
#
|
|
323
|
+
# @api public
|
|
324
|
+
def last(*args)
|
|
325
|
+
last_arg = args.last
|
|
326
|
+
|
|
327
|
+
limit = args.first if args.first.kind_of?(Integer)
|
|
328
|
+
with_query = last_arg.respond_to?(:merge) && !last_arg.blank?
|
|
329
|
+
|
|
330
|
+
query = with_query ? last_arg : {}
|
|
331
|
+
|
|
332
|
+
query = if query.kind_of?(Query)
|
|
333
|
+
query.slice(0, limit || 1).reverse!
|
|
334
|
+
else
|
|
335
|
+
offset = query.fetch(:offset, 0)
|
|
336
|
+
query = query.except(:offset)
|
|
337
|
+
scoped_query(query).slice(offset, limit || 1).reverse!
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
if limit
|
|
341
|
+
all(query)
|
|
342
|
+
else
|
|
343
|
+
query.repository.read(query).last
|
|
344
|
+
end
|
|
274
345
|
end
|
|
275
346
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
347
|
+
# Finds the first Resource by conditions, or initializes a new
|
|
348
|
+
# Resource with the attributes if none found
|
|
349
|
+
#
|
|
350
|
+
# @param [Hash] conditions
|
|
351
|
+
# The conditions to be used to search
|
|
352
|
+
# @param [Hash] attributes
|
|
353
|
+
# The attributes to be used to create the record of none is found.
|
|
354
|
+
# @return [Resource]
|
|
355
|
+
# The instance found by +query+, or created with +attributes+ if none found
|
|
356
|
+
#
|
|
357
|
+
# @api public
|
|
358
|
+
def first_or_new(conditions = {}, attributes = {})
|
|
359
|
+
first(conditions) || new(conditions.merge(attributes))
|
|
360
|
+
end
|
|
280
361
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
362
|
+
# Finds the first Resource by conditions, or creates a new
|
|
363
|
+
# Resource with the attributes if none found
|
|
364
|
+
#
|
|
365
|
+
# @param [Hash] conditions
|
|
366
|
+
# The conditions to be used to search
|
|
367
|
+
# @param [Hash] attributes
|
|
368
|
+
# The attributes to be used to create the record of none is found.
|
|
369
|
+
# @return [Resource]
|
|
370
|
+
# The instance found by +query+, or created with +attributes+ if none found
|
|
371
|
+
#
|
|
372
|
+
# @api public
|
|
373
|
+
def first_or_create(conditions = {}, attributes = {})
|
|
374
|
+
first(conditions) || create(conditions.merge(attributes))
|
|
375
|
+
end
|
|
286
376
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
377
|
+
# Initializes an instance of Resource with the given attributes
|
|
378
|
+
#
|
|
379
|
+
# @param [Hash(Symbol => Object)] attributes
|
|
380
|
+
# hash of attributes to set
|
|
381
|
+
#
|
|
382
|
+
# @return [Resource]
|
|
383
|
+
# the newly initialized Resource instance
|
|
384
|
+
#
|
|
385
|
+
# @api public
|
|
386
|
+
chainable do
|
|
387
|
+
def new(*args, &block)
|
|
388
|
+
assert_valid
|
|
389
|
+
super
|
|
290
390
|
end
|
|
291
391
|
end
|
|
292
392
|
|
|
293
|
-
|
|
294
|
-
# Create an instance of Resource with the given attributes
|
|
393
|
+
# Create a Resource
|
|
295
394
|
#
|
|
296
|
-
# @param
|
|
395
|
+
# @param [Hash(Symbol => Object)] attributes
|
|
396
|
+
# attributes to set
|
|
397
|
+
#
|
|
398
|
+
# @return [Resource]
|
|
399
|
+
# the newly created Resource instance
|
|
400
|
+
#
|
|
401
|
+
# @api public
|
|
297
402
|
def create(attributes = {})
|
|
298
|
-
|
|
299
|
-
resource.save
|
|
300
|
-
resource
|
|
403
|
+
_create(true, attributes)
|
|
301
404
|
end
|
|
302
405
|
|
|
303
|
-
|
|
304
|
-
#
|
|
406
|
+
# Create a Resource, bypassing hooks
|
|
407
|
+
#
|
|
408
|
+
# @param [Hash(Symbol => Object)] attributes
|
|
409
|
+
# attributes to set
|
|
305
410
|
#
|
|
411
|
+
# @return [Resource]
|
|
412
|
+
# the newly created Resource instance
|
|
413
|
+
#
|
|
414
|
+
# @api public
|
|
306
415
|
def create!(attributes = {})
|
|
307
|
-
|
|
308
|
-
resource = create(attributes)
|
|
309
|
-
raise PersistenceError, "Resource not saved: :new_record => #{resource.new_record?}, :dirty_attributes => #{resource.dirty_attributes.inspect}" if resource.new_record?
|
|
310
|
-
resource
|
|
416
|
+
_create(false, attributes)
|
|
311
417
|
end
|
|
312
418
|
|
|
313
|
-
##
|
|
314
419
|
# Copy a set of records from one repository to another.
|
|
315
420
|
#
|
|
316
421
|
# @param [String] source
|
|
@@ -321,7 +426,7 @@ module DataMapper
|
|
|
321
426
|
# The conditions with which to find the records to copy. These
|
|
322
427
|
# conditions are merged with Model.query
|
|
323
428
|
#
|
|
324
|
-
# @return [
|
|
429
|
+
# @return [Collection]
|
|
325
430
|
# A Collection of the Resource instances created in the operation
|
|
326
431
|
#
|
|
327
432
|
# @api public
|
|
@@ -329,198 +434,294 @@ module DataMapper
|
|
|
329
434
|
|
|
330
435
|
# get the list of properties that exist in the source and destination
|
|
331
436
|
destination_properties = properties(destination)
|
|
332
|
-
fields = query[:fields] ||= properties(source).select { |
|
|
437
|
+
fields = query[:fields] ||= properties(source).select { |property| destination_properties.include?(property) }
|
|
333
438
|
|
|
334
439
|
repository(destination) do
|
|
335
|
-
all(query.merge(:repository =>
|
|
336
|
-
create(fields.map { |
|
|
440
|
+
all(query.merge(:repository => source)).map do |resource|
|
|
441
|
+
create(fields.map { |property| [ property.name, property.get(resource) ] }.to_hash)
|
|
337
442
|
end
|
|
338
443
|
end
|
|
339
444
|
end
|
|
340
445
|
|
|
341
|
-
#
|
|
342
|
-
#
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
446
|
+
# Loads an instance of this Model, taking into account IdentityMap lookup,
|
|
447
|
+
# inheritance columns(s) and Property typecasting.
|
|
448
|
+
#
|
|
449
|
+
# @param [Enumerable(Object)] records
|
|
450
|
+
# an Array of Resource or Hashes to load a Resource with
|
|
451
|
+
#
|
|
452
|
+
# @return [Resource]
|
|
453
|
+
# the loaded Resource instance
|
|
454
|
+
#
|
|
455
|
+
# @api semipublic
|
|
456
|
+
def load(records, query)
|
|
457
|
+
repository = query.repository
|
|
458
|
+
repository_name = repository.name
|
|
459
|
+
fields = query.fields
|
|
460
|
+
discriminator = properties(repository_name).discriminator
|
|
461
|
+
no_reload = !query.reload?
|
|
346
462
|
|
|
347
|
-
|
|
348
|
-
model = values.at(inheritance_property_index) || model
|
|
349
|
-
end
|
|
463
|
+
field_map = fields.map { |property| [ property, property.field ] }.to_hash
|
|
350
464
|
|
|
351
|
-
|
|
352
|
-
|
|
465
|
+
records.map do |record|
|
|
466
|
+
identity_map = nil
|
|
467
|
+
key_values = nil
|
|
468
|
+
resource = nil
|
|
353
469
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
470
|
+
case record
|
|
471
|
+
when Hash
|
|
472
|
+
# remap fields to use the Property object
|
|
473
|
+
record = record.dup
|
|
474
|
+
field_map.each { |property, field| record[property] = record.delete(field) if record.key?(field) }
|
|
357
475
|
|
|
358
|
-
|
|
359
|
-
return resource unless query.reload?
|
|
360
|
-
else
|
|
361
|
-
resource = model.allocate
|
|
362
|
-
resource.instance_variable_set(:@repository, repository)
|
|
363
|
-
end
|
|
364
|
-
else
|
|
365
|
-
resource = model.allocate
|
|
366
|
-
resource.readonly!
|
|
367
|
-
end
|
|
476
|
+
model = discriminator && record[discriminator] || self
|
|
368
477
|
|
|
369
|
-
|
|
478
|
+
resource = if (key_values = record.values_at(*model.key(repository_name))).all?
|
|
479
|
+
identity_map = repository.identity_map(model)
|
|
480
|
+
identity_map[key_values]
|
|
481
|
+
end
|
|
370
482
|
|
|
371
|
-
|
|
372
|
-
value = property.custom? ? property.type.load(value, property) : property.typecast(value)
|
|
373
|
-
property.set!(resource, value)
|
|
483
|
+
resource ||= model.allocate
|
|
374
484
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
485
|
+
fields.each do |property|
|
|
486
|
+
next if no_reload && property.loaded?(resource)
|
|
487
|
+
|
|
488
|
+
value = record[property]
|
|
489
|
+
|
|
490
|
+
# TODO: typecasting should happen inside the Adapter
|
|
491
|
+
# and all values should come back as expected objects
|
|
492
|
+
if property.custom?
|
|
493
|
+
value = property.type.load(value, property)
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
property.set!(resource, value)
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
when Resource
|
|
500
|
+
model = record.model
|
|
501
|
+
|
|
502
|
+
resource = if (key_values = record.key).all?
|
|
503
|
+
identity_map = repository.identity_map(model)
|
|
504
|
+
identity_map[key_values]
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
resource ||= model.allocate
|
|
508
|
+
|
|
509
|
+
fields.each do |property|
|
|
510
|
+
next if no_reload && property.loaded?(resource)
|
|
511
|
+
|
|
512
|
+
property.set!(resource, property.get!(record))
|
|
513
|
+
end
|
|
382
514
|
end
|
|
383
|
-
end
|
|
384
515
|
|
|
385
|
-
|
|
386
|
-
|
|
516
|
+
resource.instance_variable_set(:@repository, repository)
|
|
517
|
+
resource.instance_variable_set(:@saved, true)
|
|
518
|
+
|
|
519
|
+
if identity_map
|
|
520
|
+
# defer setting the IdentityMap so second level caches can
|
|
521
|
+
# record the state of the resource after loaded
|
|
522
|
+
identity_map[key_values] = resource
|
|
523
|
+
else
|
|
524
|
+
resource.freeze
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
resource
|
|
387
528
|
end
|
|
529
|
+
end
|
|
388
530
|
|
|
389
|
-
|
|
531
|
+
# TODO: document
|
|
532
|
+
# @api semipublic
|
|
533
|
+
attr_reader :base_model
|
|
534
|
+
|
|
535
|
+
# TODO: document
|
|
536
|
+
# @api semipublic
|
|
537
|
+
def default_repository_name
|
|
538
|
+
Repository.default_name
|
|
390
539
|
end
|
|
391
540
|
|
|
392
|
-
# TODO:
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
Query.new(
|
|
541
|
+
# TODO: document
|
|
542
|
+
# @api semipublic
|
|
543
|
+
def default_order(repository_name = default_repository_name)
|
|
544
|
+
@default_order[repository_name] ||= key(repository_name).map { |property| Query::Direction.new(property) }.freeze
|
|
396
545
|
end
|
|
397
546
|
|
|
398
|
-
#
|
|
547
|
+
# Get the repository with a given name, or the default one for the current
|
|
548
|
+
# context, or the default one for this class.
|
|
549
|
+
#
|
|
550
|
+
# @param [Symbol] name
|
|
551
|
+
# the name of the repository wanted
|
|
552
|
+
# @param [Block] block
|
|
553
|
+
# block to execute with the fetched repository as parameter
|
|
554
|
+
#
|
|
555
|
+
# @return [Object, Respository]
|
|
556
|
+
# whatever the block returns, if given a block,
|
|
557
|
+
# otherwise the requested repository.
|
|
558
|
+
#
|
|
399
559
|
# @api private
|
|
400
|
-
def
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
560
|
+
def repository(name = nil)
|
|
561
|
+
#
|
|
562
|
+
# There has been a couple of different strategies here, but me (zond) and dkubb are at least
|
|
563
|
+
# united in the concept of explicitness over implicitness. That is - the explicit wish of the
|
|
564
|
+
# caller (+name+) should be given more priority than the implicit wish of the caller (Repository.context.last).
|
|
565
|
+
#
|
|
566
|
+
if block_given?
|
|
567
|
+
DataMapper.repository(name || repository_name) { |*block_args| yield(*block_args) }
|
|
568
|
+
else
|
|
569
|
+
DataMapper.repository(name || repository_name)
|
|
570
|
+
end
|
|
404
571
|
end
|
|
405
572
|
|
|
406
|
-
|
|
407
|
-
|
|
573
|
+
# Get the current +repository_name+ for this Model.
|
|
574
|
+
#
|
|
575
|
+
# If there are any Repository contexts, the name of the last one will
|
|
576
|
+
# be returned, else the +default_repository_name+ of this model will be
|
|
577
|
+
#
|
|
578
|
+
# @return [String]
|
|
579
|
+
# the current repository name to use for this Model
|
|
580
|
+
#
|
|
581
|
+
# @api private
|
|
582
|
+
def repository_name
|
|
583
|
+
Repository.context.any? ? Repository.context.last.name : default_repository_name
|
|
408
584
|
end
|
|
409
585
|
|
|
410
|
-
|
|
411
|
-
|
|
586
|
+
# Gets the current Set of repositories for which
|
|
587
|
+
# this Model has been defined (beyond default)
|
|
588
|
+
#
|
|
589
|
+
# @return [Set]
|
|
590
|
+
# The Set of repositories for which this Model
|
|
591
|
+
# has been defined (beyond default)
|
|
592
|
+
#
|
|
593
|
+
# @api private
|
|
594
|
+
def repositories
|
|
595
|
+
[ repository ].to_set + @properties.keys.map { |repository_name| DataMapper.repository(repository_name) }
|
|
412
596
|
end
|
|
413
597
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
598
|
+
# TODO: document
|
|
599
|
+
# @api private
|
|
600
|
+
def model_method_defined?(method)
|
|
601
|
+
model_methods.include?(method.to_s)
|
|
417
602
|
end
|
|
418
603
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
def
|
|
422
|
-
|
|
604
|
+
# TODO: document
|
|
605
|
+
# @api private
|
|
606
|
+
def resource_method_defined?(method)
|
|
607
|
+
resource_methods.include?(method.to_s)
|
|
423
608
|
end
|
|
424
609
|
|
|
425
|
-
|
|
426
|
-
assert_kind_of 'query', query, Query, Hash
|
|
610
|
+
private
|
|
427
611
|
|
|
428
|
-
|
|
612
|
+
# TODO: document
|
|
613
|
+
# @api private
|
|
614
|
+
def _create(safe, attributes)
|
|
615
|
+
resource = new(attributes)
|
|
616
|
+
resource.send(safe ? :save : :save!)
|
|
617
|
+
resource
|
|
618
|
+
end
|
|
429
619
|
|
|
430
|
-
|
|
431
|
-
|
|
620
|
+
# TODO: document
|
|
621
|
+
# @api private
|
|
622
|
+
def const_missing(name)
|
|
623
|
+
if name == :DM
|
|
624
|
+
warn "#{name} prefix deprecated and no longer necessary (#{caller[0]})"
|
|
625
|
+
self
|
|
626
|
+
elsif name == :Resource
|
|
627
|
+
Resource
|
|
628
|
+
elsif Types.const_defined?(name)
|
|
629
|
+
Types.const_get(name)
|
|
432
630
|
else
|
|
433
|
-
|
|
631
|
+
super
|
|
434
632
|
end
|
|
633
|
+
end
|
|
435
634
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
end
|
|
635
|
+
# TODO: document
|
|
636
|
+
# @api private
|
|
637
|
+
def default_storage_name
|
|
638
|
+
base_model.name
|
|
441
639
|
end
|
|
442
640
|
|
|
443
|
-
|
|
444
|
-
|
|
641
|
+
# Initializes a new Collection
|
|
642
|
+
#
|
|
643
|
+
# @return [Collection]
|
|
644
|
+
# A new Collection object
|
|
645
|
+
#
|
|
646
|
+
# @api private
|
|
647
|
+
def new_collection(query, resources = nil, &block)
|
|
648
|
+
Collection.new(query, resources, &block)
|
|
445
649
|
end
|
|
446
650
|
|
|
447
|
-
#
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
651
|
+
# @api private
|
|
652
|
+
# TODO: move the logic to create relative query into Query
|
|
653
|
+
def scoped_query(query)
|
|
654
|
+
if query.kind_of?(Query)
|
|
655
|
+
query.dup
|
|
656
|
+
else
|
|
657
|
+
repository = if query.key?(:repository)
|
|
658
|
+
query = query.dup
|
|
659
|
+
repository = query.delete(:repository)
|
|
660
|
+
|
|
661
|
+
if repository.kind_of?(Symbol)
|
|
662
|
+
DataMapper.repository(repository)
|
|
663
|
+
else
|
|
664
|
+
repository
|
|
665
|
+
end
|
|
666
|
+
else
|
|
667
|
+
self.repository
|
|
453
668
|
end
|
|
454
|
-
EOS
|
|
455
669
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
670
|
+
if self.query.repository == repository
|
|
671
|
+
self.query.merge(query)
|
|
672
|
+
else
|
|
673
|
+
Query.new(repository, self, self.query.merge(query).options)
|
|
674
|
+
end
|
|
461
675
|
end
|
|
462
676
|
end
|
|
463
677
|
|
|
464
|
-
#
|
|
465
|
-
def
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
#{property.writer_visibility}
|
|
469
|
-
def #{property.name}=(value)
|
|
470
|
-
attribute_set(#{property.name.inspect}, value)
|
|
471
|
-
end
|
|
472
|
-
EOS
|
|
473
|
-
end
|
|
474
|
-
end
|
|
678
|
+
# @api private
|
|
679
|
+
def assert_valid # :nodoc:
|
|
680
|
+
return if @valid
|
|
681
|
+
@valid = true
|
|
475
682
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
raise NotImplementedError.new
|
|
481
|
-
end
|
|
683
|
+
if properties(repository_name).empty? &&
|
|
684
|
+
!relationships(repository_name).any? { |(relationship_name, relationship)| relationship.kind_of?(Associations::ManyToOne::Relationship) }
|
|
685
|
+
raise IncompleteModelError, "#{name} must have at least one property or many to one relationship to be valid"
|
|
686
|
+
end
|
|
482
687
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
klass = self == relationship.child_model ? relationship.parent_model : relationship.child_model
|
|
486
|
-
return DataMapper::Query::Path.new(repository, [ relationship ], klass)
|
|
688
|
+
if key(repository_name).empty?
|
|
689
|
+
raise IncompleteModelError, "#{name} must have a key to be valid"
|
|
487
690
|
end
|
|
488
691
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
692
|
+
# initialize join models and target keys
|
|
693
|
+
@relationships.each_value do |relationships|
|
|
694
|
+
relationships.each_value do |relationship|
|
|
695
|
+
relationship.child_key
|
|
696
|
+
relationship.through if relationship.respond_to?(:through)
|
|
697
|
+
relationship.via if relationship.respond_to?(:via)
|
|
698
|
+
end
|
|
492
699
|
end
|
|
700
|
+
end
|
|
493
701
|
|
|
494
|
-
|
|
702
|
+
# TODO: document
|
|
703
|
+
# @api private
|
|
704
|
+
def model_methods
|
|
705
|
+
@model_methods ||= ancestor_instance_methods { |mod| mod.meta_class }
|
|
495
706
|
end
|
|
496
707
|
|
|
497
|
-
# TODO:
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
# @return <DataMapper::Adapters::Transaction
|
|
503
|
-
# a new DataMapper::Adapters::Transaction with all DataMapper::Repositories
|
|
504
|
-
# of the class of this DataMapper::Resource added.
|
|
505
|
-
#-
|
|
506
|
-
# @api public
|
|
507
|
-
#
|
|
508
|
-
# TODO: move to dm-more/dm-transactions
|
|
509
|
-
def transaction
|
|
510
|
-
DataMapper::Transaction.new(self) { |block_args| yield(*block_args) }
|
|
511
|
-
end
|
|
512
|
-
end # module Transaction
|
|
708
|
+
# TODO: document
|
|
709
|
+
# @api private
|
|
710
|
+
def resource_methods
|
|
711
|
+
@resource_methods ||= ancestor_instance_methods { |mod| mod }
|
|
712
|
+
end
|
|
513
713
|
|
|
514
|
-
|
|
714
|
+
# TODO: document
|
|
715
|
+
# @api private
|
|
716
|
+
def ancestor_instance_methods
|
|
717
|
+
methods = Set.new
|
|
515
718
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
def storage_exists?(repository_name = default_repository_name)
|
|
520
|
-
repository(repository_name).storage_exists?(storage_name(repository_name))
|
|
719
|
+
ancestors.each do |mod|
|
|
720
|
+
next unless mod <= DataMapper::Resource
|
|
721
|
+
methods.merge(yield(mod).instance_methods(false).map { |method| method.to_s })
|
|
521
722
|
end
|
|
522
|
-
end # module Migration
|
|
523
723
|
|
|
524
|
-
|
|
724
|
+
methods
|
|
725
|
+
end
|
|
525
726
|
end # module Model
|
|
526
727
|
end # module DataMapper
|