dior001
dior001

Reputation: 761

Combining blocks in Ruby

I'm configuring Algolia search for Rails. The algoliasearch method accepts a block which contains index configuration settings. Each of our searchable models has a public and a private index. The configuration of these indexes is identical with the exception that data in the public index is only indexed when the public? method returns true. I'm trying to find a way where I can not repeat the configuration specified for the secured index in the public index so that the code is more DRY. I was thinking along the lines of extracting the configuration into a Proc and using that Proc in both the secured index and public index blocks. I haven't been able to make this work. Does anyone have any suggestions about how to implement this?

algoliasearch index_name: SECURED_INDEX_NAME do
  # Index configuration. I want to extract this so I can reuse it.
  attribute :name

  add_index PUBLIC_INDEX_NAME, if: :public? do
    # Repeat index configuration here.  
  end
end

https://github.com/algolia/algoliasearch-rails#target-multiple-indexes

Here's what I have so far. It doesn't work.

module Algolia::UserIndexConcern
  extend ActiveSupport::Concern

  included do
    PUBLIC_INDEX_NAME  = "User_Public_#{Rails.env}"
    PRIVATE_INDEX_NAME = "User_Private_#{Rails.env}"

    index_config = build_index_config(index_name: PRIVATE_INDEX_NAME) # Obviously can't call method here but need to?
    algoliasearch index_name: PRIVATE_INDEX_NAME, sanitize: true, force_utf8_encoding: true, &index_config

    private def build_index_config(index_name:)
      Proc.new do
        # =====================
        # Attributes
        # =====================

        # Searchable.

        attribute :name do
          full_name
        end

        attribute :primary_employer do
          primary_employer.try(:name)
        end

        attributesToIndex ['unordered(name)', 'unordered(primary_employer)']

        attributesToHighlight [:name, :primary_employer]

        if index_name == PRIVATE_INDEX_NAME
          index_config = build_index_config(index_name: PUBLIC_INDEX_NAME)
          add_index PUBLIC_INDEX_NAME, &index_config
        end
      end
    end

    private def public?
      !exclude_from_search? && !admin_exclude_from_search?
    end
  end
end

Upvotes: 2

Views: 447

Answers (1)

dior001
dior001

Reputation: 761

Instance_eval is the key to solving this.

Evaluates a string containing Ruby source code, or the given block, within the context of the receiver (obj). In order to set the context, the variable self is set to obj while the code is executing, giving the code access to obj’s instance variables.

PUBLIC_INDEX_NAME  = "User_Public_#{Rails.env}"
PRIVATE_INDEX_NAME = "User_Private_#{Rails.env}"

common_index_attributes = Proc.new do

  # Define search attributes.

end

algoliasearch index_name: PRIVATE_INDEX_NAME, sanitize: true, force_utf8_encoding: true do
  self.instance_eval &common_index_attributes

  add_index PUBLIC_INDEX_NAME, if: :public? do
    self.instance_eval &common_index_attributes
  end
end

private def public?
  # Excluded for brevity
end

Upvotes: 3

Related Questions