Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kredis methods are undefined in production for non-ActiveRecord models #60

Closed
EmCousin opened this issue Dec 27, 2021 · 7 comments
Closed

Comments

@EmCousin
Copy link

EmCousin commented Dec 27, 2021

Hi,

First, thank you for this great gem.

I'm encountering an error when trying to use Kredis methods like kredis_string, kredis_json, etc. in a class that inherits from a base class that includes ActiveModel::Model. This issue does not occur in development, only in production, preventing the app to boot.

The error :

undefined method `kredis_json' for Foo::Bar:Class (NoMethodError)

How to reproduce the error:

# Gemfile.rb
gem 'kredis' ## => bundle install

# app/models/foo/base.rb
module Foo
  class Base
    include ActiveModel::Model
  end
end

# app/models/foo/bar.rb
module Foo
  class Bar < Base
    kredis_json :metadata # causes the app to 💥 when booting
  end
end

I wonder it this might have something to do with the way Kredis::Attributes gets included in ActiveModel::Model here. Could it be due to the fact it is called in config.after_initialize callback instead of a load hook like it's done with ActiveRecord?

Just trying to wrap my head around this. The current workaround I use is to include explicitly Kredis::Attributes:

module Foo
  class Bar < Base
    include Kredis::Attributes

    kredis_json :metadata # it works ✅ 
  end
end

Is it how we should actually use it?

Background

  • Rails 7
  • Kredis 1.0.1
@capripot
Copy link
Contributor

Thanks for reporting!
Would that be more of an issue to report to the Rails repo tho? I think that's were we'd manage which dependencies we put in ActiveModel::Model.

@dhh
Copy link
Member

dhh commented Feb 19, 2022

@fxn Would you know what's up here?

@fxn
Copy link
Member

fxn commented Feb 19, 2022

Yo!

Yes, @EmCousin guess is correct. Eager loading happens in the finisher here, and the after_initialize hook runs later.

This relative order has been the same for a long time.

I'd suspect that if you ran development mode with eager loading enabled, you'll be able to reproduce and test a solution locally.

I believe the idiomatic thing to do would be to define a load hook for Active Model. But that would need a release of Rails. Meanwhile, activemodel is a dependency of activerecord and its loading happens early on. I believe the include in Kredis could happen within the on_load hook of Active Record, as a temporary workaround.

@EmCousin while a solution for Kredis is worked out, in your application you can do the same. Throw this in an initializer or config/application.rb:

# See https://github.com/rails/kredis/issues/60.
ActiveSupport.on_load(:active_record) do
  ActiveModel::Model.include Kredis::Attributes
end

I have not tested this, but module inclusion is idempotent, so this won't interfere with Kredis doing the same later. That way, you can leave your application code clean and just remove the trick when an update upstream makes it no longer necessary.

@EmCousin
Copy link
Author

Thanks @fxn!

  • Activating eager_load does reproduce the issue in development environment
  • Your workaround works. I put it in an initializer named config/initializers/kredis.rb rather than straight to config/application.rb though, since it listens to ActiveSupport.on_load

Looking forward to the update! 🙂

@fxn
Copy link
Member

fxn commented Feb 21, 2022

@EmCousin cool!

I put it in an initializer named config/initializers/kredis.rb rather than straight to config/application.rb though, since it listens to ActiveSupport.on_load.

What do you mean?

@EmCousin
Copy link
Author

I mean that this:

# See https://github.com/rails/kredis/issues/60.
ActiveSupport.on_load(:active_record) do
  ActiveModel::Model.include Kredis::Attributes
end

does not have to be put straight into config/application.rb if you don't want to. You can put this piece of code in an initializer and it will work just as well

@fxn
Copy link
Member

fxn commented Feb 21, 2022

@EmCousin perfect :).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

4 participants