module AWS::Record::AbstractBase::InstanceMethods

Public Class Methods

new(attributes = {}) click to toggle source

Constructs a new record.

@param [Hash] attributes Attributes that should be bulk assigned

to this record.  You can also specify the shard (i.e. domain
or table) this record should persist to via +:shard+).

@option attributes [String] :shard The domain/table this record

should persist to.  If this is omitted, it will persist to the
class default shard (which defaults to the class name).

@return [Model,HashModel] Returns a new (non-persisted) record.

Call {#save} to persist changes to AWS.
# File lib/aws/record/abstract_base.rb, line 58
def initialize attributes = {}

  attributes = attributes.dup

  # supporting :domain for backwards compatability, :shard is prefered
  @_shard = attributes.delete(:domain)
  @_shard ||= attributes.delete('domain')
  @_shard ||= attributes.delete(:shard)
  @_shard ||= attributes.delete('shard')
  @_shard = self.class.shard_name(@_shard)

  @_data = {}
  assign_default_values
  bulk_assign(attributes)

end

Public Instance Methods

attributes() click to toggle source

@return [Hash] A hash with attribute names as hash keys (strings) and

attribute values (of mixed types) as hash values.
# File lib/aws/record/abstract_base.rb, line 93
def attributes
  attributes = Core::IndifferentHash.new
  attributes['id'] = id if persisted?
  self.class.attributes.keys.inject(attributes) do |hash,attr_name|
    hash.merge(attr_name => __send__(attr_name))
  end
end
attributes=(attributes) click to toggle source

Acts like {#update} but does not call {#save}.

record.attributes = { :name => 'abc', :age => 20 }

@param [Hash] attributes A hash of attributes to set on this record

without calling save.

@return [Hash] Returns the attribute hash that was passed in.

# File lib/aws/record/abstract_base.rb, line 110
def attributes= attributes
  bulk_assign(attributes)
end
delete() click to toggle source

Deletes the record. @return [true]

# File lib/aws/record/abstract_base.rb, line 194
def delete
  if persisted?
    if deleted?
      raise 'unable to delete, this object has already been deleted'
    else
      delete_storage
      @_deleted = true
    end
  else
    raise 'unable to delete, this object has not been saved yet'
  end
end
Also aliased as: destroy
deleted?() click to toggle source

@return [Boolean] Returns true if this instance object has been deleted.

# File lib/aws/record/abstract_base.rb, line 209
def deleted?
  persisted? ? !!@_deleted : false
end
destroy()
Alias for: delete
domain()
Alias for: shard
errors() click to toggle source
# File lib/aws/record/abstract_base.rb, line 142
def errors
  @errors ||= Errors.new
end
id() click to toggle source

The id for each record is auto-generated. The default strategy generates uuid strings. @return [String] Returns the id string (uuid) for this record. Retuns

nil if this is a new record that has not been persisted yet.
# File lib/aws/record/abstract_base.rb, line 87
def id
  @_id
end
new_record?() click to toggle source

@return [Boolean] Returns true if this record has not been persisted

to SimpleDB.
# File lib/aws/record/abstract_base.rb, line 129
def new_record?
  !persisted?
end
persisted?() click to toggle source

Persistence indicates if the record has been saved previously or not.

@example

@recipe = Recipe.new(:name => 'Buttermilk Pancackes')
@recipe.persisted? #=> false
@recipe.save!
@recipe.persisted? #=> true

@return [Boolean] Returns true if this record has been persisted.

# File lib/aws/record/abstract_base.rb, line 123
def persisted?
  !!@_persisted
end
save(opts = {}) click to toggle source

Creates new records, updates existing records. @param [Hash] opts Pass :validate => false to skip validations @return [Boolean] Returns true if the record saved without errors,

false otherwise.
# File lib/aws/record/abstract_base.rb, line 150
def save opts = {}
  if valid?(opts)
    persisted? ? update : create
    clear_changes!
    true
  else
    false
  end
end
save!() click to toggle source

Creates new records, updates exsting records. If there is a validation error then an exception is raised. @raise [InvalidRecordError] Raised when the record has validation

errors and can not be saved.

@return [true] Returns true after a successful save.

# File lib/aws/record/abstract_base.rb, line 165
def save!
  raise InvalidRecordError.new(self) unless save
  true
end
shard() click to toggle source

@return [String] Returns the name of the shard this record

is persisted to or will be persisted to.  Defaults to the
domain/table named after this record class.
# File lib/aws/record/abstract_base.rb, line 78
def shard
  @_shard
end
Also aliased as: domain
update_attributes(attribute_hash) click to toggle source

Bulk assigns the attributes and then saves the record. @param [Hash] attribute_hash A hash of attribute names (keys) and

attribute values to assign to this record.

@return (see save)

# File lib/aws/record/abstract_base.rb, line 174
def update_attributes attribute_hash
  bulk_assign(attribute_hash)
  save
end
update_attributes!(attribute_hash) click to toggle source

Bulk assigns the attributes and then saves the record. Raises an exception (AWS::Record::InvalidRecordError) if the record is not valid. @param (see update_attributes) @return [true]

# File lib/aws/record/abstract_base.rb, line 184
def update_attributes! attribute_hash
  if update_attributes(attribute_hash)
    true
  else
    raise InvalidRecordError.new(self)
  end
end
valid?(opts = {}) click to toggle source

@param [Hash] opts Pass :validate => false to skip validations @return [Boolean] Returns true if this record has no validation errors.

# File lib/aws/record/abstract_base.rb, line 135
def valid? opts = {}
  opts = {} if opts.nil?
  opts = {:validate => true}.merge(opts)
  run_validations if opts[:validate]
  errors.empty?
end

Protected Instance Methods

[](attribute_name) click to toggle source

Returns the typecasted value for the named attribute.

book = Book.new(:title => 'My Book')
book['title'] #=> 'My Book'
book.title    #=> 'My Book'

Intended Use

This method’s primary use is for getting/setting the value for an attribute inside a custom method:

class Book < AWS::Record::Model

  string_attr :title

  def title
    self['title'] ? self['title'].upcase : nil
  end

end

book = Book.new(:title => 'My Book')
book.title    #=> 'MY BOOK'

@param [String,Symbol] attribute_name The name of the attribute to fetch

a value for.

@return The current type-casted value for the named attribute.

# File lib/aws/record/abstract_base.rb, line 276
def [] attribute_name
  self.class.attribute_for(attribute_name) do |attribute|
    type_cast(attribute, @_data[attribute.name])
  end
end
[]=(attribute_name, new_value) click to toggle source

If you define a custom setter, you use []= to set the value on the record.

class Book < AWS::Record::Model

  string_attr :name

  # replace the default #author= method
  def author= name
    self['author'] = name.blank? ? 'Anonymous' : name
  end

end

@param [String,Symbol] The attribute name to set a value for @param attribute_value The value to assign.

# File lib/aws/record/abstract_base.rb, line 230
def []= attribute_name, new_value
  self.class.attribute_for(attribute_name) do |attribute|

    if_tracking_changes do
      original_value = type_cast(attribute, attribute_was(attribute.name))
      incoming_value = type_cast(attribute, new_value)
      if original_value == incoming_value
        clear_change!(attribute.name)
      else
        attribute_will_change!(attribute.name)
      end
    end

    @_data[attribute.name] = new_value

  end
end
create() click to toggle source
# File lib/aws/record/abstract_base.rb, line 283
def create
  populate_id
  touch_timestamps('created_at', 'updated_at')
  increment_optimistic_lock_value
  create_storage
  @_persisted = true
end
create_storage() click to toggle source
# File lib/aws/record/abstract_base.rb, line 483
def create_storage
  raise NotImplementedError
end
delete_storage() click to toggle source
# File lib/aws/record/abstract_base.rb, line 493
def delete_storage
  raise NotImplementedError
end
hydrate(id, data) click to toggle source

@private

# File lib/aws/record/abstract_base.rb, line 461
def hydrate id, data

  # @todo need to do something about partial hyrdation of attributes

  @_id = id

  # New objects are populated with default values, but we don't
  # want these values to hang around when hydrating persisted values
  # (those values may have been blanked out before save).
  self.class.attributes.values.each do |attribute|
    @_data[attribute.name] = nil
  end

  ignore_changes do
    bulk_assign(deserialize_item_data(data))
  end

  @_persisted = true

end
if_locks_optimistically() { |opt_lock_attr| ... } click to toggle source
# File lib/aws/record/abstract_base.rb, line 330
def if_locks_optimistically &block
  if opt_lock_attr = self.class.optimistic_locking_attr
    yield(opt_lock_attr)
  end
end
increment_optimistic_lock_value() click to toggle source
# File lib/aws/record/abstract_base.rb, line 319
def increment_optimistic_lock_value
  if_locks_optimistically do |lock_attr|
    if value = self[lock_attr.name]
      self[lock_attr.name] = value + 1
    else
      self[lock_attr.name] = 1
    end
  end
end
opt_lock_conditions() click to toggle source
# File lib/aws/record/abstract_base.rb, line 337
def opt_lock_conditions
  conditions = {}
  if_locks_optimistically do |lock_attr|
    if was = attribute_was(lock_attr.name)
      conditions[:if] = { lock_attr.name => lock_attr.serialize(was) }
    else
      conditions[:unless_exists] = lock_attr.name
    end
  end
  conditions
end
populate_id() click to toggle source
# File lib/aws/record/abstract_base.rb, line 300
def populate_id
  @_id = UUIDTools::UUID.random_create.to_s.downcase
end
touch_timestamps(*attributes) click to toggle source
# File lib/aws/record/abstract_base.rb, line 305
def touch_timestamps *attributes
  now = Time.now
  attributes.each do |attr_name|
    if
      self.class.attributes[attr_name] and
      !attribute_changed?(attr_name)
      # don't touch timestamps the user modified
    then
      __send__("#{attr_name}=", now)
    end
  end
end
update_storage() click to toggle source
# File lib/aws/record/abstract_base.rb, line 488
def update_storage
  raise NotImplementedError
end

Private Instance Methods

assign_default_values() click to toggle source
# File lib/aws/record/abstract_base.rb, line 350
def assign_default_values
  # populate default attribute values
  ignore_changes do
    self.class.attributes.values.each do |attribute|
      begin
        # copy default values down so methods like #gsub! don't
        # modify the default values for other objects
        @_data[attribute.name] = attribute.default_value.clone
      rescue TypeError
        @_data[attribute.name] = attribute.default_value
      end
    end
  end
end
bulk_assign(hash) click to toggle source
# File lib/aws/record/abstract_base.rb, line 366
def bulk_assign hash
  flatten_date_parts(hash).each_pair do |attr_name, attr_value|
    __send__("#{attr_name}=", attr_value)
  end
end
flatten_date_parts(attributes) click to toggle source

Rails date and time select helpers split date and time attributes into multiple values for form submission. These attributes get named things like ‘created_at(1i)’ and represent year/month/day/hour/min/sec parts of the date/time.

This method converts these attributes back into a single value and converts them to Date and DateTime objects.

# File lib/aws/record/abstract_base.rb, line 381
def flatten_date_parts attributes

  multi_attributes = Set.new

  hash = attributes.inject({}) do |hash,(key,value)|
    # collects attribuets like "created_at(1i)" into an array of parts
    if key =~ /\(/
      key, index = key.to_s.split(/\(|i\)/)
      hash[key] ||= []
      hash[key][index.to_i - 1] = value.to_i
      multi_attributes << key
    else
      hash[key] = value
    end
    hash
  end

  # convert multiattribute values into date/time objects
  multi_attributes.each do |key|

    values = hash[key]

    hash[key] = case values.size
    when 0 then nil
    when 2
      now = Time.now
      Time.local(now.year, now.month, now.day, values[0], values[1], 0, 0)
    when 3 then Date.new(*values)
    else DateTime.new(*values)
    end

  end

  hash

end
serialize_attribute(attribute, raw_value) click to toggle source
# File lib/aws/record/abstract_base.rb, line 450
def serialize_attribute attribute, raw_value
  type_casted_value = type_cast(attribute, raw_value)
  case type_casted_value
  when nil then nil
  when Set then type_casted_value.map{|v| attribute.serialize(v) }
  else attribute.serialize(type_casted_value)
  end
end
serialize_attributes() click to toggle source
# File lib/aws/record/abstract_base.rb, line 432
def serialize_attributes

  hash = {}
  self.class.attributes.each_pair do |attribute_name,attribute|
    value = serialize_attribute(attribute, @_data[attribute_name])
    unless [nil, []].include?(value)
      hash[attribute_name] = value
    end
  end

  # simple db does not support persisting items without attribute values
  raise EmptyRecordError.new(self) if hash.empty?

  hash

end
type_cast(attribute, raw) click to toggle source
# File lib/aws/record/abstract_base.rb, line 419
def type_cast attribute, raw
  if attribute.set?
    values = Record.as_array(raw).inject([]) do |values,value|
      values << attribute.type_cast(value)
      values
    end
    Set.new(values.compact)
  else
    attribute.type_cast(raw)
  end
end
update() click to toggle source
# File lib/aws/record/abstract_base.rb, line 292
def update
  return unless changed?
  touch_timestamps('updated_at')
  increment_optimistic_lock_value
  update_storage
end