Git Product home page Git Product logo

goldiloader's Introduction

Goldiloader

Gem Version Build Status Code Climate Coverage Status

Wouldn't it be awesome if ActiveRecord didn't make you think about eager loading and it just did the "right" thing by default? With Goldiloader it can!

This branch only supports Rails 6.1+ with Ruby 3.0+. For older versions of Rails/Ruby use release-4.x, release-3.x, release-2.x or release-1.x.

Consider the following models:

class Blog < ActiveRecord::Base
  has_many :posts
end

class Post < ActiveRecord::Base
  belongs_to :blog
end

Here are some sample queries without the Goldiloader:

> blogs = Blog.limit(5).to_a
# SELECT * FROM blogs LIMIT 5

> blogs.each { |blog| blog.posts.to_a }
# SELECT * FROM posts WHERE blog_id = 1
# SELECT * FROM posts WHERE blog_id = 2
# SELECT * FROM posts WHERE blog_id = 3
# SELECT * FROM posts WHERE blog_id = 4
# SELECT * FROM posts WHERE blog_id = 5

Here are the same queries with the Goldiloader:

> blogs = Blog.limit(5).to_a
# SELECT * FROM blogs LIMIT 5

> blogs.each { |blog| blog.posts.to_a }
# SELECT * FROM posts WHERE blog_id IN (1,2,3,4,5)

Whoa! It automatically loaded all of the posts for our five blogs in a single database query without specifying any eager loads! Goldiloader assumes that you'll access all models loaded from a query in a uniform way. The first time you traverse an association on any of the models it will eager load the association for all the models. It even works with arbitrary nesting of associations.

Read more about the motivation for the Goldiloader in this blog post.

Installation

Add this line to your application's Gemfile:

gem 'goldiloader'

And then execute:

$ bundle

Or install it yourself as:

$ gem install goldiloader

Usage

By default all associations will be automatically eager loaded when they are first accessed so hopefully most use cases should require no additional configuration. Note you're still free to explicitly eager load associations via eager_load, includes, or preload.

Disabling Automatic Eager Loading

You can disable automatic eager loading with auto_include query scope method:

Blog.order(:name).auto_include(false)

Note this will not disable automatic eager loading for nested associations.

Automatic eager loading can be disabled for specific associations by customizing the association's scope:

class Blog < ActiveRecord::Base
  has_many :posts, -> { auto_include(false) }
end

Automatic eager loading can be disabled globally disabled for all threads:

# config/initializers/goldiloader.rb
Goldiloader.globally_enabled = false

Automatic eager loading can then be selectively enabled for particular sections of code:

# Using a block form
Goldiloader.enabled do
  # Automatic eager loading is enabled for the current thread
  # ...
end

# Using a non-block form
Goldiloader.enabled = true
# Automatic eager loading is enabled for the current thread
# ...
Goldiloader.enabled = false

Similarly, you can selectively disable automatic eager loading for particular sections of code in a thread local manner:

# Using a block form
Goldiloader.disabled do
  # Automatic eager loading is disabled for the current thread
  # ...
end

# Using a non-block form
Goldiloader.enabled = false
# Automatic eager loading is disabled for the current thread
# ...
Goldiloader.enabled = true

Note Goldiloader.enabled=, Goldiloader.enabled, and Goldiloader.disabled are thread local to ensure proper thread isolation in multi-threaded servers like Puma.

Association Options

Goldiloader supports a few options on ActiveRecord associations to customize its behavior.

fully_load

There are several association methods that ActiveRecord can either execute on in memory models or push down into SQL depending on whether or not the association is loaded. This includes the following methods:

  • first
  • second
  • third
  • fourth
  • fifth
  • forty_two (one of the hidden gems in Rails 4.1)
  • last
  • size
  • ids_reader
  • empty?
  • exists?

This can cause problems for certain usage patterns if we're no longer specifying eager loads:

> blogs = Blog.limit(5).to_a
# SELECT * FROM blogs LIMIT 5

> blogs.each do |blog|
    if blog.posts.exists?
      puts blog.posts
    else
      puts 'No posts'
  end
# SELECT 1 AS one FROM posts WHERE blog_id = 1 LIMIT 1
# SELECT * FROM posts WHERE blog_id IN (1,2,3,4,5)

Notice the first call to blog.posts.exists? was executed via SQL because the posts association wasn't yet loaded. The fully_load option can be used to force ActiveRecord to fully load the association (and do any necessary automatic eager loading) when evaluating methods like exists?:

class Blog < ActiveRecord::Base
  has_many :posts, fully_load: true
end

Limitations

Goldiloader leverages the ActiveRecord eager loader so it shares some of the same limitations. See eager loading workarounds for some potential workarounds.

has_one associations that rely on a SQL limit

You should not try to auto eager load (or regular eager load) has_one associations that actually correspond to multiple records and rely on a SQL limit to only return one record. Consider the following example:

class Blog < ActiveRecord::Base
  has_many :posts
  has_one :most_recent_post, -> { order(published_at: desc) }, class_name: 'Post'
end

With standard Rails lazy loading the most_recent_post association is loaded with a query like this:

SELECT * FROM posts WHERE blog_id = 1 ORDER BY published_at DESC LIMIT 1

With auto eager loading (or regular eager loading) the most_recent_post association is loaded with a query like this:

SELECT * FROM posts WHERE blog_id IN (1,2,3,4,5) ORDER BY published_at DESC

Notice the SQL limit can no longer be used which results in fetching all posts for each blog. This can cause severe performance problems if there are a large number of posts.

