Skip to content

Instantly share code, notes, and snippets.

@RichardJordan
Last active April 28, 2016 05:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RichardJordan/d40ff2f76e234ae22e6938a0d69bc5fe to your computer and use it in GitHub Desktop.
Save RichardJordan/d40ff2f76e234ae22e6938a0d69bc5fe to your computer and use it in GitHub Desktop.

My basic query object pattern is super simple building on what many people seem to do nowadays.

  1. Never use method missing - always an explicitly defined interface.
  2. expose #relation and #all
  3. always return self to keep methods chainable

Example:

class CustomerQuery
  attr_reader :relation
  delegate :all, to: :relation

  def initialize(relation=Customer)
    @relation = relation
  end

  # e.g. using kaminari but perhaps incoming params in different format
  def paginate(page_param)
    unless page_param.blank?
      page_number = page_param[:number]
      per_page = page_param[:size]
      @relation = relation.page(page_number).per(per_page)
    end
    self
  end

  # use it to add include to reduce n+1 queries
  def with_addresses
    @relation = relation.includes(:addresses)
    self
  end

  # or just typical scope type things
  def without_test_orders
    @relation = relation.where(test_order: false)
    self
  end
end

Use:

$ query = CustomerQuery.new
$ query.without_test_orders.with_adress.paginate({ number: 4, size: 10 }).all
$ query.active.relation.where(market_id: 1).first

$ new_query = CustomerQuery.new(Customer.where(suspended: true))
$ new_query.with_address.all

Can add authorization using some kind of authorization policy object something like this:

class CustomerQuery
  delegate :all, to: :authorized_relation

  def initialize(relation=Customer, authorization: nil)
    @unauthorized_relation = relation
    @authorization = authorization
  end

  def authorized_relation
    @authorized_relation ||= @unauthorized_relation.where(group_id: permitted_customer_groups)
  end
  
  def active
    @authorized_relation = authorized_relation.where(active: true)
    self
  end
  
  private
  
  def permitted_customer_groups
    authorization.permitted_customer_groups if authorization
  end
end

Use:

$ customers_query = CustomerQuery.new(authorization: current_authorization)
$ active_customers = customers_query.active.all
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment