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
- #all(query = {}) ⇒ Object
- #base_model ⇒ Object
-
#copy(source, destination, query = {}) ⇒ Object
TODO SPEC.
-
#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_conventions ⇒ Hash(Symbol => 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, &block) ⇒ 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)
429 430 431 432 433 434 435 436 437 438 439 440 |
# File 'lib/dm-core/model.rb', line 429 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/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, Hash.new { |h,k| h[k] = repository(k).adapter.resource_naming_convention.call(model.send(:default_storage_name)) }) model.instance_variable_set(:@properties, Hash.new { |h,k| h[k] = k == Repository.default_name ? PropertySet.new : h[Repository.default_name].dup }) model.instance_variable_set(:@field_naming_conventions, Hash.new { |h,k| h[k] = repository(k).adapter.field_naming_convention }) 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
74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/dm-core/model.rb', line 74 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
250 251 252 253 |
# File 'lib/dm-core/model.rb', line 250 def [](*key) warn("#{name}[] is deprecated. Use #{name}.get! instead.") get!(*key) end |
#all(query = {}) ⇒ Object
234 235 236 237 |
# File 'lib/dm-core/model.rb', line 234 def all(query = {}) query = scoped_query(query) query.repository.read_many(query) end |
#base_model ⇒ Object
86 87 88 |
# File 'lib/dm-core/model.rb', line 86 def base_model @base_model ||= self end |
#copy(source, destination, query = {}) ⇒ Object
TODO SPEC
293 294 295 296 297 298 299 |
# File 'lib/dm-core/model.rb', line 293 def copy(source, destination, query = {}) repository(destination) do repository(source).read_many(query).each do |resource| self.create(resource) end end end |
#create(attributes = {}) ⇒ Object
Create an instance of Resource with the given attributes
276 277 278 279 280 |
# File 'lib/dm-core/model.rb', line 276 def create(attributes = {}) resource = new(attributes) resource.save resource end |
#create!(attributes = {}) ⇒ Object
This method is deprecated, and will be removed from dm-core.
285 286 287 288 289 290 |
# File 'lib/dm-core/model.rb', line 285 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
221 222 223 |
# File 'lib/dm-core/model.rb', line 221 def default_order(repository_name = default_repository_name) key(repository_name).map { |property| Query::Direction.new(property) } end |
#default_repository_name ⇒ Object
356 357 358 |
# File 'lib/dm-core/model.rb', line 356 def default_repository_name Repository.default_name end |
#eager_properties(repository_name = default_repository_name) ⇒ Object
196 197 198 |
# File 'lib/dm-core/model.rb', line 196 def eager_properties(repository_name = default_repository_name) @properties[repository_name].defaults end |
#field_naming_conventions ⇒ Hash(Symbol => String)
The field naming conventions for this resource across all repositories.
134 135 136 |
# File 'lib/dm-core/model.rb', line 134 def field_naming_conventions @field_naming_conventions end |
#find_by_sql(*args) ⇒ Collection
A String, Array or Query is required.
Find instances by manually providing SQL
-
655 656 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 |
# File 'lib/dm-core/adapters/data_objects_adapter.rb', line 655 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
239 240 241 242 243 244 245 246 247 248 |
# File 'lib/dm-core/model.rb', line 239 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
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/dm-core/model.rb', line 255 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
225 226 227 228 |
# File 'lib/dm-core/model.rb', line 225 def get(*key) key = typecast_key(key) repository.identity_map(self).get(key) || first(to_query(repository, key)) end |
#get!(*key) ⇒ Object
230 231 232 |
# File 'lib/dm-core/model.rb', line 230 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
217 218 219 |
# File 'lib/dm-core/model.rb', line 217 def inheritance_property(repository_name = default_repository_name) @properties[repository_name].inheritance_property 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 |
# 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, Hash.new { |h,k| h[k] = k == Repository.default_name ? PropertySet.new : h[Repository.default_name].dup }) 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 = Hash.new { |h,k| h[k] = {} } @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][name] = dup end end target.instance_variable_set(:@relationships, duped_relationships) end end |
#key(repository_name = default_repository_name) ⇒ Object
213 214 215 |
# File 'lib/dm-core/model.rb', line 213 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
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
# File 'lib/dm-core/model.rb', line 303 def load(values, query) repository = query.repository model = self if inheritance_property_index = query.inheritance_property_index(repository) 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
360 361 362 363 |
# File 'lib/dm-core/model.rb', line 360 def paranoid_properties @paranoid_properties ||= {} @paranoid_properties end |
#properties(repository_name = default_repository_name) ⇒ Object
192 193 194 |
# File 'lib/dm-core/model.rb', line 192 def properties(repository_name = default_repository_name) @properties[repository_name] 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.
201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/dm-core/model.rb', line 201 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
145 146 147 148 149 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 |
# File 'lib/dm-core/model.rb', line 145 def property(name, type, = {}) property = Property.new(self, name, type, ) create_property_getter(property) create_property_setter(property) @properties[repository_name][property.name] = property # 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
188 189 190 |
# File 'lib/dm-core/model.rb', line 188 def repositories [ repository ].to_set + @properties.keys.collect { |repository_name| DataMapper.repository(repository_name) } end |
#repository(name = nil, &block) ⇒ 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.
-
105 106 107 108 109 110 111 112 |
# File 'lib/dm-core/model.rb', line 105 def repository(name = nil, &block) # # 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). # DataMapper.repository(name || repository_name, &block) end |
#repository_name ⇒ Object
90 91 92 |
# File 'lib/dm-core/model.rb', line 90 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
118 119 120 |
# File 'lib/dm-core/model.rb', line 118 def storage_name(repository_name = default_repository_name) @storage_names[repository_name] end |
#storage_names ⇒ Hash(Symbol => String)
the names of the storage recepticles for this resource across all repositories
126 127 128 |
# File 'lib/dm-core/model.rb', line 126 def storage_names @storage_names end |
#to_query(repository, key, query = {}) ⇒ Object
TODO: spec this
347 348 349 350 |
# File 'lib/dm-core/model.rb', line 347 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
352 353 354 |
# File 'lib/dm-core/model.rb', line 352 def typecast_key(key) self.key(repository_name).zip(key).map { |k, v| k.typecast(v) } end |