Module: DataMapper::Resource

Extended by:
Deprecate
Includes:
Assertions
Defined in:
lib/dm-core/resource.rb,
lib/dm-core/backwards.rb,
lib/dm-core/resource/persistence_state.rb,
lib/dm-core/resource/persistence_state/clean.rb,
lib/dm-core/resource/persistence_state/dirty.rb,
lib/dm-core/resource/persistence_state/deleted.rb,
lib/dm-core/resource/persistence_state/immutable.rb,
lib/dm-core/resource/persistence_state/persisted.rb,
lib/dm-core/resource/persistence_state/transient.rb

Defined Under Namespace

Classes: PersistenceState

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Deprecate

deprecate

Methods included from Assertions

#assert_kind_of

Class Method Details

.append_inclusions(*inclusions) ⇒ Object

Deprecated.


6
7
8
# File 'lib/dm-core/resource.rb', line 6

def self.append_inclusions(*inclusions)
  raise "DataMapper::Resource.append_inclusions is deprecated, use DataMapper::Model.append_inclusions instead (#{caller.first})"
end

.descendantsObject

Deprecated.


16
17
18
# File 'lib/dm-core/resource.rb', line 16

def self.descendants
  raise "DataMapper::Resource.descendants is deprecated, use DataMapper::Model.descendants instead (#{caller.first})"
end

.extra_inclusionsObject

Deprecated.


11
12
13
# File 'lib/dm-core/resource.rb', line 11

def self.extra_inclusions
  raise "DataMapper::Resource.extra_inclusions is deprecated, use DataMapper::Model.extra_inclusions instead (#{caller.first})"
end

.included(model) ⇒ 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.

Makes sure a class gets all the methods when it includes Resource

Note that including this module into an anonymous class will leave the model descendant tracking mechanism with no possibility to reliably track the anonymous model across code reloads. This means that DescendantSet will currently leak memory in scenarios where anonymous models are reloaded multiple times (as is the case in dm-rails development mode for example).



70
71
72
# File 'lib/dm-core/resource.rb', line 70

def self.included(model)
  model.extend Model
end

Instance Method Details

#<=>(other) ⇒ Integer

Compares two Resources to allow them to be sorted

Parameters:

  • other (Resource)

    The other Resource to compare with

Returns:

  • (Integer)

    Return 0 if Resources should be sorted as the same, -1 if the other Resource should be after self, and 1 if the other Resource should be before self



505
506
507
508
509
510
511
512
513
514
515
# File 'lib/dm-core/resource.rb', line 505

def <=>(other)
  model = self.model
  unless other.kind_of?(model.base_model)
    raise ArgumentError, "Cannot compare a #{other.class} instance with a #{model} instance"
  end
  model.default_order(repository_name).each do |direction|
    cmp = direction.get(self) <=> direction.get(other)
    return cmp if cmp.nonzero?
  end
  0
end

#==(other) ⇒ Boolean

Compares another Resource for equivalency

Resource is equivalent to other if they are the same object (identical object_id) or all of their attribute are equivalent

Parameters:

  • other (Resource)

    the other Resource to compare with

Returns:

  • (Boolean)

    true if they are equivalent, false if not



488
489
490
491
492
# File 'lib/dm-core/resource.rb', line 488

def ==(other)
  return true if equal?(other)
  return false unless other.kind_of?(Resource) && model.base_model.equal?(other.model.base_model)
  cmp?(other, :==)
end

#after_create_hookundefined (protected)

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.

Method for hooking callbacks after resource creation

Returns:

  • (undefined)


703
704
705
# File 'lib/dm-core/resource.rb', line 703

def after_create_hook
  execute_hooks_for(:after, :create)
end

#after_destroy_hookundefined (protected)

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.

Method for hooking callbacks after resource destruction

Returns:

  • (undefined)


739
740
741
# File 'lib/dm-core/resource.rb', line 739

def after_destroy_hook
  execute_hooks_for(:after, :destroy)
end

#after_save_hookundefined (protected)

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.

Method for hooking callbacks after resource saving

Returns:

  • (undefined)


685
686
687
# File 'lib/dm-core/resource.rb', line 685

def after_save_hook
  execute_hooks_for(:after, :save)
end

#after_update_hookundefined (protected)

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.

Method for hooking callbacks after resource updating

Returns:

  • (undefined)


721
722
723
# File 'lib/dm-core/resource.rb', line 721

def after_update_hook
  execute_hooks_for(:after, :update)
end

#attribute_dirty?(name) ⇒ Boolean

Checks if an attribute has unsaved changes

Parameters:

  • name (Symbol)

    name of attribute to check for unsaved changes

Returns:

  • (Boolean)

    true if attribute has unsaved changes



600
601
602
# File 'lib/dm-core/resource.rb', line 600

def attribute_dirty?(name)
  dirty_attributes.key?(properties[name])
end

#attribute_get(name) ⇒ Object Also known as: []

Returns the value of the attribute.

Do not read from instance variables directly, but use this method. This method handles lazy loading the attribute and returning of defaults if nessesary.

Examples:

class Foo
  include DataMapper::Resource

  property :first_name, String
  property :last_name,  String

  def full_name
    "#{attribute_get(:first_name)} #{attribute_get(:last_name)}"
  end

  # using the shorter syntax
  def name_for_address_book
    "#{last_name}, #{first_name}"
  end
end

Parameters:

  • name (Symbol)

    name of attribute to retrieve

Returns:

  • (Object)

    the value stored at that given attribute (nil if none, and default if necessary)



239
240
241
242
# File 'lib/dm-core/resource.rb', line 239

def attribute_get(name)
  property = properties[name]
  persistence_state.get(property) if property
end

#attribute_loaded?(name) ⇒ Boolean

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.

Checks if an attribute has been loaded from the repository

Examples:

class Foo
  include DataMapper::Resource

  property :name,        String
  property :description, Text,   :lazy => false
end

Foo.new.attribute_loaded?(:description)   #=> false

Returns:

  • (Boolean)

    true if ivar name has been loaded

  • (Boolean)

    true if ivar name has been loaded



587
588
589
# File 'lib/dm-core/resource.rb', line 587

def attribute_loaded?(name)
  properties[name].loaded?(self)
end

#attribute_set(name, value) ⇒ undefined Also known as: []=

Sets the value of the attribute and marks the attribute as dirty if it has been changed so that it may be saved. Do not set from instance variables directly, but use this method. This method handles the lazy loading the property and returning of defaults if nessesary.

Examples:

class Foo
  include DataMapper::Resource

  property :first_name, String
  property :last_name,  String

  def full_name(name)
    name = name.split(' ')
    attribute_set(:first_name, name[0])
    attribute_set(:last_name, name[1])
  end

  # using the shorter syntax
  def name_from_address_book(name)
    name = name.split(', ')
    first_name = name[1]
    last_name = name[0]
  end
end

Parameters:

  • name (Symbol)

    name of attribute to set

  • value (Object)

    value to store

Returns:

  • (undefined)


281
282
283
284
285
286
287
# File 'lib/dm-core/resource.rb', line 281

def attribute_set(name, value)
  property = properties[name]
  if property
    value = property.typecast(value)
    self.persistence_state = persistence_state.set(property, value)
  end
end

#attributes(key_on = :name) ⇒ Hash

Gets all the attributes of the Resource instance

Parameters:

  • key_on (Symbol) (defaults to: :name)

    Use this attribute of the Property as keys. defaults to :name. :field is useful for adapters :property or nil use the actual Property object.

Returns:

  • (Hash)

    All the attributes



302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/dm-core/resource.rb', line 302

def attributes(key_on = :name)
  attributes = {}

  lazy_load(properties)
  fields.each do |property|
    if model.public_method_defined?(name = property.name)
      key = case key_on
        when :name  then name
        when :field then property.field
        else             property
      end

      attributes[key] = __send__(name)
    end
  end

  attributes
end

#attributes=(attributes) ⇒ Hash

Assign values to multiple attributes in one call (mass assignment)

Parameters:

  • attributes (Hash)

    names and values of attributes to assign

Returns:

  • (Hash)

    names and values of attributes assigned



330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# File 'lib/dm-core/resource.rb', line 330