Other Limitations

Associations with any of the following options cannot be eager loaded:

  • limit
  • offset
  • finder_sql

Goldiloader detects associations with any of these options and disables automatic eager loading on them.

It might still be possible to eager load these with Goldiloader by using custom preloads.

Eager Loading Limitation Workarounds

Most of the Rails limitations with eager loading can be worked around by pushing the problematic SQL into the database via lateral joins (or database views if your database doesn't support lateral joins). Consider the following example with associations that can't be eager loaded due to SQL limits:

class Blog < ActiveRecord::Base
  has_many :posts
  has_one :most_recent_post, -> { order(published_at: desc) }, class_name: 'Post'
  has_many :recent_posts, -> { order(published_at: desc).limit(5) }, class_name: 'Post'
end

This can be reworked to push the order/limit into lateral joins like this:

class Blog < ActiveRecord::Base
  has_many :posts
  has_one :most_recent_post, -> {
    joins(Arel.sql(<<-SQL.squish))
      INNER JOIN LATERAL (
        SELECT id
        FROM posts p1
        WHERE blog_id = posts.blog_id
        ORDER BY published_at DESC
        LIMIT 1
      ) p2 on (p2.id = posts.id)
    SQL
  }, class_name: 'Post'
  has_many :recent_posts, -> {
    joins(Arel.sql(<<-SQL.squish))
      INNER JOIN LATERAL (
        SELECT id
        FROM posts p1
        WHERE blog_id = posts.blog_id
        ORDER BY published_at DESC
        LIMIT 5
      ) p2 on (p2.id = posts.id)
    SQL
  }, class_name: 'Post'
end

Custom Preloads

In addition to preloading relations, you can also define custom preloads by yourself in your model. The only requirement is that you need to be able to perform a lookup for multiple records/ids and return a single Hash with the ids as keys. If that's the case, these preloads can nearly be anything. Some examples could be:

  • simple aggregations (count, sum, maximum, etc.)
  • more complex custom SQL queries
  • external API requests (ElasticSearch, Redis, etc.)
  • relations with primary keys stored in a jsonb column

Here's how:

class Blog < ActiveRecord::Base
  has_many :posts

  def posts_count
    goldiload do |ids|
      # By default, `ids` will be an array of `Blog#id`s
      Post
        .where(blog_id: ids)
        .group(:blog_id)
        .count
    end
  end
end

The first time you call the posts_count method, it will call the block with all model ids from the current context and reuse the result from the block for all other models in the context.

A more complex example might use a custom primary key instead of id, use a non ActiveRecord API and have more complex return values than just scalar values:

class Post < ActiveRecord::Base
  def main_translator_reference
    json_payload[:main_translator_reference]
  end

  def main_translator
    goldiload(key: :main_translator_reference) do |references|
      # `references` will be an array of `Post#main_translator_reference`s
      SomeExternalApi.fetch_translators(
        id: references
      ).index_by(&:id)
    end
  end
end

If you want to preload something that is based on multiple keys, you can also pass an array:

class Meeting < ActiveRecord::Base
  def organizer_notes
    goldiload(key: [:organizer_id, :room_id]) do |id_sets|
      # +id_sets+ will be a two dimensional array with the
      # organizer_id and room_id for each item, e.g.
      # [
      #   [<organizer_id_1>, <room_id_1>],
      #   [<organizer_id_2>, <room_id_2>]
      # ]
      notes = logic_for_fetching_organizer_notes
      notes.group_by do |report|
        [report.organizer_id, report.room_id]
      end
    end
  end
end

Note: The goldiload method will use the source_location of the given block as a cache name to distinguish between multiple defined preloads. If this causes an issue for you, you can also pass a cache name explicitly as the first argument to the goldiload method.

Gotchas

Even though the methods in the examples above (posts_count, main_translator) are actually instance methods, the block passed to goldiload should not contain any references to these instances, as this could break the internal lookup/caching mechanism. We prevent this for the self keyword, so you'll get a NoMethodError. If you get this, you might want to think about the implementation rather than just trying to work around the exception.

Upgrading

From 0.x, 1.x

The auto_include association option has been removed in favor of the auto_include query scope method. Associations that specify this option must migrate to use the query scope method:

class Blog < ActiveRecord::Base
  # Old syntax
  has_many :posts, auto_include: false

  # New syntax
  has_many :posts, -> { auto_include(false) }
end

Status

This gem is tested with Rails 6.1, 7.0, 7.1, and Edge using MRI 3.0, 3.1, 3.2, and 3.3.

Let us know if you find any issues or have any other feedback.

Change log

See the change log.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

goldiloader's People

Contributors

apauly avatar atsheehan avatar bdurand avatar dfreeman avatar flixt avatar fsateler avatar ghiculescu avatar giovannibonetti avatar gremerritt avatar grosser avatar hsbt avatar jcalvert avatar joshbranham avatar joshrpowell avatar jturkel avatar miraks avatar olleolleolle avatar oyeanuj avatar pikachuexe avatar rodrigovirgilio avatar sobrinho avatar swrobel avatar yunusbulutt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

goldiloader's Issues

inverse_of does not work

I have two models like below:

model

class Parent < ApplicationRecord
  has_many :children
end
class Child < ApplicationRecord
  belongs_to :parent
end

Without goldiloader, "inverse_of" automatically works.

parent = Parent.create
parent.children.create
parent.children.first.parent.object_id == parent.object_id # true
parent.children.each { |child| p child.parent.object_id == parent.object_id } # true
parent.reload
parent.children.first.parent.object_id == parent.object_id # true
parent.children.each { |child| p child.parent.object_id == parent.object_id } # true

But, "inverse_of" does not work correctly with goldiloader

parent = Parent.create
parent.children.create
parent.children.first.parent.object_id == parent.object_id # true
parent.children.each { |child| p child.parent.object_id == parent.object_id } # true
parent.reload
parent.children.first.parent.object_id == parent.object_id # true
parent.children.each { |child| p child.parent.object_id == parent.object_id } # **false**

rails: 5.0.2
goldiloader: 0.0.11

Can't auto eager load has_many through association with a scope that uses includes

Consider the following models:

class Blog < ActiveRecord::Base
  has_many :posts
  has_many :authors, -> { includes(:address).where('addresses.city IS NOT NULL').references(:address) }, through: :posts
end

class Post < ActiveRecord::Base
  belongs_to :blog
  belongs_to :author
end

class Author < ActiveRecord::Base
  has_many :posts
  has_one :address
end

class Address < ActiveRecord::Base
  belongs_to :author
end

Attempting to auto eager load (or regular eager load) Blog#authors results in the following error:

ActiveRecord::ConfigurationError: Association named 'address' was not found on Post; perhaps you misspelled it?

Underlying Rails bug is fixed by rails/rails#12725. Workaround in Golidloader is to set auto_include = false.

Stack level too deep, incomplete response

Hi, after latest update I am getting errors from the title.
Is this something that points to my code problem or goldieloader bug?
I mean, when i disable goldie it works just fine.

Thx

Auto eager loading broken for associations that use unscope

class Blog < ActiveRecord::Base
  has_many :posts
  has_many :all_posts, -> { unscope(where: :hidden) }, class_name: 'Post'
end

class Post < ActiveRecord::Base
  default_scope { where(hidden: false) }
end

Auto eager loading (and regular eager loading) of Blog#all_posts ignores the unscope.

Corresponding Rails bug is rails/rails#11036. Workaround is to set auto_include = false on the association.

Goldiloader don't preload data

Hi,

I configure goldiloader with Goldiloader.globally_enabled = false via rails initializer in config/initializers/goldiloader.rb:

# frozen_string_literal: true

Goldiloader.globally_enabled = false

and it doesn't preload data in blocks. For example:

def blueprints
  Goldiloader.enabled? # => false

  Goldiloader.enabled do
    Goldiloader.enabled? # => true

    ::Eve::Blueprint.order(:type_id) # here, goldiloader should preload data, but not.
  end
end

Changing Goldiloader.globally_enabled in initiliazer to true, "fixing" problem.

As I understand documentation, if I set Goldiloader.globally_enabled to true, goldiloader works.
If I set Goldiloader.globally_enabled to false, goldiloader disabled until I run anything in Goldiloader.enabled block.

Versions:

* rails (7.0.0.alpha2)
* goldiloader (4.1.2)

Auto eager loading broken for has_and_belongs_to_many associations that use the uniq option

Consider the following models:

class Post < ActiveRecord::Base
  has_and_belongs_to_many :authors, uniq: true
end

class Author < ActiveRecord::Base
  has_and_belongs_to_many :posts, uniq: true
end

The uniq option is ignored by Rails when eager loading Post#authors.

Note this is only a problem in Rails 3 since the uniq option was removed in Rails 4 (in favor of using a lambda to customize the association scope).

Failure during replace of HABTM associations

I have a straightforward HABTM setup:

class Subclass < ActiveRecord::Base
end

class ParticipantSubclass < Subclass
end

class Participant < ActiveRecord::Base
     has_and_belongs_to_many :participant_subclasses,
                          association_foreign_key: :subclass_id
end

Without goldiloader I get no errors from my unit tests.

However when I query with goldiloader in place I get strange results. I have a participant that is associated with 3 participant_subclasses.

when I do the following:

puts @participant.participant_subclasses.size # <= 3
puts @participant.participant_subclasses.to_a.size # <= 1

Visually if I p @participant.participant_subclasses I see one association:

#<ActiveRecord::Associations::CollectionProxy [#<ParticipantSubclass id: 912, classification_id: 257, title: "Development", description: nil, updated_at: "2016-03-29 18:37:47", source: "[email protected]", seq: 1, showhover: true, incpoints: false, excpoints: false, permalink: "912-23ac5e2f", assessment_id: 944, oeuse: false, created_at: "2014-12-31 17:19:23", type: "ParticipantSubclass", active: true, minrespondents: 2>]>

> p.participant_subclasses.map(&:id) #  => [912]
> p.participant_subclasses.ids # => [922, 912, 925]


This is very confusing. The way I found this is in my controller I replace the subclass list using the input:

@participant.participant_subclasses = subclass_list

When it tries to replace the list with the same list from input, it fails because it only recognizes that 1 of the 3 is already in place (912) and tries to add the other 2 and fails immediately adding 922 with a duplicate_key.

Ignore has one associations that relies on LIMIT 1

Hi there,

We can almost detect that relies on LIMIT 1 by checking if an order was specified.

Example:

class Order < ApplicationRecord
  has_many :payments
  has_one :last_payment, through: :payments, order: 'paid_at desc'
end

If the order is specified, we are certain that the database may have more than one record.

In that case, we can simply do not auto include because we know that may trigger a problem.

Wdyt?

New instances of a tree like model wrongfully return true on the exists? method of the children association

I hope the following testcase explains the problem better then any text i could write. The problem also seems to be limited to exists?, the children association itself returns an empty array.

Removing the goldiloader gem would make this test pass (just in case this questions comes to mind).

begin
  require 'bundler/inline'
rescue LoadError => e
  $stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler'
  raise e
end

gemfile(true) do
  source 'https://rubygems.org'
  gem 'rails'#, github: 'rails/rails'
  gem 'goldiloader', github: 'salsify/goldiloader'
  gem 'sqlite3'
end

require 'active_record'
require 'minitest/autorun'
require 'logger'
# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :comments, force: true do |t|
    t.integer :parent_id
  end
end

class Comment < ActiveRecord::Base
  has_many :children, class_name: 'Comment', foreign_key: :parent_id
end

class BugTest < Minitest::Test
  def test_association_stuff
    Comment.create!
    assert !Comment.new.children.exists?
  end
end

PS: Thank you for this nice gem.

Rails 5.2 Support

There are currently two failing tests with Rails 5.2 RC2:

Failures:

  1) Goldiloader when a model is destroyed auto eager loads the associaton on other models
     Failure/Error: ::ActiveRecord::Associations::Preloader.new.preload(models, [association_name])
     
     RuntimeError:
       Can't modify frozen hash
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activemodel-5.2.0.rc2/lib/active_model/attribute_set/builder.rb:44:in `[]='
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activemodel-5.2.0.rc2/lib/active_model/attribute_set.rb:57:in `write_from_user'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/attribute_methods/write.rb:51:in `_write_attribute'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/attribute_methods/write.rb:45:in `write_attribute'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/attribute_methods.rb:410:in `[]='
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/belongs_to_association.rb:92:in `replace_keys'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/belongs_to_association.rb:33:in `target='
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader/association.rb:50:in `associate_records_to_owner'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader/association.rb:26:in `block in run'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader/association.rb:25:in `each'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader/association.rb:25:in `run'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:142:in `block (2 levels) in preloaders_for_one'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:140:in `each'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:140:in `map'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:140:in `block in preloaders_for_one'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:139:in `each'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:139:in `flat_map'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:139:in `preloaders_for_one'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:106:in `preloaders_on'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:93:in `block in preload'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:92:in `each'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:92:in `flat_map'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:92:in `preload'
     # ./lib/goldiloader/association_loader.rb:18:in `eager_load'
     # ./lib/goldiloader/association_loader.rb:12:in `load'
     # ./lib/goldiloader/active_record_patches.rb:113:in `load_with_auto_include'
     # ./lib/goldiloader/active_record_patches.rb:126:in `find_target'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/association.rb:151:in `load_target'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/association.rb:47:in `reload'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/singular_association.rb:9:in `reader'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/builder/association.rb:108:in `blog'
     # ./spec/goldiloader/goldiloader_spec.rb:501:in `block (4 levels) in <top (required)>'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activesupport-5.2.0.rc2/lib/active_support/callbacks.rb:426:in `block in make_lambda'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activesupport-5.2.0.rc2/lib/active_support/callbacks.rb:236:in `block in halting_and_conditional'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activesupport-5.2.0.rc2/lib/active_support/callbacks.rb:517:in `block in invoke_after'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activesupport-5.2.0.rc2/lib/active_support/callbacks.rb:517:in `each'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activesupport-5.2.0.rc2/lib/active_support/callbacks.rb:517:in `invoke_after'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activesupport-5.2.0.rc2/lib/active_support/callbacks.rb:133:in `run_callbacks'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activesupport-5.2.0.rc2/lib/active_support/callbacks.rb:816:in `_run_destroy_callbacks'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/callbacks.rb:323:in `destroy'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/transactions.rb:305:in `block in destroy'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/transactions.rb:386:in `block in with_transaction_returning_status'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/connection_adapters/abstract/database_statements.rb:254:in `block in transaction'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/connection_adapters/abstract/transaction.rb:230:in `block in within_new_transaction'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/connection_adapters/abstract/transaction.rb:227:in `within_new_transaction'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/connection_adapters/abstract/database_statements.rb:254:in `transaction'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/transactions.rb:212:in `transaction'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/transactions.rb:383:in `with_transaction_returning_status'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/transactions.rb:305:in `destroy'
     # ./spec/goldiloader/goldiloader_spec.rb:503:in `block (3 levels) in <top (required)>'

  2) Goldiloader when a model is destroyed can load associations in after_destroy callbacks
     Failure/Error: ::ActiveRecord::Associations::Preloader.new.preload(models, [association_name])
     
     RuntimeError:
       Can't modify frozen hash
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activemodel-5.2.0.rc2/lib/active_model/attribute_set/builder.rb:44:in `[]='
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activemodel-5.2.0.rc2/lib/active_model/attribute_set.rb:57:in `write_from_user'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/attribute_methods/write.rb:51:in `_write_attribute'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/attribute_methods/write.rb:45:in `write_attribute'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/attribute_methods.rb:410:in `[]='
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/belongs_to_association.rb:92:in `replace_keys'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/belongs_to_association.rb:33:in `target='
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader/association.rb:50:in `associate_records_to_owner'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader/association.rb:26:in `block in run'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader/association.rb:25:in `each'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader/association.rb:25:in `run'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:142:in `block (2 levels) in preloaders_for_one'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:140:in `each'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:140:in `map'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:140:in `block in preloaders_for_one'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:139:in `each'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:139:in `flat_map'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:139:in `preloaders_for_one'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:106:in `preloaders_on'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:93:in `block in preload'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:92:in `each'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:92:in `flat_map'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/preloader.rb:92:in `preload'
     # ./lib/goldiloader/association_loader.rb:18:in `eager_load'
     # ./lib/goldiloader/association_loader.rb:12:in `load'
     # ./lib/goldiloader/active_record_patches.rb:113:in `load_with_auto_include'
     # ./lib/goldiloader/active_record_patches.rb:126:in `find_target'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/association.rb:151:in `load_target'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/association.rb:47:in `reload'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/singular_association.rb:9:in `reader'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/associations/builder/association.rb:108:in `blog'
     # ./spec/goldiloader/goldiloader_spec.rb:501:in `block (4 levels) in <top (required)>'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activesupport-5.2.0.rc2/lib/active_support/callbacks.rb:426:in `block in make_lambda'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activesupport-5.2.0.rc2/lib/active_support/callbacks.rb:236:in `block in halting_and_conditional'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activesupport-5.2.0.rc2/lib/active_support/callbacks.rb:517:in `block in invoke_after'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activesupport-5.2.0.rc2/lib/active_support/callbacks.rb:517:in `each'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activesupport-5.2.0.rc2/lib/active_support/callbacks.rb:517:in `invoke_after'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activesupport-5.2.0.rc2/lib/active_support/callbacks.rb:133:in `run_callbacks'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activesupport-5.2.0.rc2/lib/active_support/callbacks.rb:816:in `_run_destroy_callbacks'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/callbacks.rb:323:in `destroy'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/transactions.rb:305:in `block in destroy'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/transactions.rb:386:in `block in with_transaction_returning_status'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/connection_adapters/abstract/database_statements.rb:254:in `block in transaction'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/connection_adapters/abstract/transaction.rb:230:in `block in within_new_transaction'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/connection_adapters/abstract/transaction.rb:227:in `within_new_transaction'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/connection_adapters/abstract/database_statements.rb:254:in `transaction'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/transactions.rb:212:in `transaction'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/transactions.rb:383:in `with_transaction_returning_status'
     # /Users/jturkel/.rvm/gems/ruby-2.4.3/gems/activerecord-5.2.0.rc2/lib/active_record/transactions.rb:305:in `destroy'
     # ./spec/goldiloader/goldiloader_spec.rb:503:in `block (3 levels) in <top (required)>'

Finished in 2.24 seconds (files took 1.05 seconds to load)
75 examples, 2 failures

Failed examples:

rspec ./spec/goldiloader/goldiloader_spec.rb:511 # Goldiloader when a model is destroyed auto eager loads the associaton on other models
rspec ./spec/goldiloader/goldiloader_spec.rb:507 # Goldiloader when a model is destroyed can load associations in after_destroy callbacks

The run in Travis can be found here.

JRuby 9000 Support

Hopefully this is just enabling tests on JRuby 9000 but life is rarely that easy.

got an error while trying to load a polymorphic association

Hi

I have the following structure:

class User < ApplicationRecord
  ....
  has_many :app_notifications, inverse_of: :user, dependent: :destroy
end

class AppNotification < ApplicationRecord
  has_many :references,
           inverse_of: :app_notification,
           class_name: 'AppNotificationReference',
           dependent: :destroy
end

class AppNotificationReference < ApplicationRecord
  acts_as_paranoid

  belongs_to :app_notification, required: true, inverse_of: :references
  belongs_to :notifiable, -> { with_deleted }, polymorphic: true, required: true
end

I don't have any other validations.

after adding goldiloader i started seeing this error on my specs:

ArgumentError:
       Polymorphic associations do not support computing the class.
     # /usr/local/bundle/gems/goldiloader-4.0.0/lib/goldiloader/active_record_patches.rb:82:in `eager_loadable?'
     # /usr/local/bundle/gems/goldiloader-4.0.0/lib/goldiloader/active_record_patches.rb:114:in `eager_loadable?'
     # /usr/local/bundle/gems/goldiloader-4.0.0/lib/goldiloader/active_record_patches.rb:104:in `auto_include?'
     # /usr/local/bundle/gems/goldiloader-4.0.0/lib/goldiloader/active_record_patches.rb:121:in `load_with_auto_include'
     # /usr/local/bundle/gems/goldiloader-4.0.0/lib/goldiloader/active_record_patches.rb:143:in `find_target'
     # ./spec/domains/payments/card/services/authorization_handler_spec.rb:252:in `block (6 levels) in <top (required)>'


