0

I have the following method in an ActiveRecord, with some simplified classes/associations detailed as well:

class LineItem < ApplicationRecord
  has_many :line_item_parts

  def media
    best = line_item_parts.detect do |part|
      part.part.medias.any?
    end
    best_media = best && best.part.medias.first
    best_media || product.medias.first
  end
end

class LineItemPart < ApplicationRecord
  belongs_to :line_item
  belongs_to :part
end

class Part < ApplicationRecord
  belongs_to :product
  has_many :medias
end

class Media < ApplicationRecord
  belongs_to :product
  belongs_to :part, optional: true
end

When running it on the server in a binding.pry, I do the following:

> line_item.media #=> nil
> line_item.media #=> <Media: ...>

It sometimes will return nil the first time, but when I run it a second time it always returns the correct object. I see the following line in the logs for the first run:

  Products::Media Exists (1.0ms)  SELECT  1 AS one FROM "products_medias" WHERE "products_medias"."part_id" = $1 LIMIT $2  [["part_id", 472], ["LIMIT", 1]]

Executing the query manually returns a Media result, as expected. And in the second run, it's the same query but uses AR's CACHE, but on the second run it returns the correct result (a non-nil Media object).

How is this possible? Is this an ActiveRecord bug? I don't even know where to start with googling something like this.

Gems:

  • Rails (5.1.5)
  • pg (0.21.0)

Postgres version: psql (PostgreSQL) 10.2 (Debian 10.2-1.pgdg90+1)

Running in Docker for Mac.

5
  • Can you post the classes so we can understand what everything is? At the very least we need the names and relationships. Commented Apr 2, 2018 at 18:29
  • @JacobVanus done! Sorry about that. Commented Apr 2, 2018 at 18:40
  • Do you mean LineItemPart has_many :medias, through: :part? Commented Apr 2, 2018 at 18:56
  • @pdobb Good catch! I didn't actually declare that relation in my code. I've made the other associations a bit clearer too. Commented Apr 2, 2018 at 18:59
  • Also: This only happens in the rails server process. Inside a rails console, the configuration#media method seems to work 100% of the time (with the same configuration ids). Commented Apr 2, 2018 at 19:09

1 Answer 1

1

I don't see the error, but that looks like it will result in several queries anyway. Might I suggest an alternative?

 def media
   best_media = Media
      .joins(parts: [line_item_parts: :line_items])
      .where('line_items.id = ?', id)
      .first
   best_media || product.medias.first
 end

If you want to stick with using the associations directly, you might try adding .to_a before the enumerator method so you are explicitly operating on the results.

def media
  best = line_item_parts.to_a.detect do |lip|
    lip.part.medias.any?
  end
  best_media = best && best.part.medias.first
  best_media || product.medias.first
end
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.