0

Given I have an AR object (object) and a string (s). s is based out of the associations of object. ie. Product belongs to Aisle, Aisle belongs to Department, Department belongs to Shop

I want to get an array of all those associations. This is what Ive come up with

object = Product.last
s='aisle.department.shop'

new_array = s.split(',').each_with_object([]).with_index do |(assoc_set, array), index|
  a = assoc_set.split('.')
  a.each_with_index do |assoc, index|
    if index == 0
      array << object.send(assoc)

    elsif index == 1
      array << object.send(a[index - 1]).send(a[index])

    elsif index == 2
      array << object.send(a[index - 2]).send(a[index - 1]).send(a[index])

    elsif index == 3
      array << object.send(a[index - 3]).send(a[index - 2]).send(a[index - 1]).send(a[index])
    end
  end
end

Outputs exactly what I want:

[
  #<Aisle:0x00007fa001d6f800 ...>,
  #<Department:0x00007fa001d44b00 ...>,
  #<Shop:0x00007fa020f4bc68 ...>,
]

Except code isnt dynamic. As you can see, it only goes up to 3 levels deep. How can I refactor this?

Christian Fazzini
  • 19,613
  • 21
  • 110
  • 215
  • 1
    Why do you need this string `aisle.department.shop`? Is it the same all the time? You can call associations one by one. – Yakov Apr 01 '20 at 08:31
  • Checkout https://stackoverflow.com/questions/4099409/ruby-how-to-chain-multiple-method-calls-together-with-send to get some help around how to make it dynamic – Amit Patel Apr 01 '20 at 08:34
  • @Yakov It will be based out of `params`. Meaning it wont be the same all the time – Christian Fazzini Apr 01 '20 at 08:35

1 Answers1

2

This should work as you wanted

object = Product.last
relations_chain='aisle.department.shop'

relations_chain.split('.').each_with_object([]).with_index do |(relation_name, array), index|
  prev_object = index.zero? ? object : array[index - 1]
  array << prev_object.send(relation_name)
end

You can delegate relations and call everything on Product model.

class Department < ApplicationRecord
  delegate :shop, to: :department, allow_nil: true
end

class Product < ApplicationRecord
  delegate :department, to: :aisle, allow_nil: true
  delegate :shop, to: :aisle, allow_nil: true
end

relations_chain.split('.').map { |relation_name| object.send(relation_name) }
Yakov
  • 3,033
  • 1
  • 11
  • 22