Module: DataMapper::Model
- Includes:
- AggregateFunctions, Migration, Transaction
- Defined in:
- lib/gems/dm-core-0.9.9/lib/dm-core/adapters/data_objects_adapter.rb,
lib/gems/dm-core-0.9.9/lib/dm-core/model.rb,
lib/gems/dm-aggregates-0.9.9/lib/dm-aggregates/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
- #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 AggregateFunctions
#aggregate, #avg, #count, #max, #min, #sum
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)
472 473 474 475 476 477 478 479 480 481 482 483 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 472 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 if property = properties(repository_name)[method] return property 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/gems/dm-core-0.9.9/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/gems/dm-core-0.9.9/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/gems/dm-core-0.9.9/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/gems/dm-core-0.9.9/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
267 268 269 270 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 267 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
390 391 392 393 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 390 def _load(marshalled) repository_name, attributes = Marshal.load(marshalled) repository(repository_name) { new(attributes) } end |
#all(query = {}) ⇒ Object
251 252 253 254 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 251 def all(query = {}) query = scoped_query(query) query.repository.read_many(query) end |
#base_model ⇒ Object
87 88 89 |
# File 'lib/gems/dm-core-0.9.9/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.
324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 324 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
293 294 295 296 297 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 293 def create(attributes = {}) resource = new(attributes) resource.save resource end |
#create!(attributes = {}) ⇒ Object
This method is deprecated, and will be removed from dm-core.
302 303 304 305 306 307 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 302 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
237 238 239 240 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 237 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
399 400 401 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 399 def default_repository_name Repository.default_name end |
#eager_properties(repository_name = default_repository_name) ⇒ Object
216 217 218 |
# File 'lib/gems/dm-core-0.9.9/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/gems/dm-core-0.9.9/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
-
657 658 659 660 661 662 663 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 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/adapters/data_objects_adapter.rb', line 657 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
256 257 258 259 260 261 262 263 264 265 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 256 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
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 272 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
242 243 244 245 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 242 def get(*key) key = typecast_key(key) repository.identity_map(self).get(key) || first(to_query(repository, key)) end |
#get!(*key) ⇒ Object
247 248 249 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 247 def get!(*key) get(*key) || raise(ObjectNotFoundError, "Could not find #{self.name} with key #{key.inspect}") 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/gems/dm-core-0.9.9/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/gems/dm-core-0.9.9/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
339 340 341 342 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 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 339 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 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) identity_map.set(key_values, resource) 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 resource end |
#paranoid_properties ⇒ Object
403 404 405 406 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 403 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/gems/dm-core-0.9.9/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/gems/dm-core-0.9.9/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/gems/dm-core-0.9.9/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/gems/dm-core-0.9.9/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/gems/dm-core-0.9.9/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/gems/dm-core-0.9.9/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/gems/dm-core-0.9.9/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/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 131 def storage_names @storage_names end |
#to_query(repository, key, query = {}) ⇒ Object
TODO: spec this
383 384 385 386 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 383 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
395 396 397 |
# File 'lib/gems/dm-core-0.9.9/lib/dm-core/model.rb', line 395 def typecast_key(key) self.key(repository_name).zip(key).map { |k, v| k.typecast(v) } end |