def attributes=(attributes)
  model = self.model
  attributes.each do |name, value|
    case name
      when String, Symbol
        if model.allowed_writer_methods.include?(setter = "#{name}=")
          __send__(setter, value)
        else
          raise ArgumentError, "The attribute '#{name}' is not accessible in #{model}"
        end
      when Associations::Relationship, Property
        # only call a public #typecast (e.g. on Property instances)
        if name.respond_to?(:typecast)
          value = name.typecast(value)
        end
        self.persistence_state = persistence_state.set(name, value)
    end
  end
end

#before_create_hookundefined (protected)

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.

Method for hooking callbacks before resource creation

Returns:

  • (undefined)


694
695
696
# File 'lib/dm-core/resource.rb', line 694

def before_create_hook
  execute_hooks_for(:before, :create)
end

#before_destroy_hookundefined (protected)

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.

Method for hooking callbacks before resource destruction

Returns:

  • (undefined)


730
731
732
# File 'lib/dm-core/resource.rb', line 730

def before_destroy_hook
  execute_hooks_for(:before, :destroy)
end

#before_save_hookundefined (protected)

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.

Method for hooking callbacks before resource saving

Returns:

  • (undefined)


676
677
678
# File 'lib/dm-core/resource.rb', line 676

def before_save_hook
  execute_hooks_for(:before, :save)
end

#before_update_hookundefined (protected)

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.

Method for hooking callbacks before resource updating

Returns:

  • (undefined)


712
713
714
# File 'lib/dm-core/resource.rb', line 712

def before_update_hook
  execute_hooks_for(:before, :update)
end

#clean?Boolean

Checks if the resource has no changes to save

Returns:

  • (Boolean)

    true if the resource may not be persisted



181
182
183
184
# File 'lib/dm-core/resource.rb', line 181

def clean?
  persistence_state.kind_of?(PersistenceState::Clean) ||
    persistence_state.kind_of?(PersistenceState::Immutable)
end

#collectionnil, Collection

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.

Returns the Collection the Resource is associated with

Returns:

  • (nil)

    nil if this is a new record

  • (Collection)

    a Collection that self belongs to



629
630
631
632
# File 'lib/dm-core/resource.rb', line 629

def collection
  return @_collection if @_collection || new? || readonly?
  collection_for_self
end

#collection=(collection) ⇒ nil, Collection

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.

Associates a Resource to a Collection

Parameters:

  • collection (Collection, nil)

    the collection to associate the resource with

Returns:

  • (nil)

    nil if this is a new record

  • (Collection)

    a Collection that self belongs to



645
646
647
# File 'lib/dm-core/resource.rb', line 645

def collection=(collection)
  @_collection = collection
end

#collection_for_selfCollection

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.

Return a collection including the current resource only

Returns:



655
656
657
# File 'lib/dm-core/resource.rb', line 655

def collection_for_self
  Collection.new(query, [ self ])
end

#destroyBoolean

Destroy the instance, remove it from the repository

Returns:

  • (Boolean)

    true if resource was destroyed



436
437
438
439
440
441
442
443
444
# File 'lib/dm-core/resource.rb', line 436

def destroy
  return true if destroyed?
  catch :halt do
    before_destroy_hook
    _destroy
    after_destroy_hook
  end
  destroyed?
end

#destroy!Boolean

Destroy the instance, remove it from the repository, bypassing hooks

Returns:

  • (Boolean)

    true if resource was destroyed



452
453
454
455
456
# File 'lib/dm-core/resource.rb', line 452

def destroy!
  return true if destroyed?
  _destroy(false)
  destroyed?
end

#destroyed?Boolean

Checks if this Resource instance is destroyed

Returns:

  • (Boolean)

    true if the resource has been destroyed



171
172
173
# File 'lib/dm-core/resource.rb', line 171

def destroyed?
  readonly? && !key.nil?
end

#dirty?Boolean

Checks if the resource has unsaved changes

Returns:

  • (Boolean)

    true if resource may be persisted



192
193
194
195
196
# File 'lib/dm-core/resource.rb', line 192

def dirty?
  run_once(true) do
    dirty_self? || dirty_parents? || dirty_children?
  end
end

#dirty_attributesHash

Hash of attributes that have unsaved changes

Returns:

  • (Hash)

    attributes that have unsaved changes



610
611
612
613
614
615
616
617
618
619
# File 'lib/dm-core/resource.rb', line 610