expect(app_notification.references.first.notifiable).to eq(payment_card)

I tried to add -> { auto_include(false) } to disable eager loading to the associations on app_notification, even on user. nothing helped.

Is this an issue or misconfiguration?

Associations break in after destroy

Imagine following situation.

class User < ActiveRecord::Base
  has_many :things

  def tickle; end
end

# Thing always has a user
class Thing < ActiveRecord::Base
  belongs_to :user
  after_destroy :tickle_user

  def tickle_user
    user.tickle
  end
end

When you destroy the user in plain AR it works fine. With Goldiloader activated it will claim user is nil. This can be fixed by loading the user association before the destroy is called, but it is surprising behaviour non the less.

I believe that the issue lays here lib/goldiloader/association_loader.rb:29

I don't think the check for destroy should happen. In case this checks happens to not hit "can't modify frozen hash", I would suggest falling back to default AR behaviour on destroyed models.

Goldiloader with ActiveStorage

Dear author(s),

I have issue with pack of goldiloader with ActiveStorage.
When i tried to remove something AS or update i fell into "Stack too deep" error.
Through this gem we can disabling goldiloader auto-loading only for associations.
It will be best upgrade for gem if it possible to disabling auto eager loading for AS association. For example something like this:
"has_many_attached :something, auto_include: :false"

