4
votes

I have a case where I have a model called Event which: * has_one primary_day * has_one backup_day * has_one emergency_day

In each of the has_one associations there a group of boolean fields that are the same e.g. approved_legal,approved_malfunction,approved_costs etc

In my search form - I don't want to search each association individually for those same fields.

e.g I don't want to do this:

<%= f.check_box :primary_day_approved_legal_true %>

for each of the fields as it's too verbose instead I want to do this:

<%= f.check_box :approved_legal_true %> 

which will collate all the associations and return Events that have approved_legal true across all the has_one associations.

I have tried a few different approaches to solving this but I can't get it to work so I assume that what I want to do is currently not possible. Could you confirm my theory or let me know how it could be achieved if it is indeed possible?

For reference I have tried the following:

1. custom ransacker in the Event model

Since I'm happy to use the existing true predicate I thought just adding in a ransacker that returned what I wanted would be possible. But it seems not e.g.

ransacker :created_at do |parent|
  Arel.sql('select * from events inner join on primary_days .......')
end

this ransacker seems to alway start with a select on the Model it was added to. So I cannot use a join to the associations. It seems the ransacker must return an existing database column on the model it's related to. So for the same reason there was not Arel code I could come up with that would make this approach work. Despite trying to create an Arel table to represent the associations and then trying to do the join in arel - but the result was always the same - no such method eq on ArelSelectManager.

e.g

ransacker :created_at do |parent|
  primary_days = Arel::Table.new(:primary_days)
  events = parent.table
  events.join(primary_days).on(events[:id].eq(primary_days[:event_id]))
end

2. Using a named scope with a patch for scopes found in issue 61 - https://gist.github.com/RohanM/5350768

I put the code in the initializers/ransack.rb and then:

Model:

scope_ransackable :approved_legal
scope :approved_legal_scope, -> { 
  Event.joins(:primary_day).where(:primary_days: {approved_legal: true}) 
}

Controller

Event.search_with_scopes(params[q])

View

<%= f.check_box(:approved_legal_scope)%>

But this just resulted in an error saying that approved_legal_scope method was not found on Ransack Search object

Any pointer in the right direction would be very much appreciated. Thanks.

1
Scope's not found since you've whitelisted "approved_legal" instead of "approved_legal_scope"(which you're using).Vadim Chumel
You don't have to do the join form within Arel. It will pick up your joined tables which you can specify in the controller.Sash

1 Answers

1
votes

I have managed to achieve this with chaining scopes with a pre-Rails 5 hack.

scope :short_search_scope, ->(search) {
   joins(:something)
  .where(
    unscoped.one_scope(search)
     .another_scope(search)
     .where_values.join(' OR '))
}

scope :one_scope, -> (search) {
  where(approved_legal: true)
}

...

def self.ransackable_scopes(auth_object = nil)
  [:short_search_scope]
end

The where_values.join(' OR ') can be replaced with where().or.where() when this feature is released in Rails 5.