Capsula

The tool for encapsulating (preloading, including) related objects into any object.

How to use:

starships = <get_your_space_fleet>

StarshipsEncapsulator.new(starships).plans(
  :fuel, :oxygen, :food, { spaceman: [:space_suit] }
).encapsulate

# Now let's get a suit for our spaceman:
starships.first&.spaceman&.space_suit
=> <awesome space suit> # Capsula's wrapper answers

Let's see how StarshipsEncapsulator looks:

class StarshipsEncapsulator < Capsula::Base
  plan_for :fuel,
    src_key: :fuel_id,
    dst_key: :id,
    dst_loader: ->(ids,opt){ MyFuelStation.find_fuel_by_ids(ids) }
    # Example loader for ActiveRecord model Fuel:
    # dst_loader: ->(ids,opt){ Fuel.where(id: ids).includes(opt[:plans]).to_a }

  # Plans for other relations...
end

Definition for src_key and dst_key can be skipped if they values are :fuel_id (<key_name>_id) and :id

Also, for key-definition can be used lambdas:

src_key: ->(o){ o.some_hash_data.fetch(:fuel_id, default: 'A-95') }
dst_key: ->(o){ o.extact_fuel_id_from_octane_number }

How it works:

All objects is wrapping into special transparent wrapper which translate all methods to wrapped object, except methods-names which was used for encapsulating before:

StarshipsEncapsulator.new(starships).plan(:fuel, :oxygen).encapsulate
starships.first.oxygen
=> <oxygen> # instant response, because already present in Capsula

Rest methods transparently sending to wrapped object:

starships.first.food
=> <pizza> # returns to Mothership and cook pizza

Custom encapsulators (dst_loaders):

For difficult preloading logic can be used custom loader:

class CustomLoader
    def initialize items:, opt: {}
        @items = items; @opt = opt; @store = [];
    end

    # This method is triggered by Capsula
    def collect_ids_and_load_relations
        ids = @items.map{ |i| i.fuel_id }
        @store = Fuel.where(id: ids).to_a
    end

    # This method is calling by Capsula during encapsulation
    def get_preloads_for_object starship
        @store.find { |fuel| starship.fuel_id == fuel.id }
    end
end


class StarshipsEncapsulator < Capsula::Base
  plan_for :fuel, delegate_to: CustomLoader
end

Contributing

  • Fork the project.
  • Run bundle install
  • Run bundle exec guard
  • Make your feature addition or bug fix.
  • Add tests for it. This is important.
  • Send me a pull request.