If u have solution please share.
Best regards.

Too many assiated models may be loaded

First of all thanks for a great gem. I use this heavily.

I'll try to explain my problem:

I have a controller action that does something like this

def related
  room = Room.find(params[:id])
  collection = SimilarRooms.call(room: room, limit: 50)
  # collection is now an array of 50 Rooms
  render json: collection
end

The SimilarRooms service (stripped down to what matters):

def call
  similar_rooms = Room.within(100, origin: room) # <- this is the problematic line I think
  # finds all the rooms within 100km of the origin room
  # this returns a lot of rooms
  # ... do some math and some custom ordering ...
  similar_rooms.take(limit) # take the first 50 rooms
end

When I render the collection (which only includes the top 50 rooms) I also include data from an associated company has_one relation. That association is preloaded, however, it looks like it includes the company for every room found in the within query and not just the companies for the 50 rooms in the array that I try to render.

Does this explanation make sense? Is there anything to be done about this?

Rails 5.1 support

Can you please update the rails dependency so this works with 5.1. I'm able to use the gem successfully with 5.1.0.rc2.

Can't install in Rails 3.2 app when using RubyGems hosted gem

When trying to use the RubyGems hosted gem in a Rails 3.2 app I get the following error:

$ bundle install
Fetching source index from https://rubygems.org/
Fetching source index from https://rails-assets.org/
Resolving dependencies........
Bundler could not find compatible versions for gem "activesupport":
  In snapshot (Gemfile.lock):
    activesupport (3.2.18)

  In Gemfile:
    goldiloader (>= 0.0.2) ruby depends on
      activesupport (= 4.1) ruby