def dirty_attributes
  dirty_attributes = {}

  original_attributes.each_key do |property|
    next unless property.respond_to?(:dump)
    dirty_attributes[property] = property.dump(property.get!(self))
  end

  dirty_attributes
end

#eql?(other) ⇒ Boolean

Compares another Resource for equality

Resource is equal to other if they are the same object (identical object_id) or if they are both of the *same model* and all of their attributes are equivalent

Parameters:

  • other (Resource)

    the other Resource to compare with

Returns:

  • (Boolean)

    true if they are equal, false if not



471
472
473
474
# File 'lib/dm-core/resource.rb', line 471

def eql?(other)
  return true if equal?(other)
  instance_of?(other.class) && cmp?(other, :eql?)
end

#hashObject

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.

Returns hash value of the object. Two objects with the same hash value assumed equal (using eql? method)

DataMapper resources are equal when their models have the same hash and they have the same set of properties

When used as key in a Hash or Hash subclass, objects are compared by eql? and thus hash value has direct effect on lookup



527
528
529
# File 'lib/dm-core/resource.rb', line 527

def hash
  model.hash ^ key.hash
end

#inspectString

Get a Human-readable representation of this Resource instance

Foo.new   #=> #<Foo name=nil updated_at=nil created_at=nil id=nil>

Returns:

  • (String)

    Human-readable representation of this Resource instance



539
540
541
542
543
544
545
546
547
548
549
550
551
552
# File 'lib/dm-core/resource.rb', line 539

def inspect
  # TODO: display relationship values
  attrs = properties.map do |property|
    value = if new? || property.loaded?(self)
      property.get!(self).inspect
    else
      '<not loaded>'
    end

    "#{property.instance_variable_name}=#{value}"
  end

  "#<#{model.name} #{attrs.join(' ')}>"
end

#keyArray(Key)

Retrieve the key(s) for this resource.

This always returns the persisted key value, even if the key is changed and not yet persisted. This is done so all relations still work.

Returns:

  • (Array(Key))

    the key(s) identifying this resource



132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/dm-core/resource.rb', line 132

def key
  return @_key if defined?(@_key)

  model_key = model.key(repository_name)

  key = model_key.map do |property|
    original_attributes[property] || (property.loaded?(self) ? property.get!(self) : nil)
  end

  # only memoize a valid key
  @_key = key if model_key.valid?(key)
end

#new?Boolean

Checks if this Resource instance is new

Returns:

  • (Boolean)

    true if the resource is new and not saved



151
152
153
# File 'lib/dm-core/resource.rb', line 151

def new?
  persistence_state.kind_of?(PersistenceState::Transient)
end

#original_attributesHash

Hash of original values of attributes that have unsaved changes

Returns:

  • (Hash)

    original values of attributes that have unsaved changes



560
561
562
563
564
565
566
# File 'lib/dm-core/resource.rb', line 560

def original_attributes
  if persistence_state.respond_to?(:original_attributes)
    persistence_state.original_attributes.dup.freeze
  else
    {}.freeze
  end
end

#persistence_stateResource::PersistenceState

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.

Get the persisted state for the resource

Returns:



83
84
85
# File 'lib/dm-core/resource.rb', line 83

def persistence_state
  @_persistence_state ||= Resource::PersistenceState::Transient.new(self)
end

#persistence_state=(state) ⇒ undefined

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.

Set the persisted state for the resource

Parameters:

Returns:

  • (undefined)


95
96
97
# File 'lib/dm-core/resource.rb', line 95

def persistence_state=(state)
  @_persistence_state = state
end

#persistence_state?Boolean

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.

Test if the persisted state is set

Returns:

  • (Boolean)

    true if the persisted state is set



105
106
107
# File 'lib/dm-core/resource.rb', line 105

def persistence_state?
  defined?(@_persistence_state) ? true : false
end

#queryQuery

Returns a Query that will match the resource

Returns:

  • (Query)

    Query that will match the resource



665
666
667
# File 'lib/dm-core/resource.rb', line 665

def query
  repository.new_query(model, :fields => fields, :conditions => conditions)
end

#raise_on_save_failureBoolean

Return if Resource#save should raise an exception on save failures (per-resource)

This delegates to model.raise_on_save_failure by default.

user.raise_on_save_failure  # => false

Returns:

  • (Boolean)

    true if a failure in Resource#save should raise an exception



