Module: DataMapper::Model
- Includes:
- Migration, Transaction
- Defined in:
- lib/dm-core/adapters/data_objects_adapter.rb,
lib/dm-core/model.rb
Overview
TODO: move to dm-ar-finders
Defined Under Namespace
Modules: Migration, Transaction
Class Method Summary collapse
-
.append_extensions(*extensions) ⇒ TrueClass, FalseClass
Extends the model with this module after DataMapper::Resource has been included.
- .extended(model) ⇒ Object
- .extra_extensions ⇒ Object
- .new(storage_name, &block) ⇒ Object
Instance Method Summary collapse
- #[](*key) ⇒ Object
-
#_load(marshalled) ⇒ Object
private
TODO: add docs.
- #all(query = {}) ⇒ Object
- #base_model ⇒ Object
-
#copy(source, destination, query = {}) ⇒ DataMapper::Collection
Copy a set of records from one repository to another.
-
#create(attributes = {}) ⇒ Object
Create an instance of Resource with the given attributes.
-
#create!(attributes = {}) ⇒ Object
This method is deprecated, and will be removed from dm-core.
- #default_order(repository_name = default_repository_name) ⇒ Object
- #default_repository_name ⇒ Object
- #eager_properties(repository_name = default_repository_name) ⇒ Object
-
#field_naming_convention(repository_name = default_storage_name) ⇒ String
The field naming conventions for this resource across all repositories.
-
#find_by_sql(*args) ⇒ Collection
Find instances by manually providing SQL.
- #first(*args) ⇒ Object
- #first_or_create(query, attributes = {}) ⇒ Object
- #get(*key) ⇒ Object
- #get!(*key) ⇒ Object
- #inheritance_property(repository_name = default_repository_name) ⇒ Object
- #inherited(target) ⇒ Object
- #key(repository_name = default_repository_name) ⇒ Object
-
#load(values, query) ⇒ Object
private
TODO: spec this.
- #paranoid_properties ⇒ Object
- #properties(repository_name = default_repository_name) ⇒ Object
- #properties_with_subclasses(repository_name = default_repository_name) ⇒ Object private
-
#property(name, type, options = {}) ⇒ Object
defines a property on the resource.
- #repositories ⇒ Object
-
#repository(name = nil) ⇒ Object, DataMapper::Respository
Get the repository with a given name, or the default one for the current context, or the default one for this class.
- #repository_name ⇒ Object
-
#storage_name(repository_name = default_repository_name) ⇒ String
the name of the storage recepticle for this resource.
-
#storage_names ⇒ Hash(Symbol => String)
the names of the storage recepticles for this resource across all repositories.
-
#to_query(repository, key, query = {}) ⇒ Object
TODO: spec this.
- #typecast_key(key) ⇒ Object
Methods included from Migration
Methods included from Transaction
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object (private)
483 484 485 486 487 488 489 490 491 492 493 494 495 |
# File 'lib/dm-core/model.rb', line 483 def method_missing(method, *args, &block) if relationship = self.relationships(repository_name)[method] klass = self == relationship.child_model ? relationship.parent_model : relationship.child_model return DataMapper::Query::Path.new(repository, [ relationship ], klass) end property_set = properties(repository_name) if property_set.has_property?(method) return property_set[method] end super end |
Class Method Details
.append_extensions(*extensions) ⇒ TrueClass, FalseClass
Extends the model with this module after DataMapper::Resource has been included.
This is a useful way to extend DataMapper::Model while still retaining a self.extended method.
- TODO: Move this do DataMapper::Model when DataMapper::Model is created
22 23 24 25 |
# File 'lib/dm-core/model.rb', line 22 def self.append_extensions(*extensions) extra_extensions.concat extensions true end |
.extended(model) ⇒ Object
31 32 33 34 35 36 |
# File 'lib/dm-core/model.rb', line 31 def self.extended(model) model.instance_variable_set(:@storage_names, {}) model.instance_variable_set(:@properties, {}) model.instance_variable_set(:@field_naming_conventions, {}) extra_extensions.each { |extension| model.extend(extension) } end |
.extra_extensions ⇒ Object
27 28 29 |
# File 'lib/dm-core/model.rb', line 27 def self.extra_extensions @extra_extensions ||= [] end |
.new(storage_name, &block) ⇒ Object
75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/dm-core/model.rb', line 75 def self.new(storage_name, &block) model = Class.new model.send(:include, Resource) model.class_eval <<-EOS, __FILE__, __LINE__ def self.default_storage_name #{Extlib::Inflection.classify(storage_name).inspect} end EOS model.instance_eval(&block) if block_given? model end |
Instance Method Details
#[](*key) ⇒ Object
271 272 273 274 |
# File 'lib/dm-core/model.rb', line 271 def [](*key) warn("#{name}[] is deprecated. Use #{name}.get! instead.") get!(*key) end |
#_load(marshalled) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
TODO: add docs
400 401 402 403 404 |
# File 'lib/dm-core/model.rb', line 400 def _load(marshalled) resource = allocate Marshal.load(marshalled).each { |kv| resource.instance_variable_set(*kv) } resource end |
#all(query = {}) ⇒ Object
255 256 257 258 |
# File 'lib/dm-core/model.rb', line 255 def all(query = {}) query = scoped_query(query) query.repository.read_many(query) end |
#base_model ⇒ Object
87 88 89 |
# File 'lib/dm-core/model.rb', line 87 def base_model @base_model ||= self end |
#copy(source, destination, query = {}) ⇒ DataMapper::Collection
Copy a set of records from one repository to another.
328 329 330 331 332 333 334 335 336 337 338 339 |
# File 'lib/dm-core/model.rb', line 328 def copy(source, destination, query = {}) # get the list of properties that exist in the source and destination destination_properties = properties(destination) fields = query[:fields] ||= properties(source).select { |p| destination_properties.has_property?(p.name) } repository(destination) do all(query.merge(:repository => repository(source))).map do |resource| create(fields.map { |p| [ p.name, p.get(resource) ] }.to_hash) end end end |
#create(attributes = {}) ⇒ Object
Create an instance of Resource with the given attributes
297 298 299 300 301 |
# File 'lib/dm-core/model.rb', line 297 def create(attributes = {}) resource = new(attributes) resource.save resource end |
#create!(attributes = {}) ⇒ Object
This method is deprecated, and will be removed from dm-core.
306 307 308 309 310 311 |
# File 'lib/dm-core/model.rb', line 306 def create!(attributes = {}) warn("Model#create! is deprecated. It is moving to dm-validations, and will be used to create a record without validations") resource = create(attributes) raise PersistenceError, "Resource not saved: :new_record => #{resource.new_record?}, :dirty_attributes => #{resource.dirty_attributes.inspect}" if resource.new_record? resource end |
#default_order(repository_name = default_repository_name) ⇒ Object
241 242 243 244 |
# File 'lib/dm-core/model.rb', line 241 def default_order(repository_name = default_repository_name) @default_order ||= {} @default_order[repository_name] ||= key(repository_name).map { |property| Query::Direction.new(property) } end |
#default_repository_name ⇒ Object
410 411 412 |
# File 'lib/dm-core/model.rb', line 410 def default_repository_name Repository.default_name end |
#eager_properties(repository_name = default_repository_name) ⇒ Object
216 217 218 |
# File 'lib/dm-core/model.rb', line 216 def eager_properties(repository_name = default_repository_name) properties(repository_name).defaults end |
#field_naming_convention(repository_name = default_storage_name) ⇒ String
The field naming conventions for this resource across all repositories.
139 140 141 |
# File 'lib/dm-core/model.rb', line 139 def field_naming_convention(repository_name = default_storage_name) @field_naming_conventions[repository_name] ||= repository(repository_name).adapter.field_naming_convention end |
#find_by_sql(*args) ⇒ Collection
A String, Array or Query is required.
Find instances by manually providing SQL
-
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 |
# File 'lib/dm-core/adapters/data_objects_adapter.rb', line 664 def find_by_sql(*args) sql = nil query = nil bind_values = [] properties = nil do_reload = false repository_name = default_repository_name args.each do |arg| if arg.is_a?(String) sql = arg elsif arg.is_a?(Array) sql = arg.first bind_values = arg[1..-1] elsif arg.is_a?(DataMapper::Query) query = arg elsif arg.is_a?(Hash) repository_name = arg.delete(:repository) if arg.include?(:repository) properties = Array(arg.delete(:properties)) if arg.include?(:properties) do_reload = arg.delete(:reload) if arg.include?(:reload) raise "unknown options to #find_by_sql: #{arg.inspect}" unless arg.empty? end end repository = repository(repository_name) raise "#find_by_sql only available for Repositories served by a DataObjectsAdapter" unless repository.adapter.is_a?(DataMapper::Adapters::DataObjectsAdapter) if query sql = repository.adapter.send(:read_statement, query) bind_values = query.bind_values end raise "#find_by_sql requires a query of some kind to work" unless sql properties ||= self.properties(repository.name) Collection.new(Query.new(repository, self)) do |collection| repository.adapter.send(:with_connection) do |connection| command = connection.create_command(sql) begin reader = command.execute_reader(*bind_values) while(reader.next!) collection.load(reader.values) end ensure reader.close if reader end end end end |
#first(*args) ⇒ Object
260 261 262 263 264 265 266 267 268 269 |
# File 'lib/dm-core/model.rb', line 260 def first(*args) query = args.last.respond_to?(:merge) ? args.pop : {} query = scoped_query(query.merge(:limit => args.first || 1)) if args.any? query.repository.read_many(query) else query.repository.read_one(query) end end |
#first_or_create(query, attributes = {}) ⇒ Object
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/dm-core/model.rb', line 276 def first_or_create(query, attributes = {}) first(query) || begin resource = allocate query = query.dup properties(repository_name).key.each do |property| if value = query.delete(property.name) resource.send("#{property.name}=", value) end end resource.attributes = query.merge(attributes) resource.save resource end end |
#get(*key) ⇒ Object
246 247 248 249 |
# File 'lib/dm-core/model.rb', line 246 def get(*key) key = typecast_key(key) repository.identity_map(self).get(key) || first(to_query(repository, key)) end |
#get!(*key) ⇒ Object
251 252 253 |
# File 'lib/dm-core/model.rb', line 251 def get!(*key) get(*key) || raise(ObjectNotFoundError, "Could not find #{self.name} with key #{key.inspect}") end |
#inheritance_property(repository_name = default_repository_name) ⇒ Object
237 238 239 |
# File 'lib/dm-core/model.rb', line 237 def inheritance_property(repository_name = default_repository_name) @properties[repository_name].detect { |property| property.type == DataMapper::Types::Discriminator } end |
#inherited(target) ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/dm-core/model.rb', line 38 def inherited(target) target.instance_variable_set(:@storage_names, @storage_names.dup) target.instance_variable_set(:@properties, {}) target.instance_variable_set(:@base_model, self.base_model) target.instance_variable_set(:@paranoid_properties, @paranoid_properties) target.instance_variable_set(:@field_naming_conventions, @field_naming_conventions.dup) if self.respond_to?(:validators) @validations.contexts.each do |context, validators| validators.each { |validator| target.validators.context(context) << validator } end end @properties.each do |repository_name,properties| repository(repository_name) do properties.each do |property| next if target.properties(repository_name).has_property?(property.name) target.property(property.name, property.type, property..dup) end end end if @relationships duped_relationships = {} @relationships.each do |repository_name,relationships| relationships.each do |name, relationship| dup = relationship.dup dup.instance_variable_set(:@child_model, target) if dup.instance_variable_get(:@child_model) == self dup.instance_variable_set(:@parent_model, target) if dup.instance_variable_get(:@parent_model) == self duped_relationships[repository_name] ||= {} duped_relationships[repository_name][name] = dup end end target.instance_variable_set(:@relationships, duped_relationships) end end |
#key(repository_name = default_repository_name) ⇒ Object
233 234 235 |
# File 'lib/dm-core/model.rb', line 233 def key(repository_name = default_repository_name) properties(repository_name).key end |
#load(values, query) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
TODO: spec this
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 |
# File 'lib/dm-core/model.rb', line 343 def load(values, query) repository = query.repository model = self if inheritance_property_index = query.inheritance_property_index model = values.at(inheritance_property_index) || model end key_values = nil identity_map = nil if key_property_indexes = query.key_property_indexes(repository) key_values = values.values_at(*key_property_indexes) identity_map = repository.identity_map(model) if resource = identity_map.get(key_values) return resource unless query.reload? else resource = model.allocate resource.instance_variable_set(:@repository, repository) end else resource = model.allocate resource.readonly! end resource.instance_variable_set(:@new_record, false) query.fields.zip(values) do |property,value| value = property.custom? ? property.type.load(value, property) : property.typecast(value) property.set!(resource, value) if track = property.track case track when :hash resource.original_values[property.name] = value.dup.hash unless resource.original_values.has_key?(property.name) rescue value.hash when :load resource.original_values[property.name] = value unless resource.original_values.has_key?(property.name) end end end if key_values && identity_map identity_map.set(key_values, resource) end resource end |
#paranoid_properties ⇒ Object
414 415 416 417 |
# File 'lib/dm-core/model.rb', line 414 def paranoid_properties @paranoid_properties ||= {} @paranoid_properties end |
#properties(repository_name = default_repository_name) ⇒ Object
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/dm-core/model.rb', line 198 def properties(repository_name = default_repository_name) # We need to check whether all relations are already set up. # If this isn't the case, we try to reload them here if !@_valid_relations && respond_to?(:many_to_one_relationships) @_valid_relations = true begin many_to_one_relationships.each do |r| r.child_key end rescue NameError # Apparently not all relations are loaded, # so we will try again later on @_valid_relations = false end end @properties[repository_name] ||= repository_name == Repository.default_name ? PropertySet.new : properties(Repository.default_name).dup end |
#properties_with_subclasses(repository_name = default_repository_name) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/dm-core/model.rb', line 221 def properties_with_subclasses(repository_name = default_repository_name) properties = PropertySet.new ([ self ].to_set + (respond_to?(:descendants) ? descendants : [])).each do |model| model.relationships(repository_name).each_value { |relationship| relationship.child_key } model.many_to_one_relationships.each do |relationship| relationship.child_key end model.properties(repository_name).each do |property| properties << property unless properties.has_property?(property.name) end end properties end |
#property(name, type, options = {}) ⇒ Object
defines a property on the resource
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/dm-core/model.rb', line 150 def property(name, type, = {}) property = Property.new(self, name, type, ) create_property_getter(property) create_property_setter(property) properties(repository_name)[property.name] = property @_valid_relations = false # Add property to the other mappings as well if this is for the default # repository. if repository_name == default_repository_name @properties.each_pair do |repository_name, properties| next if repository_name == default_repository_name properties << property unless properties.has_property?(property.name) end end # Add the property to the lazy_loads set for this resources repository # only. # TODO Is this right or should we add the lazy contexts to all # repositories? if property.lazy? context = .fetch(:lazy, :default) context = :default if context == true Array(context).each do |item| properties(repository_name).lazy_context(item) << name end end # add the property to the child classes only if the property was # added after the child classes' properties have been copied from # the parent if respond_to?(:descendants) descendants.each do |model| next if model.properties(repository_name).has_property?(name) model.property(name, type, ) end end property end |
#repositories ⇒ Object
194 195 196 |
# File 'lib/dm-core/model.rb', line 194 def repositories [ repository ].to_set + @properties.keys.collect { |repository_name| DataMapper.repository(repository_name) } end |
#repository(name = nil) ⇒ Object, DataMapper::Respository
Get the repository with a given name, or the default one for the current context, or the default one for this class.
-
106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/dm-core/model.rb', line 106 def repository(name = nil) # # There has been a couple of different strategies here, but me (zond) and dkubb are at least # united in the concept of explicitness over implicitness. That is - the explicit wish of the # caller (+name+) should be given more priority than the implicit wish of the caller (Repository.context.last). # if block_given? DataMapper.repository(name || repository_name) { |*block_args| yield(*block_args) } else DataMapper.repository(name || repository_name) end end |
#repository_name ⇒ Object
91 92 93 |
# File 'lib/dm-core/model.rb', line 91 def repository_name Repository.context.any? ? Repository.context.last.name : default_repository_name end |
#storage_name(repository_name = default_repository_name) ⇒ String
the name of the storage recepticle for this resource. IE. table name, for database stores
123 124 125 |
# File 'lib/dm-core/model.rb', line 123 def storage_name(repository_name = default_repository_name) @storage_names[repository_name] ||= repository(repository_name).adapter.resource_naming_convention.call(base_model.send(:default_storage_name)) end |
#storage_names ⇒ Hash(Symbol => String)
the names of the storage recepticles for this resource across all repositories
131 132 133 |
# File 'lib/dm-core/model.rb', line 131 def storage_names @storage_names end |
#to_query(repository, key, query = {}) ⇒ Object
TODO: spec this
393 394 395 396 |
# File 'lib/dm-core/model.rb', line 393 def to_query(repository, key, query = {}) conditions = Hash[ *self.key(repository.name).zip(key).flatten ] Query.new(repository, self, query.merge(conditions)) end |
#typecast_key(key) ⇒ Object
406 407 408 |
# File 'lib/dm-core/model.rb', line 406 def typecast_key(key) self.key(repository_name).zip(key).map { |k, v| k.typecast(v) } end |