Module: ComputedModel::Model

Extended by:
ActiveSupport::Concern
Defined in:
lib/computed_model/model.rb

Overview

A mixin for batch-loadable compound models. This is the main API of ComputedModel.

See ClassMethods for methods you can use in the including classes.

Examples:

require 'computed_model'

# Consider them external sources (ActiveRecord or resources obtained via HTTP)
RawUser = Struct.new(:id, :name, :title)
Preference = Struct.new(:user_id, :name_public)

class User
  include ComputedModel::Model

  attr_reader :id
  def initialize(raw_user)
    @id = raw_user.id
    @raw_user = raw_user
  end

  def self.list(ids, with:)
    bulk_load_and_compute(Array(with), ids: ids)
  end

  define_primary_loader :raw_user do |_subfields, ids:, **|
    # In ActiveRecord:
    # raw_users = RawUser.where(id: ids).to_a
    raw_users = [
      RawUser.new(1, "Tanaka Taro", "Mr. "),
      RawUser.new(2, "Yamada Hanako", "Dr. "),
    ].filter { |u| ids.include?(u.id) }
    raw_users.map { |u| User.new(u) }
  end

  define_loader :preference, key: -> { id } do |user_ids, _subfields, **|
    # In ActiveRecord:
    # Preference.where(user_id: user_ids).index_by(&:user_id)
    {
      1 => Preference.new(1, true),
      2 => Preference.new(2, false),
    }.filter { |k, _v| user_ids.include?(k) }
  end

  delegate_dependency :name, to: :raw_user
  delegate_dependency :title, to: :raw_user
  delegate_dependency :name_public, to: :preference

  dependency :name, :name_public
  computed def public_name
    name_public ? name : "Anonymous"
  end

  dependency :public_name, :title
  computed def public_name_with_title
    "#{title}#{public_name}"
  end
end

# You can only access the field you requested ahead of time
users = User.list([1, 2], with: [:public_name_with_title])
users.map(&:public_name_with_title) # => ["Mr. Tanaka Taro", "Dr. Anonymous"]
users.map(&:public_name) # => error (ForbiddenDependency)

users = User.list([1, 2], with: [:public_name_with_title, :public_name])
users.map(&:public_name_with_title) # => ["Mr. Tanaka Taro", "Dr. Anonymous"]
users.map(&:public_name) # => ["Tanaka Taro", "Anonymous"]

# In this case, preference will not be loaded.
users = User.list([1, 2], with: [:title])
users.map(&:title) # => ["Mr. ", "Dr. "]

Defined Under Namespace

Modules: ClassMethods

Instance Method Summary collapse

Instance Method Details

#current_depsSet<Symbol>

Returns dependency of the currently computing field, or the toplevel dependency if called outside of computed fields.

Returns:

  • (Set<Symbol>)


430
431
432
# File 'lib/computed_model/model.rb', line 430

def current_deps
  @__computed_model_stack.last.deps
end

#current_subfieldsComputedModel::NormalizableArray?

Returns subfield selectors passed to the currently computing field, or nil if called outside of computed fields.



437
438
439
# File 'lib/computed_model/model.rb', line 437

def current_subfields
  @__computed_model_stack.last.subfields
end