Convert Rails method to chainable scope

121 views Asked by At

I have a page on my site that allows people to filter cars based on a number of criteria. These criteria are passed via parameters.

I want to add a new criteria but the logic in the model that does this is a method. I can't add this onto my controllers filtering logic as that is all based on Active Record queries and chainable parts.

How can I convert the following method into something that is compatible with my queries and chains in my filter logic?

Method:

  def self.new_cars_in_year(year)
    new_cars = []

    Car.all.includes(:drives).each do |car|
      years_driven_car = []
      c = car.drives.where("date IS NOT ?", nil)

      c.each do |drive|
        years_driven_car << (Date.parse drive.date).year
      end

      years_driven_car = years_driven_car.uniq

      if car.unknown_drives
        years_driven_car.shift
      end

      if years_driven_car.min == year
        new_cars << car
      end
    end

    new_cars
  end

Schema:

ActiveRecord::Schema.define(version: 20170816154910) do
  create_table "cars", force: :cascade do |t|
    t.text     "notes",                     limit: 65535
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "slug",                      limit: 255
    t.string   "covering",                  limit: 255
    t.text     "style",                     limit: 65535
    t.text     "model",                     limit: 65535
    t.float    "speed",                     limit: 24
    t.boolean  "unknown_drives"
  end

  create_table "drives", force: :cascade do |t|
    t.integer  "car_id", limit: 4
    t.string   "notes",      limit: 255
    t.string   "date",       limit: 255
    t.datetime "created_at",             null: false
    t.datetime "updated_at",             null: false
  end
end

Thankyou

Edit:

1

There are 1 answers

0
roo On

The issue as I see it is that you want the chain a Car collection but filtered on if the car has not been driven before a particular year. There could be several 'drives' in the selected year so you need to aggregate on the year the drive occurred.

If this was SQL it would be something like:

select cars.* from cars inner join drives on drives.car_id = cars.id
  where YEAR(STR_TO_DATE(`drives.date`, "%m/%d/%Y")) = <some year>
  group by cars.id

This could work, though I'm not familiar with your application:

Car.joins(:drives)
  .where('year(str_to_date(`drives.date`, "%m/%d/%Y")) = ?', year)
  .uniq # Need to do distinct cars.id otherwise duplicate records