Extends the model with this module after Resource has been included.
This is a useful way to extend Model while still retaining a self.extended method.
@param [Module] extensions
List of modules that will extend the model after it is extended by Model
@return [Boolean]
whether or not the inclusions have been successfully appended to the list
@api semipublic
# File lib/dm-core/model.rb, line 187 187: def self.append_extensions(*extensions) 188: extra_extensions.concat extensions 189: 190: # Add the extension to existing descendants 191: descendants.each do |model| 192: extensions.each { |extension| model.extend(extension) } 193: end 194: 195: true 196: end
Appends a module for inclusion into the model class after Resource.
This is a useful way to extend Resource while still retaining a self.included method.
@param [Module] inclusions
the module that is to be appended to the module after Resource
@return [Boolean]
true if the inclusions have been successfully appended to the list
@api semipublic
# File lib/dm-core/model.rb, line 156 156: def self.append_inclusions(*inclusions) 157: extra_inclusions.concat inclusions 158: 159: # Add the inclusion to existing descendants 160: descendants.each do |model| 161: inclusions.each { |inclusion| model.send :include, inclusion } 162: end 163: 164: true 165: end
Return all models that extend the Model module
class Foo include DataMapper::Resource end DataMapper::Model.descendants.first #=> Foo
@return [DescendantSet]
Set containing the descendant models
@api semipublic
# File lib/dm-core/model.rb, line 46 46: def self.descendants 47: @descendants ||= DescendantSet.new 48: end
@api private
# File lib/dm-core/model.rb, line 208 208: def self.extended(descendant) 209: descendants << descendant 210: 211: descendant.instance_variable_set(:@valid, false) 212: descendant.instance_variable_set(:@base_model, descendant) 213: descendant.instance_variable_set(:@storage_names, {}) 214: descendant.instance_variable_set(:@default_order, {}) 215: 216: descendant.extend(Chainable) 217: 218: extra_extensions.each { |mod| descendant.extend(mod) } 219: extra_inclusions.each { |mod| descendant.send(:include, mod) } 220: end
The current registered extra extensions
@return [Set]
@api private
# File lib/dm-core/model.rb, line 203 203: def self.extra_extensions 204: @extra_extensions ||= [] 205: end
The current registered extra inclusions
@return [Set]
@api private
# File lib/dm-core/model.rb, line 172 172: def self.extra_inclusions 173: @extra_inclusions ||= [] 174: end
Creates a new Model class with its constant already set
If a block is passed, it will be eval’d in the context of the new Model
@param [#] name
the name of the new model
@param [Object] namespace
the namespace that will hold the new model
@param [Proc] block
a block that will be eval'd in the context of the new Model class
@return [Model]
the newly created Model class
@api private
# File lib/dm-core/model.rb, line 23 23: def self.new(name = nil, namespace = Object, &block) 24: model = name ? namespace.const_set(name, Class.new) : Class.new 25: 26: model.class_eval include DataMapper::Resource, __FILE__, __LINE__ + 1 27: 28: model.instance_eval(&block) if block 29: model 30: end
Return if Resource#save should raise an exception on save failures (globally)
This is false by default.
DataMapper::Model.raise_on_save_failure # => false
@return [Boolean]
true if a failure in Resource#save should raise an exception
@api public
# File lib/dm-core/model.rb, line 79 79: def self.raise_on_save_failure 80: if defined?(@raise_on_save_failure) 81: @raise_on_save_failure 82: else 83: false 84: end 85: end
Specify if Resource#save should raise an exception on save failures (globally)
@param [Boolean]
a boolean that if true will cause Resource#save to raise an exception
@return [Boolean]
true if a failure in Resource#save should raise an exception
@api public
# File lib/dm-core/model.rb, line 96 96: def self.raise_on_save_failure=(raise_on_save_failure) 97: @raise_on_save_failure = raise_on_save_failure 98: end
# File lib/dm-core/model.rb, line 294 294: def [](*args) 295: all[*args] 296: end
Find a set of records matching an optional set of conditions. Additionally, specify the order that the records are return.
Zoo.all # all zoos Zoo.all(:open => true) # all zoos that are open Zoo.all(:opened_on => start..end) # all zoos that opened on a date in the date-range Zoo.all(:order => [ :tiger_count.desc ]) # Ordered by tiger_count
@param [Hash] query
A hash describing the conditions and order for the query
@return [Collection]
A set of records found matching the conditions in +query+
@see Collection
@api public
# File lib/dm-core/model.rb, line 336 336: def all(query = Undefined) 337: if query.equal?(Undefined) || (query.kind_of?(Hash) && query.empty?) 338: # TODO: after adding Enumerable methods to Model, try to return self here 339: new_collection(self.query.dup) 340: else 341: new_collection(scoped_query(query)) 342: end 343: end
# File lib/dm-core/model.rb, line 300 300: def at(*args) 301: all.at(*args) 302: end
Copy a set of records from one repository to another.
@param [String] source_repository_name
The name of the Repository the resources should be copied _from_
@param [String] target_repository_name
The name of the Repository the resources should be copied _to_
@param [Hash] query
The conditions with which to find the records to copy. These conditions are merged with Model.query
@return [Collection]
A Collection of the Resource instances created in the operation
@api public
# File lib/dm-core/model.rb, line 537 537: def copy(source_repository_name, target_repository_name, query = {}) 538: target_properties = properties(target_repository_name) 539: 540: query[:fields] ||= properties(source_repository_name).select do |property| 541: target_properties.include?(property) 542: end 543: 544: repository(target_repository_name) do |repository| 545: resources = [] 546: 547: all(query.merge(:repository => source_repository_name)).each do |resource| 548: new_resource = new 549: query[:fields].each { |property| new_resource.__send__("#{property.name}=", property.get(resource)) } 550: resources << new_resource if new_resource.save 551: end 552: 553: all(Query.target_query(repository, self, resources)) 554: end 555: end
@api semipublic
# File lib/dm-core/model.rb, line 659 659: def default_order(repository_name = default_repository_name) 660: @default_order[repository_name] ||= key(repository_name).map { |property| Query::Direction.new(property) }.freeze 661: end
@api semipublic
# File lib/dm-core/model.rb, line 654 654: def default_repository_name 655: Repository.default_name 656: end
Return all models that inherit from a Model
class Foo include DataMapper::Resource end class Bar < Foo end Foo.descendants.first #=> Bar
@return [Set]
Set containing the descendant classes
@api semipublic
# File lib/dm-core/model.rb, line 65 65: def descendants 66: @descendants ||= DescendantSet.new 67: end
Remove all Resources from the repository
@return [Boolean]
true if the resources were successfully destroyed
@api public
# File lib/dm-core/model.rb, line 509 509: def destroy 510: all.destroy 511: end
Remove all Resources from the repository, bypassing validation
@return [Boolean]
true if the resources were successfully destroyed
@api public
# File lib/dm-core/model.rb, line 519 519: def destroy! 520: all.destroy! 521: end
# File lib/dm-core/model.rb, line 316 316: def each(&block) 317: all.each(&block) 318: self 319: end
# File lib/dm-core/model.rb, line 304 304: def fetch(*args, &block) 305: all.fetch(*args, &block) 306: end
Finish model setup and verify it is valid
@return [undefined]
@api public
# File lib/dm-core/model.rb, line 136 136: def finalize 137: finalize_relationships 138: finalize_allowed_writer_methods 139: assert_valid_name 140: assert_valid_properties 141: assert_valid_key 142: end
Return the first Resource or the first N Resources for the Model with an optional query
When there are no arguments, return the first Resource in the Model. When the first argument is an Integer, return a Collection containing the first N Resources. When the last (optional) argument is a Hash scope the results to the query.
@param [Integer] limit (optional)
limit the returned Collection to a specific number of entries
@param [Hash] query (optional)
scope the returned Resource or Collection to the supplied query
@return [Resource, Collection]
The first resource in the entries of this collection, or a new collection whose query has been merged
@api public
# File lib/dm-core/model.rb, line 362 362: def first(*args) 363: first_arg = args.first 364: last_arg = args.last 365: 366: limit_specified = first_arg.kind_of?(Integer) 367: with_query = (last_arg.kind_of?(Hash) && !last_arg.empty?) || last_arg.kind_of?(Query) 368: 369: limit = limit_specified ? first_arg : 1 370: query = with_query ? last_arg : {} 371: 372: query = self.query.slice(0, limit).update(query) 373: 374: if limit_specified 375: all(query) 376: else 377: query.repository.read(query).first 378: end 379: end
Finds the first Resource by conditions, or creates a new Resource with the attributes if none found
@param [Hash] conditions
The conditions to be used to search
@param [Hash] attributes
The attributes to be used to create the record of none is found.
@return [Resource]
The instance found by +query+, or created with +attributes+ if none found
@api public
# File lib/dm-core/model.rb, line 443 443: def first_or_create(conditions = {}, attributes = {}) 444: first(conditions) || create(conditions.merge(attributes)) 445: end
Finds the first Resource by conditions, or initializes a new Resource with the attributes if none found
@param [Hash] conditions
The conditions to be used to search
@param [Hash] attributes
The attributes to be used to create the record of none is found.
@return [Resource]
The instance found by +query+, or created with +attributes+ if none found
@api public
# File lib/dm-core/model.rb, line 428 428: def first_or_new(conditions = {}, attributes = {}) 429: first(conditions) || new(conditions.merge(attributes)) 430: end
Grab a single record by its key. Supports natural and composite key lookups as well.
Zoo.get(1) # get the zoo with primary key of 1. Zoo.get!(1) # Or get! if you want an ObjectNotFoundError on failure Zoo.get('DFW') # wow, support for natural primary keys Zoo.get('Metro', 'DFW') # more wow, composite key look-up
@param [Object] *key
The primary key or keys to use for lookup
@return [Resource, nil]
A single model that was found If no instance was found matching +key+
@api public
# File lib/dm-core/model.rb, line 270 270: def get(*key) 271: assert_valid_key_size(key) 272: 273: repository = self.repository 274: key = self.key(repository.name).typecast(key) 275: 276: repository.identity_map(self)[key] || first(key_conditions(repository, key).update(:order => nil)) 277: end
Grab a single record just like #, but raise an ObjectNotFoundError if the record doesn’t exist.
@param [Object] *key
The primary key or keys to use for lookup
@return [Resource]
A single model that was found
@raise [ObjectNotFoundError]
The record was not found
@api public
# File lib/dm-core/model.rb, line 290 290: def get!(*key) 291: get(*key) || raise(ObjectNotFoundError, "Could not find #{self.name} with key #{key.inspect}") 292: end
@api private
# File lib/dm-core/model.rb, line 223 223: def inherited(descendant) 224: descendants << descendant 225: 226: descendant.instance_variable_set(:@valid, false) 227: descendant.instance_variable_set(:@base_model, base_model) 228: descendant.instance_variable_set(:@storage_names, @storage_names.dup) 229: descendant.instance_variable_set(:@default_order, @default_order.dup) 230: end
Return the last Resource or the last N Resources for the Model with an optional query
When there are no arguments, return the last Resource for the Model. When the first argument is an Integer, return a Collection containing the last N Resources. When the last (optional) argument is a Hash scope the results to the query.
@param [Integer] limit (optional)
limit the returned Collection to a specific number of entries
@param [Hash] query (optional)
scope the returned Resource or Collection to the supplied query
@return [Resource, Collection]
The last resource in the entries of this collection, or a new collection whose query has been merged
@api public
# File lib/dm-core/model.rb, line 398 398: def last(*args) 399: first_arg = args.first 400: last_arg = args.last 401: 402: limit_specified = first_arg.kind_of?(Integer) 403: with_query = (last_arg.kind_of?(Hash) && !last_arg.empty?) || last_arg.kind_of?(Query) 404: 405: limit = limit_specified ? first_arg : 1 406: query = with_query ? last_arg : {} 407: 408: query = self.query.slice(0, limit).update(query).reverse! 409: 410: if limit_specified 411: all(query) 412: else 413: query.repository.read(query).last 414: end 415: end
Loads an instance of this Model, taking into account IdentityMap lookup, inheritance columns(s) and Property typecasting.
@param [Enumerable(Object)] records
an Array of Resource or Hashes to load a Resource with
@return [Resource]
the loaded Resource instance
@api semipublic
# File lib/dm-core/model.rb, line 567 567: def load(records, query) 568: repository = query.repository 569: repository_name = repository.name 570: fields = query.fields 571: discriminator = properties(repository_name).discriminator 572: no_reload = !query.reload? 573: 574: field_map = Hash[ fields.map { |property| [ property, property.field ] } ] 575: 576: records.map do |record| 577: identity_map = nil 578: key_values = nil 579: resource = nil 580: 581: case record 582: when Hash 583: # remap fields to use the Property object 584: record = record.dup 585: field_map.each { |property, field| record[property] = record.delete(field) if record.key?(field) } 586: 587: model = discriminator && discriminator.load(record[discriminator]) || self 588: model_key = model.key(repository_name) 589: 590: resource = if model_key.valid?(key_values = record.values_at(*model_key)) 591: identity_map = repository.identity_map(model) 592: identity_map[key_values] 593: end 594: 595: resource ||= model.allocate 596: 597: fields.each do |property| 598: next if no_reload && property.loaded?(resource) 599: 600: value = record[property] 601: 602: # TODO: typecasting should happen inside the Adapter 603: # and all values should come back as expected objects 604: value = property.load(value) 605: 606: property.set!(resource, value) 607: end 608: 609: when Resource 610: model = record.model 611: model_key = model.key(repository_name) 612: 613: resource = if model_key.valid?(key_values = record.key) 614: identity_map = repository.identity_map(model) 615: identity_map[key_values] 616: end 617: 618: resource ||= model.allocate 619: 620: fields.each do |property| 621: next if no_reload && property.loaded?(resource) 622: 623: property.set!(resource, property.get!(record)) 624: end 625: end 626: 627: resource.instance_variable_set(:@_repository, repository) 628: 629: if identity_map 630: resource.persistence_state = Resource::PersistenceState::Clean.new(resource) unless resource.persistence_state? 631: 632: # defer setting the IdentityMap so second level caches can 633: # record the state of the resource after loaded 634: identity_map[key_values] = resource 635: else 636: resource.persistence_state = Resource::PersistenceState::Immutable.new(resource) 637: end 638: 639: resource 640: end 641: end
Return if Resource#save should raise an exception on save failures (per-model)
This delegates to DataMapper::Model.raise_on_save_failure by default.
User.raise_on_save_failure # => false
@return [Boolean]
true if a failure in Resource#save should raise an exception
@api public
# File lib/dm-core/model.rb, line 110 110: def raise_on_save_failure 111: if defined?(@raise_on_save_failure) 112: @raise_on_save_failure 113: else 114: DataMapper::Model.raise_on_save_failure 115: end 116: end
Specify if Resource#save should raise an exception on save failures (per-model)
@param [Boolean]
a boolean that if true will cause Resource#save to raise an exception
@return [Boolean]
true if a failure in Resource#save should raise an exception
@api public
# File lib/dm-core/model.rb, line 127 127: def raise_on_save_failure=(raise_on_save_failure) 128: @raise_on_save_failure = raise_on_save_failure 129: end
Gets the current Set of repositories for which this Model has been defined (beyond default)
@return [Set]
The Set of repositories for which this Model has been defined (beyond default)
@api private
# File lib/dm-core/model.rb, line 708 708: def repositories 709: [ repository ].to_set + @properties.keys.map { |repository_name| DataMapper.repository(repository_name) } 710: end
Get the repository with a given name, or the default one for the current context, or the default one for this class.
@param [Symbol] name
the name of the repository wanted
@param [Block] block
block to execute with the fetched repository as parameter
@return [Object, Respository]
whatever the block returns, if given a block, otherwise the requested repository.
@api private
# File lib/dm-core/model.rb, line 676 676: def repository(name = nil, &block) 677: # 678: # There has been a couple of different strategies here, but me (zond) and dkubb are at least 679: # united in the concept of explicitness over implicitness. That is - the explicit wish of the 680: # caller (+name+) should be given more priority than the implicit wish of the caller (Repository.context.last). 681: # 682: 683: DataMapper.repository(name || repository_name, &block) 684: end
Get the current repository_name for this Model.
If there are any Repository contexts, the name of the last one will be returned, else the default_repository_name of this model will be
@return [String]
the current repository name to use for this Model
@api private
# File lib/dm-core/model.rb, line 695 695: def repository_name 696: context = Repository.context 697: context.any? ? context.last.name : default_repository_name 698: end
# File lib/dm-core/model.rb, line 312 312: def reverse 313: all.reverse 314: end
Gets the name of the storage receptacle for this resource in the given Repository (ie., table name, for database stores).
@return [String]
the storage name (ie., table name, for database stores) associated with this resource in the given repository
@api public
# File lib/dm-core/model.rb, line 240 240: def storage_name(repository_name = default_repository_name) 241: storage_names[repository_name] ||= repository(repository_name).adapter.resource_naming_convention.call(default_storage_name).freeze 242: end
the names of the storage receptacles for this resource across all repositories
@return [Hash(Symbol => String)]
All available names of storage receptacles
@api public
# File lib/dm-core/model.rb, line 250 250: def storage_names 251: @storage_names 252: end
Update every Resource
Person.update(:allow_beer => true)
@param [Hash] attributes
attributes to update with
@return [Boolean]
true if the resources were successfully updated
@api public
# File lib/dm-core/model.rb, line 484 484: def update(attributes) 485: all.update(attributes) 486: end
Update every Resource, bypassing validations
Person.update!(:allow_beer => true)
@param [Hash] attributes
attributes to update with
@return [Boolean]
true if the resources were successfully updated
@api public
# File lib/dm-core/model.rb, line 499 499: def update!(attributes) 500: all.update!(attributes) 501: end
@api private
# File lib/dm-core/model.rb, line 715 715: def _create(attributes, execute_hooks = true) 716: resource = new(attributes) 717: resource.__send__(execute_hooks ? :save : :save!) 718: resource 719: end
Test if the model has a valid key
@return [undefined]
@raise [IncompleteModelError]
raised if the model does not have a valid key
@api private
# File lib/dm-core/model.rb, line 862 862: def assert_valid_key 863: if key(repository_name).empty? 864: raise IncompleteModelError, "#{name} must have a key to be valid" 865: end 866: end
Raises an exception if # receives the wrong number of arguments
@param [Array] key
the key value
@return [undefined]
@raise [UpdateConflictError]
raise if the resource is dirty
@api private
# File lib/dm-core/model.rb, line 815 815: def assert_valid_key_size(key) 816: expected_key_size = self.key(repository_name).size 817: actual_key_size = key.size 818: 819: if actual_key_size != expected_key_size 820: raise ArgumentError, "The number of arguments for the key is invalid, expected #{expected_key_size} but was #{actual_key_size}" 821: end 822: end
Test if the model name is valid
@return [undefined]
@api private
# File lib/dm-core/model.rb, line 829 829: def assert_valid_name 830: if name.to_s.strip.empty? 831: raise IncompleteModelError, "#{inspect} must have a name" 832: end 833: end
Test if the model has properties
A model may also be valid if it has at least one m:1 relationships which will add inferred foreign key properties.
@return [undefined]
@raise [IncompleteModelError]
raised if the model has no properties
@api private
# File lib/dm-core/model.rb, line 846 846: def assert_valid_properties 847: repository_name = self.repository_name 848: if properties(repository_name).empty? && 849: !relationships(repository_name).any? { |relationship| relationship.kind_of?(Associations::ManyToOne::Relationship) } 850: raise IncompleteModelError, "#{name} must have at least one property or many to one relationship to be valid" 851: end 852: end
@api private
# File lib/dm-core/model.rb, line 722 722: def const_missing(name) 723: if name == :DM 724: raise "#{name} prefix deprecated and no longer necessary (#{caller.first})" 725: elsif name == :Resource 726: Resource 727: else 728: super 729: end 730: end
@api private
# File lib/dm-core/model.rb, line 733 733: def default_storage_name 734: base_model.name 735: end
Initialize the list of allowed writer methods
@return [undefined]
@api private
# File lib/dm-core/model.rb, line 790 790: def finalize_allowed_writer_methods 791: @allowed_writer_methods = public_instance_methods.map { |method| method.to_s }.grep(WRITER_METHOD_REGEXP).to_set 792: @allowed_writer_methods -= INVALID_WRITER_METHODS 793: @allowed_writer_methods.freeze 794: end
Initialize all foreign key properties established by relationships
@return [undefined]
@api private
# File lib/dm-core/model.rb, line 781 781: def finalize_relationships 782: relationships(repository_name).each { |relationship| relationship.finalize } 783: end
Initializes a new Collection
@return [Collection]
A new Collection object
@api private
# File lib/dm-core/model.rb, line 743 743: def new_collection(query, resources = nil, &block) 744: Collection.new(query, resources, &block) 745: end
@api private TODO: move the logic to create relative query into Query
# File lib/dm-core/model.rb, line 749 749: def scoped_query(query) 750: if query.kind_of?(Query) 751: query.dup 752: else 753: repository = if query.key?(:repository) 754: query = query.dup 755: repository = query.delete(:repository) 756: 757: if repository.kind_of?(Symbol) 758: DataMapper.repository(repository) 759: else 760: repository 761: end 762: else 763: self.repository 764: end 765: 766: query = self.query.merge(query) 767: 768: if self.query.repository == repository 769: query 770: else 771: repository.new_query(self, query.options) 772: end 773: end 774: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.