Running `bundle update` will rebuild your snapshot from scratch, using only
the gems in your Gemfile, which may resolve the conflict.

The workaround is to instead use the tag in the Gemfile:

gem 'goldiloader', github: 'salsify/goldiloader', tag: 'v0.0.2' 

Compatibility with Rails 6.1

Hello Salsify Team. Goldiloader seems to not be compatible with Rails 6.1, which was released today:

Bundler could not find compatible versions for gem "activerecord":
  In Gemfile:
    goldiloader (~> 3.1.1) was resolved to 3.1.1, which depends on
      activerecord (>= 4.2, < 6.1)

Is there a specific reason why it depends on AR < 6.1?

Goldiloader attempts to eager load uneager loadable associations

There are several Rails 3.2 association options that don't seem to work properly with eager loading:

  • limit
  • group
  • finder_sql
  • offset

In Rails 4 these options are set via block.

We should not attempt to eager load these problematic associations.

Note: Rails seems to let you eager load these associations and then either returns wrong results or if you're luck throws an exception (e.g. if the finder_sql is ignored you might get invalid SQL). I'll file a bug with Rails to handle these scenarios better.

Loading polymorphic belongs_to associations sometimes fails in Rails 4

Goldiloader fails to load polymorphic belongs_to associations in Rails 4 when the values have a mix of nil and non-nil values.

The corresponding stack trace is:

NoMethodError: undefined method `limit_value' for nil:NilClass
/Users/jturkel/.rvm/gems/ruby-2.0.0-p481@rails-4-upgrade/gems/goldiloader-0.0.7/lib/goldiloader/association_info.rb:30:in `limit?'
/Users/jturkel/.rvm/gems/ruby-2.0.0-p481@rails-4-upgrade/gems/goldiloader-0.0.7/lib/goldiloader/active_record_patches.rb:62:in `eager_loadable?'
/Users/jturkel/.rvm/gems/ruby-2.0.0-p481@rails-4-upgrade/gems/goldiloader-0.0.7/lib/goldiloader/active_record_patches.rb:51:in `auto_include?'
/Users/jturkel/.rvm/gems/ruby-2.0.0-p481@rails-4-upgrade/gems/goldiloader-0.0.7/lib/goldiloader/association_loader.rb:59:in `load?'
/Users/jturkel/.rvm/gems/ruby-2.0.0-p481@rails-4-upgrade/gems/goldiloader-0.0.7/lib/goldiloader/association_loader.rb:9:in `block in load'
/Users/jturkel/.rvm/gems/ruby-2.0.0-p481@rails-4-upgrade/gems/goldiloader-0.0.7/lib/goldiloader/association_loader.rb:8:in `select'
/Users/jturkel/.rvm/gems/ruby-2.0.0-p481@rails-4-upgrade/gems/goldiloader-0.0.7/lib/goldiloader/association_loader.rb:8:in `load'
/Users/jturkel/.rvm/gems/ruby-2.0.0-p481@rails-4-upgrade/gems/goldiloader-0.0.7/lib/goldiloader/active_record_patches.rb:78:in `load_with_auto_include'
/Users/jturkel/.rvm/gems/ruby-2.0.0-p481@rails-4-upgrade/gems/goldiloader-0.0.7/lib/goldiloader/active_record_patches.rb:92:in `find_target_with_auto_include'
/Users/jturkel/.rvm/gems/ruby-2.0.0-p481@rails-4-upgrade/gems/activerecord-4.0.10/lib/active_record/associations/association.rb:147:in `load_target'

Not loading has_one resources

I tried with a discussion class which:

    has_one :content, through: :assignment
    has_one :classroom, through: :assignment
    belongs_to :topic

And I got several queries for the first two. Topic is fine.

Add configuration for auto_include(false) by default

Is there the ability to set goldiloader to set the whole application to auto_include(false) by default? I only want to use goldiloader for certain controller actions. I think I need something like:

Goldiloader.config do |config|
 config.auto_include = false
end

Then when I want to use it I can do the following

@posts = Post.auto_include # preferable
# or
@posts = Post.auto_include(true)

Auto eager loading broken for associations that join a column used in an order

Consider the following models:

class Blog < ActiveRecord::Base
  has_many :posts, -> { joins(:author).order('authors.name') }
end

class Post < ActiveRecord::Base
  belongs_to :blog
  belongs_to :author
end

class Author < ActiveRecord::Base
  has_many :posts
end

Auto eager loading (and regular eager loading) is broken for the Blog post association:

ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: authors.name: SELECT "posts".* FROM "posts" WHERE "posts"."blog_id" IN (1)  ORDER BY authors.name

Exception when association method overridden

If an association method is overridden to return non-model objects then Goldiloader blows up:

class Blog < ActiveRecord::Base
  has_many :posts

  def posts
    'boom'
  end
end

Leads to the following exception:

NoMethodError: undefined method `auto_include_context=' for "boom":String
    /Users/jturkel/dev/goldiloader/lib/goldiloader/auto_include_context.rb:11:in `block in register_models'
    /Users/jturkel/dev/goldiloader/lib/goldiloader/auto_include_context.rb:10:in `each'
    /Users/jturkel/dev/goldiloader/lib/goldiloader/auto_include_context.rb:10:in `register_models'
    /Users/jturkel/dev/goldiloader/lib/goldiloader/association_loader.rb:23:in `load'
    /Users/jturkel/dev/goldiloader/lib/goldiloader/active_record_patches.rb:73:in `load_with_auto_include'
    /Users/jturkel/dev/goldiloader/lib/goldiloader/active_record_patches.rb:88:in `find_target_with_auto_include'

