class Shamu::Entities::Entity

An entity is an abstract set of data returned from a {Services::Service} describing the current state of an object.

Entities are immutable. They will not change for the duration of the request. Instead use a {Services::Service} to mutate the underlying data and request an updated copy of the Entity.

See {Shamu::Entities} for more on using entities.

## Helper Methods

Entities can define helper methods to perform simple calculations or projections of it's data. They only rely on state available by other attribute projections. This makes the entity cacheable and serializable.

### Why class instead of module?

The Entity class is ridiculously simple. It just mixes in a few modules and adds a few helper methods. It could just as easily been implemented as a module to mixin with POROs.

While modules are generally preferred for non-domain specific behaviors, in this case the purpose is to intentionally make it harder to mix the responsibilities of an Entity class with another object in your project. This tends to lead to better separation in your design.

@example

class LiveAccount < Shamu::Entities::Entity

  # Use an ActiveRecord User model for the actual data. Not accessible
  # to callers.
  model :user

  # Simple projections
  attribute :name, on: :user
  attribute :email, on: :user

  # Computed projections. Only calculated once, then cached in the
  # entity instance.
  attribute :signed_up_on do
    I18n.localize( user.created_at )
  end

  attribute :last_login_at do
    user.login_reccords.last
  end

  # Project another model
  model :contact

  # Project a JSON object using another entity class
  attribute :address, AddressEntity, on: :contact

  # Helper method
  def new_user?
    signed_up_on > 3.days.ago
  end
end

Private Class Methods

attr_accessor( * ) click to toggle source

Redefined to prevent creating mutable attributes.

# File lib/shamu/entities/entity.rb, line 223
def attr_accessor( * )
  fail "Remember, an Entity is immutable. Use a Services::Service to mutate the underlying data."
end
attr_writer( * ) click to toggle source

Redefined to prevent creating mutable attributes.

# File lib/shamu/entities/entity.rb, line 228
def attr_writer( * )
  fail "Remember, an Entity is immutable. Use a Services::Service to mutate the underlying data."
end
model( name, **args, &block ) click to toggle source

@!visibility public Define a model attribute that the entity will project. Use additional {.attribute} calls to define the actual projections.

Model attributes are private and should never be exposed to any client from another domain. Instead project only the properties needed for the Entity's clients.

@param (see Shamu::Attributes::DSL#attribute) @return [self]

@example

class Account < Shamu::Entities::Entity
  model :user

  attribute :username, on: :user
  attribute :email, on: :user
end
# File lib/shamu/entities/entity.rb, line 215
def model( name, **args, &block )
  attribute( name, **args, &block )
  attributes[name][:model] = true
  attributes[name][:ignore_equality] = true
  private name
end
model_name() click to toggle source

@return [ActiveModel::Name] used by url_helpers etc when generating

model specific names for this entity.
# File lib/shamu/entities/entity.rb, line 161
def model_name
  @model_name ||= begin
    base_name = name.sub /(::)?Entity$/, ""
    parts     = base_name.split "::"
    parts[-1] = parts[-1].singularize
    base_name = parts.join "::"

    ::ActiveModel::Name.new( self, nil, base_name )
  end
end
null_entity( &block ) click to toggle source

Define custom default attributes for a {NullEntity} for this class. @return [Class] the {NullEntity} class for the entity.

@example

class Users::UserEntity < Shamu::Entities::Entity
  attribute :id
  attribute :name
  attribute :level

  null_entity do
    attribute :level do
      "Guest"
    end
  end
end
# File lib/shamu/entities/entity.rb, line 188
def null_entity( &block )
  null_class = ::Shamu::Entities::NullEntity.for( self )
  null_class.class_eval( &block ) if block_given?
  null_class
end

Public Instance Methods

blank?()
Alias for: empty?
empty?() click to toggle source

@return [false] real entities are not empty. See {NullEntity}.

# File lib/shamu/entities/entity.rb, line 96
def empty?
  false
end
Also aliased as: blank?
id() click to toggle source

@!attribute @return [Object] id of the entity.

Shamu makes the assumption that all entities will have a single unique identifier that can be used to distinguish the entity from other instances of the same class.

While not strictly necessary in an purely abstract service, this invariant significantly reduces the complexity of caching, lookups and other helpful conventions and is almost always true in most modern architectures.

If an Entity does not have a natural primary key id, an id may be generated by joining the values of the composite key that is used to identify the resource.

# File lib/shamu/entities/entity.rb, line 91
def id
  fail "No id attribute defined. Add `attribute :id, on: :record` to #{ self.class.name }"
end
persisted?() click to toggle source

Entities are always immutable - so they are considered persisted. Use a {Services::ChangeRequest} to back a form instead.

# File lib/shamu/entities/entity.rb, line 108
def persisted?
  true
end
present?() click to toggle source

@return [true] the entity is present. See {NullEntity}.

# File lib/shamu/entities/entity.rb, line 102
def present?
  !empty?
end
redact( attributes ) click to toggle source

@return [Entity] a modified version of the entity with the given attributes redacted.

# File lib/shamu/entities/entity.rb, line 136
def redact( attributes )
  dup.redact!( attributes )
end
redact!( *attributes ) click to toggle source

Redact attributes on the entity.

@param [Array<Symbol>,Hash] attributes to redact on the entity. Either a list of attributes to set to nil or a hash of attributes with their values.

# File lib/shamu/entities/entity.rb, line 122
def redact!( *attributes )
  hash =
    if attributes.first.is_a?( Symbol )
      Hash[ attributes.zip( [ nil ] * attributes.length ) ]
    else
      attributes.first
    end

  assign_attributes hash
  self
end
to_entity() click to toggle source

@return [self]

# File lib/shamu/entities/entity.rb, line 113
def to_entity
  self
end

Private Instance Methods

attribute_eql?( other, name ) click to toggle source
# File lib/shamu/entities/entity.rb, line 146
def attribute_eql?( other, name )
  value = send( name )
  other_value = other.send( name )

  if value.is_a?( Entity ) || other_value.is_a?( Entity )
    return value.id.eql?( other_value.id )
  else
    value.eql?( other_value )
  end
end
serialize_attribute?( name, options ) click to toggle source
Calls superclass method Shamu::Attributes#serialize_attribute?
# File lib/shamu/entities/entity.rb, line 142
def serialize_attribute?( name, options )
  super && !options[:model]
end