30
31
32
33
34
35
36
# File 'lib/dm-core/resource.rb', line 30

def raise_on_save_failure
  if defined?(@raise_on_save_failure)
    @raise_on_save_failure
  else
    model.raise_on_save_failure
  end
end

#raise_on_save_failure=(raise_on_save_failure) ⇒ Boolean

Specify if Resource#save should raise an exception on save failures (per-resource)

Parameters:

  • a (Boolean)

    boolean that if true will cause Resource#save to raise an exception

Returns:

  • (Boolean)

    true if a failure in Resource#save should raise an exception



47
48
49
# File 'lib/dm-core/resource.rb', line 47

def raise_on_save_failure=(raise_on_save_failure)
  @raise_on_save_failure = raise_on_save_failure
end

#readonly?Boolean

Checks if this Resource instance is readonly

Returns:

  • (Boolean)

    true if the resource cannot be persisted



204
205
206
# File 'lib/dm-core/resource.rb', line 204

def readonly?
  persistence_state.kind_of?(PersistenceState::Immutable)
end

#reloadResource

Reloads association and all child association

This is accomplished by resetting the Resource key to it’s original value, and then removing all the ivars for properties and relationships. On the next access of those ivars, the resource will eager load what it needs. While this is more of a lazy reload, it should result in more consistent behavior since no cached results will remain from the initial load.

Returns:

  • (Resource)

    the receiver, the current Resource instance



363
364
365
366
367
368
369
370
371
372
# File 'lib/dm-core/resource.rb', line 363

def reload
  if key
    reset_key
    clear_subjects
  end

  self.persistence_state = persistence_state.rollback

  self
end

#repositoryRepository

Repository this resource belongs to in the context of this collection or of the resource’s class.

Returns:

  • (Repository)

    the respository this resource belongs to, in the context of a collection OR in the instance’s Model’s context



117
118
119
120
# File 'lib/dm-core/resource.rb', line 117

def repository
  # only set @_repository explicitly when persisted
  defined?(@_repository) ? @_repository : model.repository
end

#saveBoolean

Save the instance and loaded, dirty associations to the data-store

Returns:

  • (Boolean)

    true if Resource instance and all associations were saved



410
411
412
413
414
415
# File 'lib/dm-core/resource.rb', line 410

def save
  assert_not_destroyed(:save)
  retval = _save
  assert_save_successful(:save, retval)
  retval
end

#save!Boolean

Save the instance and loaded, dirty associations to the data-store, bypassing hooks

Returns:

  • (Boolean)

    true if Resource instance and all associations were saved



423
424
425
426
427
428
# File 'lib/dm-core/resource.rb', line 423

def save!
  assert_not_destroyed(:save!)
  retval = _save(false)
  assert_save_successful(:save!, retval)
  retval
end

#saved?Boolean

Checks if this Resource instance is saved

Returns:

  • (Boolean)

    true if the resource has been saved



161
162
163
# File 'lib/dm-core/resource.rb', line 161

def saved?
  persistence_state.kind_of?(PersistenceState::Persisted)
end

#update(attributes) ⇒ Boolean

Updates attributes and saves this Resource instance

Parameters:

  • attributes (Hash)

    attributes to be updated

Returns:

  • (Boolean)

    true if resource and storage state match



383
384
385
386
387
# File 'lib/dm-core/resource.rb', line 383

def update(attributes)
  assert_update_clean_only(:update)
  self.attributes = attributes
  save
end

#update!(attributes) ⇒ Boolean

Updates attributes and saves this Resource instance, bypassing hooks

Parameters:

  • attributes (Hash)

    attributes to be updated

Returns:

  • (Boolean)

    true if resource and storage state match



398
399
400
401
402
# File 'lib/dm-core/resource.rb', line 398

def update!(attributes)
  assert_update_clean_only(:update!)
  self.attributes = attributes
  save!
end

#update_attributes(attributes = {}, *allowed) ⇒ Object

Deprecated.

Deprecated API for updating attributes and saving Resource

See Also:



56
57
58
# File 'lib/dm-core/resource.rb', line 56

def update_attributes(attributes = {}, *allowed)
  raise "#{model}#update_attributes is deprecated, use #{model}#update instead (#{caller.first})"
end