Don't auto eager load associations with instance dependent scopes

Rails 4 and above support instance dependent association scopes which cannot be eager loaded e.g.

class Blog < ActiveRecord::Base
  has_many :posts, ->(instance) { Post.where(blog_id: instance.id) }, class_name: 'Post'
end

Goldiloader should disable auto eager loading for these associations.

Noticed this because Rails 4.2 added a deprecation warning when attempting to eager load these associations.

Issue with Rails 6.0.0.rc1

I have received many duplicated errors associated with Goldiloader after Rails update to 6.0.0.rc1 version.

NoMethodError:
       undefined method `set_value' for #<ActiveRecord::AssociationRelation []>
       Did you mean?  offset_value
     # .../ruby-2.6.2/gems/goldiloader-3.1.0/lib/goldiloader/active_record_patches.rb:63:in `auto_include_value='
     # .../ruby-2.6.2/gems/goldiloader-3.1.0/lib/goldiloader/active_record_patches.rb:73:in `merge_single_values'

Can you please help me to fix them?

Identifier is too long

Is it possible for me to shorten the length of the 'as' identifier to under 30 char, due to the fact of oracle having identifiers be under 30 char..

OCIError: ORA-00972: identifier is too long: SELECT "CR_AREAS".*, "T0"."CONTRACT_ID" AS ar_association_key_name_contract_id FROM "CR_AREAS" INNER JOIN "CR_AREAS_CONTRACTS" "T0" ON "CR_AREAS"."ID" = "T0"."AREA_ID" WHERE "T0"."CONTRACT_ID" IN (10437, 10438, 10428, 10429, 10543, 10552, 10553, 10555, 11532, 11495, 11419, 11601, 11602, 11615, 11617, 11619, 11432, 11518, 11852, 11855, 11877, 11878, 11879, 11880, 11901, 11908, 12396, 11951, 11909, 11778, 11819, 11438, 11443, 12266, 12267, 12268, 12269, 12273, 12289, 12291, 12292, 12299, 12300, 12306, 12307, 11986, 11823, 11822, 11824, 11825, 11829, 11830, 12108, 12619, 12839, 12842, 12871, 12872, 12873, 12874, 12880, 12898, 12899, 12760, 12796, 12544, 12517, 12537, 12565, 12454, 12655, 12721, 12768, 10006, 10010, 10011, 13326, 13346, 13351, 13352, 13366, 13171, 13172, 13193, 13194, 13195, 13196, 13197, 13198, 13199, 13200, 13201, 1, 13248, 13267, 13446, 12953, 12971, 13388, 13389, 13390, 13405, 12911, 13408, 13409, 13154, 13489, 13490, 13491, 13492, 13484, 13485, 13486, 13487, 13100, 12955, 12957, 12960, 13107, 13108, 13109, 13110, 13111, 13112, 13113, 13115, 13041, 13266, 13325)

"NameError uninitialized constant Goldiloader" on uninstallation

Hi,

First of all, thank you for this great gem.

Some of our endpoints are being cached (using Rails.cache) and they failed with Dalli::RingError: No server available upon uninstalling goldiloader.

Checking the log, I saw this:

NameError uninitialized constant Goldiloader

Clearing up the cache brings back the server but I'm curious as to how I can prevent this in the future?

Rails 5.2 with bundle install

In the gemfile, it states that it requires rails version < 5.2 https://github.com/salsify/goldiloader/blob/master/goldiloader.gemspec#L26

Therefore, it would fail the rails v5.2 when doing bundle install, having the following error:

