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

Model does not reload #465

Closed
cr0t opened this issue Feb 10, 2013 · 9 comments · Fixed by #477
Closed

Model does not reload #465

cr0t opened this issue Feb 10, 2013 · 9 comments · Fixed by #477
Labels

Comments

@cr0t
Copy link

cr0t commented Feb 10, 2013

Hello.

I found a strange bug with Draper gem and latest Rails (3.2.11) - I didn't check other Rails versions, but they might be buggy, too.

Weird behavior

If I decorate a model once in my code, than after second request model source code does not reload till reloading of full rails application (CTRL+C in case of running rails s).

I am sure that this is Draper gem problem (it can be related to pg), because when I do not use decorate method everything goes fine. I created a test app to check it.

How to reproduce

Please clone this repo https://github.com/cr0t/weird-draper-test

Bundle gems, initialize database, migrate and seed it. Then start the server and open http://localhost:3000.

Open two files in editor:

  • app/models/project.rb
  • app/views/welcome/index.html.erb

Then change calling method test_one to test_two (for example... trick is in change method name, or add a new method) in the view file and update Project model method name too.

Reload page in the browser. Everything is fine.

Do the same things one more time (change method name to new version in both places, test_three, for example).

Reload page. Oh! Crap! NoMethodError!

Screenshot: http://d.pr/i/7VLq

Information about my environment can be found in the README file in test app repository.

@steveklabnik
Copy link
Member

I just want to say thank you for an excellent bug report. I don't have time to check this out immediately, but this is very helpful.

@yfeldblum
Copy link

+1. For the way the bug report was done. I don't know about the bug itself. Cheers!

@cr0t
Copy link
Author

cr0t commented Feb 11, 2013

Hi.

I've checked this issue with mysql. You can check it from mysql-check branch of my test repo.

Issue is not just draper+pg related, with mysql I have the same results.

@sidonath
Copy link

I believe we're experiencing the same bug. Here are few side-effects (for those who google):

  • testing the type of ActiveRecord model doesn't work after a partial reload (eg. User === User.new will return false)
  • declarative_authorization rejects users even though correct permissions are set after a partial reload

@haines
Copy link
Contributor

haines commented Feb 16, 2013

@sidonath User.new === User should always return false, did you mean User === User.new (in which case, I can't reproduce).

@sidonath
Copy link

@haines yes indeed, I meant User === User.new. However, what I actually wanted to say is that in our case User === current_user fails, which is — I only now realize — significantly different. It implies that current_user is initialized from a different version of the User class.

Sorry about the confusion.

@haines
Copy link
Contributor

haines commented Feb 16, 2013

I've been looking into this, thanks heaps for an awesome bug report @cr0t!!

It doesn't seem to be ActiveRecord-related, so it's not dependent on the database: my fork demonstrates this. It seems to be purely related to Rails' code reloading.

Basically, it seems to comes down to whether the model is referenced before the decorator:

  • Referencing the model first causes the issue. The first two requests reload the model and then the decorator, but from the third request onwards only the decorator is reloaded.
  • On the other hand, if the decorator is referenced first, the model is always reloaded and so the issue does not occur.

Why the model is not reloaded in the former situation is beyond me, but I'll keep digging!

@haines
Copy link
Contributor

haines commented Feb 17, 2013

Here's the problem (line 005):

Loading development environment (Rails 3.2.11)
irb(main):001:0> ActiveSupport::Dependencies.clear
=> []
irb(main):002:0> Project; ProjectDecorator
Loading model
Loading decorator
=> ProjectDecorator
irb(main):003:0> ActiveSupport::Dependencies.autoloaded_constants
=> ["Project", "ProjectDecorator"]
irb(main):004:0> ActiveSupport::Dependencies.loaded
=> #<Set: {".../app/models/project", ".../app/decorators/project_decorator"}>
irb(main):005:0> ActiveSupport::Dependencies.clear
Loading model  <==== WTF!
=> []
irb(main):006:0> ActiveSupport::Dependencies.autoloaded_constants
=> []
irb(main):007:0> ActiveSupport::Dependencies.loaded
=> #<Set: {".../app/models/project"}>

If you reference the model before the decorator, it removes it before the decorator. Then when it removes the decorator, it reloads the model for some reason. Because it's no longer in autoloaded_constants it is never reloaded again.

If you reference the decorator first, it removes it before the model, and the model is then unloaded successfully.

Haven't worked out why it does this yet, but getting closer...

@haines
Copy link
Contributor

haines commented Feb 17, 2013

Aha, found it... Rails checks if the class being unloaded responds to before_remove_const (here). The decorator class doesn't respond to this message itself, so it checks whether it can be proxied to the model class, which causes the model class to be reloaded 💥

Should be an easy fix!

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

Successfully merging a pull request may close this issue.

5 participants