-
Notifications
You must be signed in to change notification settings - Fork 21.4k
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
Ensure HABTM relationships produce valid class names (Fixes #17119) #17217
Conversation
I guess I need to figure out a fix that runs on Ruby 1.9.3 and 2.0. |
Adjustment made to pass on 2.1, 2.0, and 1.9.3 in my environment. Now let's see how Travis does. 😄 |
# we've been able to reproduce one of the 2 parts of this bug | ||
refute_nil File.read(File.expand_path("../../../fixtures/developers.yml", __FILE__)).index("shared_computers") | ||
Developer._reflections.values.each do |association| | ||
constant = ObjectSpace.each_object(Class).detect{|c| c.to_s == association.class_name } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is ObjectSpace available in jruby ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to be available, but turned off by default.
Since this is only in a test, do you think it is unreasonable to require JRuby users to turn ObjectSpace
on for the purposes of running the tests? (i.e., it will not affect anyone using Rails, only those running the tests)
Unfortunately, const_get
and const_defined?
behave differently between 1.9.3, 2.0, and 2.1, so we cannot use those (if they are even available by default in JRuby -- which I'm not sure of, since they might rely on ObjectSpace
for all I know).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can't have failing jruby test in travis. We either have to turn on ObjectSpace
or skip this test in jruby.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, ObjectSpace
is used elsewhere in Rails as of the time of this comment. Not a lot, but it is there. Does Rails already require JRuby to have it turned on? Or is the project trying to move away from it?
Alternatively, do you know a way that would work in both? All else being equal, I'd rather have 100% JRuby support without requiring switching from defaults if I can.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, thanks. I'll test it locally on JRuby and see what I can come up with if it fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Look good to me, the failure in jruby are unrelated to your change i think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I cannot get the tests to run in JRuby. For me, it says --dev
is not a valid option. On travis https://travis-ci.org/rails/rails/jobs/37502973#L223 it is not even loading to run the tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sure we can think of a way to test this without ObjectSpace. :-/ walking all classes in ObjectSpace seems a bit extreme.
Aaron Patterson
http://tenderlovemaking.com/
I'm on an iPhone so I apologize for top posting.
On Oct 9, 2014, at 9:31 AM, Abdelkader Boudih notifications@github.com wrote:
In activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:
@@ -883,4 +887,15 @@ def test_redefine_habtm
child.special_projects << SpecialProject.new("name" => "Special Project")
assert child.save, 'child object should be saved'
end
+
- def test_reflections_do_not_have_nonexistent_constants_as_class_names
- refute_nil Developer._reflections['shared_computers']
Checking the fixture for named association is important here, because it's the only way
we've been able to reproduce one of the 2 parts of this bug
- refute_nil File.read(File.expand_path("../../../fixtures/developers.yml", FILE)).index("shared_computers")
- Developer._reflections.values.each do |association|
we can't have failing jruby test in travis. We either have to turn on ObjectSpace or skip this test in jruby.constant = ObjectSpace.each_object(Class).detect{|c| c.to_s == association.class_name }
cc @senny @jeremy @tenderlove @rafaelfranca
�
Reply to this email directly or view it on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tenderlove Because HABTM_*
classes are built under the ActiveRecord model's namespace, there are multiple levels we need to check for constants.
In Ruby 1.9.3, const_get
does not look recursively if we pass in Developer::HABTM_Projects
for instance, while 2.0 mentions "this method will recursively look up constant names if a namespaced class name is provided.
Although I couldn't find it documented, I noticed the same behavior when using const_defined?
(which was the reason the original pull request failed on 2.0 and 1.9.3).
We could create our own recursive lookup, but that seems worse to me.
I suppose we could just try accessing the constant under assert_nothing_raised
... I'll try that.
|
I just rebased against master since this has sit for so long (2 more betas have come out!) This should be a show-stopper bug: It breaks documented functionality in at least the case where every test in your Rails app will error out if you use a relationship with a non-standard name, and who knows what else it affects. @tenderlove you had an objection before to using I don't like I'm open to doing that if you prefer that. Let me know. 😄 |
# we've been able to reproduce one of the 2 parts of this bug | ||
refute_nil File.read(File.expand_path("../../../fixtures/developers.yml", __FILE__)).index("shared_computers") | ||
Developer._reflections.values.each do |association| | ||
assert_nothing_raised{ association.class_name.constantize } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no need to call assert_nothing_raised
; actually it will only yield the content of the given block so we try to avoid to use it. You can simply write association.class_name.constantize
inside your #each
block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback. I'll remove it, but I preferred it because it makes the intent more clear. I would hate to see the test removed in the future because it doesn't appear to be testing anything. Perhaps the comments are enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this is safe ; the test's name is pretty clear and there's always git blame
. :-)
@codeodor: Could you please add a changelog entry ? Thanks a lot for your patch so far ! :-) |
@robin850 I'll make the suggested changes later today. However since this is simply a bug fix to a beta release, is the changelog entry desired? Normally I would add it, but I thought for something like this it's not wanted. 😄 |
Yep you're right, sorry, nevermind ! |
f284340
to
08b830f
Compare
Ok, |
Is it possible to demonstrate this bug without specifically constantizing the class name? |
Why? Just do I still have the earlier question though, is it possible to demo the bug without specifically constantizing that value. This test is testing internals. |
Then it would certainly be testing internals. For example, we know one of the classes we want to test is called In any case, suppose the naming convention later changes, or another kind of abstraction is produced as an intermediate layer: If we iterate over the classes produced by AR, then we cover those cases without tying the test to the internals (other than iterating over the reflections). If we instead hard code the class names in the test, we lose that ability.
I've only thought of 2 ways to do ensure a string is a valid constant:
The original version of this test did look at I am totally open to having a better test. I'm not happy with this one. But I was unable to figure out a better way to produce the errors in a test. If you have any more ideas, I would love to improve it. I just feel like hard coding the class names in the test is worse than iterating through My biggest concern is a release of 4.2 without this fix, because I and anyone else who uses a non-standard name as a relationship will be unable to run their tests. For example, with the following line and
every test in the app that loads the fixture will error out. That is the most immediate problem I encountered in the wild. |
I suppose we could drop the class name part of the test altogether and just ensure the fixture has the expected data that will cause an error without the fix applied, since that's the only place in the wild where I've seen the error. I only discovered the class name issue as a by-product of that, since it was ignoring the So if you'd like me to just remove that part of the test in an effort to make sure this fix gets into 4.2, I'd be happy to do so. |
Yes, this is exactly what I want. The generated class names, and even constantizing them are internals and I want to be free to change that. In fact, my original implementation created no constants at all for the middle models. Eventually I would like to return to that, but testing directly against the internal constant doesn't really prevent the regression you were seeing. Testing the constants directly forces us to be bound to this implementation. |
Ok, done. Thanks @tenderlove for helping me see the light you were sharing! 😄 |
Ensure HABTM relationships produce valid class names (Fixes #17119)
Sorry for all the back and forth. Thanks for the hard work on this!!! |
@tenderlove @codeodor for some reason, this commit broke the build after being merged into master. Would one of you be able to take a look? If not, I'll try to figure it out later this week |
Weird. Travis gave it a pass. I can take a look later. |
Weird that it didn't catch it before, since the commit that seems to cause the issue was introduced in 2009 so it would have run against it. 😄 (See https://github.com/rails/rails/blame/master/activerecord/test/cases/validations/uniqueness_validation_test.rb#L44) It seems to only fail when running that test file in isolation, so maybe that's why I didn't catch it. In any case, if we remove the developers fixture from that test (it is not used), the problem goes away. I will issue a new pull request to fix it. |
See #17578 for the fix. |
@tenderlove It was my pleasure. Thanks for having patience with me. 😁 |
Ah, now I know what's going on. @matthewd disabled running the isolated tests on PRs on travis, that's why it didn't caught it :) |
Fix bug found when running individual tests against #17217 after merging
`Computer` class needs to be require See rails#17217 for more details
`Computer` class needs to be require See rails#17217 for more details
See #17119 for the back story.