Fetching gem metadata from https://rubygems.org/......
Fetching source index from https://rubygems.org/
Resolving dependencies............................................
Bundler could not find compatible versions for gem "activerecord":
  In Gemfile:
    annotate was resolved to 2.7.2, which depends on
      activerecord (< 6.0, >= 3.2)

    goldiloader was resolved to 0.0.4, which depends on
      activerecord (<= 4.1, >= 3.2)

    paper_trail was resolved to 9.0.0, which depends on
      activerecord (< 5.3, >= 4.2)

    paranoia was resolved to 1.2.0, which depends on
      activerecord (>= 3.0.0)

    rails was resolved to 5.2.0, which depends on
      activerecord (= 5.2.0)

strangely, this error only happens since today.

What's the reason for this gem to require rails version < 5.2 ?

Thanks ๐Ÿ˜„

Eager load arbitrary nesting associations (with STI)

First of all thanks for a great gem.

I just found it and would like to use it in a project, where we heavily use includes. So, we need the feature to eager load arbitrary nesting of associations. But it looks like it is limited to what it can do for us, because it does not load all nesting associations. I'll try to explain my problem:

Some of our Models:

# STI models
class Person < ApplicationRecord
  has_many :persons_nationalities, as: :person, dependent: :destroy, inverse_of: :person
...
end

class Candidate < Person
...
end

# Relations
class PersonsNationality < ApplicationRecord
  belongs_to :person, polymorphic: true
  belongs_to :nationality, class_name: "Country"
end

class Country < ApplicationRecord
...
  has_many :persons_nationalities, foreign_key: :nationality_id
end

In the candidate views I render:

# app/views/candidates/_candidate.json.jbuilder
...
# ! does not load all nesting associations !
json.persons_nationalities object.persons_nationalities, partial: "persons_nationalities/persons_nationality", as: :object

# app/views/persons_nationalities/_persons_nationality.json.jbuilder
json.(object.nationality, :id, :adjective, :name) # <= Country

End in controller I do load:

@candidate = Candidate.find(params[:id])

When I load with @candidate = Candidate.includes(persons_nationalities: [:nationality]).find(params[:id]) it is working, but then I do not use this gem, right?

Does anyone have a suggestion or solution?! Thank you in advance.

Our Rails environment:

Docker ruby:2.7.3-slim-buster
rails 6.0.3.4
ruby 2.7.3p183

# Gemfile
gem "puma", "~> 5.0"
gem "pg", "~> 1.2"
gem "goldiloader", "~> 4.1"

Auto eager loading broken for has_many through associations where the model is an STI subtype

Example models:

class Group < ActiveRecord::Base
  has_many :users
  has_many :articles, through: :users
end

class User < ActiveRecord::Base
  belongs_to :group
  has_many :articles
end

class Post < ActiveRecord::Base
  belongs_to :user
end

class Article < Post
end

Can't auto eager load (or regular eager load) Group#articles.

Underlying issue is rails/rails#11078. Workaround is to set auto_include = false on these associations

Auto eager loading broken for has many through queries that specify select or order

I'm not sure if this is the same bug as #12, but a has many through association that specifies select or order referring to the source table crashes and needs the workaround/fix as well.

For example:

has_many :template_questions, dependent: :delete_all
has_many :questions, through: :template_questions, source: :question,
    select: "questions.*, template_questions.sequence AS sequence"

#=> Mysql2::Error: Unknown column 'template_questions.sequence' in 'field list'

It eager-loaded the AR::Relation when calling `==` method

I recently had trouble with unexpected eager-loading. I have something like this:

class Post < ActiveRecord::Base
  belongs_to :user
  scope :rank, -> {
    # do some ranking..
  }
end

class User < ActiveRecord::Base
  has_many :posts, dependent: :destroy
end
def filter_scope(scope)
  scope = scope.rank if scope == Post || (scope.is_a?(ActiveRecord::Relation) && scope.klass == Post)
  # ...
  scope
end

filter_scope(user.posts)

The expected result of filter_scope is an ActiveRecord::Relation object and it should not execute any queries. However, it triggered the query at the line scope = scope.rank if scope == Post:

SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (?)

If I change the variables position, the problem goes away:

scope = scope.rank if Post == scope #...
# Or also work with this:
scope = scope.rank if scope.equal?(Post) # ...

And if I disable Goldiloader, the problem also does not happen.
FYI, I'm using Rails 4.2.4.

Auto eager loaded models not marked as read only

The readonly option on associations doesn't work with Goldiloader:

class Blog < ActiveRecord::Base
  has_many :posts, readonly: true
end

blog.posts.first.readonly? returns false. Note Rails 4 syntax for this option is different.

This appears to be a bug in Rails since it reproduces without Goldiloader using regular eager loading (at least in Rails 3.2.18). I'll also file a bug with Rails about this.

Getting nomethoderror 'each' on NilClass after adding this gem

This gem looks like it would really simplify my performance hunt. But after adding it I am getting in a number of places NoMethodError 'each' on NilClass. Here is one code #sample:

  def compatible_persons(tenant = nil)
    # all active persons with same email address
    candidates = persons
    # if tenant provided - subset to just those in that tenant
    candidates = candidates.select { |person| person.tenant_id.equal?(tenant.id) } if tenant
    # if SP person and no persons with SP person email on list, then use SP persons master
    candidates = [master] if !candidates.any? && schellingpoint_person?
    candidates
  end

I get errors on the .select line, and was getting errors on the candidates.any? line which was candidates.blank? before I changed it to !candidates.any?. I tried candidates.none? and that still elicited the error.

I should add am running Rails 4.2.4 on Ruby 2.2.4.

Without the gem installed this worked fine, and after removing it, it works as expected. I had to remove the gem, but would be willing to help if I got some guidance on what to look for.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.