module Cistern::Associations

Public Class Methods

extended(klass) click to toggle source
Calls superclass method
# File lib/cistern/associations.rb, line 4
def self.extended(klass)
  def klass.association_overlay
    @association_overlay ||= const_set(:Associations, Module.new)
  end

  klass.send(:include, klass.association_overlay)

  super
end

Public Instance Methods

associations() click to toggle source

Lists the associations defined on the resource @return [Hash{Symbol=>Array}] mapping of association type to name

# File lib/cistern/associations.rb, line 16
def associations
  @associations ||= Hash.new { |h, k| h[k] = [] }
end
belongs_to(name, *args, &block) click to toggle source

Define an assocation that references a model. @param name [Symbol] name of association and corresponding reader. @param scope [Proc] returning a {Cistern::Model} that is evaluated within the context of the model. @return [Cistern::Model] as defined by {#scope} @example

class Firm < Law::Model
  identity :registration_id
  belongs_to :leader, -> { cistern.employees.get(:ceo) }
 end
# File lib/cistern/associations.rb, line 77
def belongs_to(name, *args, &block)
  name_sym = name.to_sym

  reader_method = name
  writer_method = "#{name}="

  options = args.last.is_a?(::Hash) ? args.pop : {}
  scope = args.first || block

  attribute name_sym, options.merge(writer: false, reader: false)

  association_overlay.module_eval do
    define_method reader_method do
      model = instance_exec(&scope)
      attributes[name_sym] = model.attributes
      model
    end
  end

  association_overlay.module_eval do
    define_method writer_method do |model|
      previous_value = attributes[name_sym]
      new_value = model.respond_to?(:attributes) ? model.attributes : model
      attributes[name_sym] = new_value

      changed!(name_sym, previous_value, new_value)

      new_value
    end
  end

  associations[:belongs_to] << name_sym
end
has_many(name, *args, &block) click to toggle source

Define an assocation that references a collection. @param name [Symbol] name of association and corresponding reader and writer. @param scope [Proc] returning {Cistern::Collection} instance to load models into. {#scope} is evaluated within the

context of the model.

@return [Cistern::Collection] as defined by {#scope} @example

class Firm < Law::Model
  identity :registration_id
  has_many :lawyers, -> { cistern.associates(firm_id: identity) }
 end
# File lib/cistern/associations.rb, line 30
def has_many(name, *args, &block)
  name_sym = name.to_sym

  reader_method = name
  writer_method = "#{name}="

  options = args.last.is_a?(::Hash) ? args.pop : {}
  scope = args.first || block

  attribute name_sym, options.merge(writer: false, reader: false, type: :array)

  association_overlay.module_eval do
    define_method reader_method do
      collection = instance_exec(&scope)
      records = attributes[name_sym] || []

      collection.load(records) if records.any?
      collection
    end
  end

  association_overlay.module_eval do
    define_method writer_method do |models|
      previous_value = attributes[name_sym]
      new_value =
        attributes[name_sym] = Array(models).map do |model|
          model.respond_to?(:attributes) ? model.attributes : model
        end

      changed!(name_sym, previous_value, new_value)

      new_value
    end
  end

  associations[:has_many] << name_sym
end