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

Don't generate dashboards for non-constant models #259

Merged
merged 1 commit into from
Nov 21, 2015

Conversation

c-lliope
Copy link
Contributor

Fixes #145 and #219

Problem:

When the same has_and_belongs_to_many relationship is defined twice,
Rails generates an unnamed subclass of ActiveRecord::Base.
This is probably a bug in Rails.

The unnamed subclass shows up like this:

> ActiveRecord::Base.descendants.last
 #<Class:0x007fddea1bdf08> (call '#<Class:0x007fddea1bdf08>.connection' to establish a connection)

When the install generator tries to populate the DashboardManifest
with all of the ActiveRecord models,
it trips up on unnamed models,
and generates a manifest with invalid syntax.

This results in a NameError for the user when they run the
administrate:install generator:

/gems/administrate-0.1.0/lib/generators/administrate/dashboard/dashboard_generator.rb:87:
in `const_get': wrong constant name #<class:0x007fcf52bfe248> (NameError)

This error most often pops up in applications
that are using the spring gem.

Solution:

  • Add a test case that reproduces the error
    by defining duplicate has_many associations.
  • In the install generator, ignore any models that are not constants.
    We determine constants by checking that the model's #name
    matches its #to_s.
  • Print a warning for the ignored models.

@c-lliope c-lliope added this to the v0.1.2 milestone Nov 20, 2015
This was referenced Nov 20, 2015

unnamed_constants.each do |invalid_model|
puts "WARNING: Unable to generate a dashboard for #{invalid_model}."
puts " It is not a valid Ruby constant."

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there something that could be linked to the user to help them debug/fix this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jakecraige I... don't know.

I'm about 85% sure this is a bug in Rails, and I'm going to open a PR there to try to fix it. On the user's end, they're probably not aware they even have a non-constant class in their application, so it'll be strange for them to see a warning.

I'm considering removing this warning altogether, or changing it to something less scary-sounding, like:

NOTICE: Skipping dynamically generated model `#<Class:asdfasdf>`.

Thoughts?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spent a bit thinking about this. Does this even need to be a warning? I feel like we can pretty certainly say that if a class isn't assigned to a constant, you can ignore it. (Also you should look at the implementation of descendants if you haven't in the past, because it's hilarious)

@c-lliope
Copy link
Contributor Author

Ping @sgrif - figured out the issue where ActiveRecord is creating unnamed classes. Looks like it's a bug in ActiveRecord - is that right?

I have the bandwidth to open up a PR for it, if you'd like.

@c-lliope
Copy link
Contributor Author

Talked to @sgrif - since calling has_and_belongs_to_many multiple times for the same relationship isn't a supported use case in Rails, the bug actually lives in Spring. For some reason, it's reloading the relationship when it shouldn't be.

@sgrif
Copy link

sgrif commented Nov 20, 2015

For some reason, it's reloading the relationship when it shouldn't be.

More likely it's retaining the constant that should be getting released as part of the general constant reloading process.

@c-lliope
Copy link
Contributor Author

yep, good catch. Thanks!

@Rodrigora
Copy link
Contributor

Cool @Graysonwright. 💯

Just letting you know that I do not have duplicated HABTM relationships. These are my models:

class Feature < ActiveRecord::Base
  has_and_belongs_to_many :units
end

class Unit < ActiveRecord::Base
  has_and_belongs_to_many :features
end

@c-lliope
Copy link
Contributor Author

@Rodrigora yep, I think there may be a bug in spring that is causing the equivalent effect of a duplicated HABTM relationship.

Are you using spring in your app?

@Rodrigora
Copy link
Contributor

Yes @Graysonwright, I'm using it.

You're right, certainly it is a Spring bug. Thanks!

Fixes #145 and #219

Problem:

When the install generator tries to populate the `DashboardManifest`
with all of the `ActiveRecord` models,
it trips up on unnamed models,
and generates a manifest with invalid syntax.

Offending unnamed subclasses show up like this:

```ruby
> ActiveRecord::Base.descendants.last
 #<Class:0x007fddea1bdf08> (call '#<Class:0x007fddea1bdf08>.connection' to establish a connection)
```

When users have one of these unnamed subclasses in their application,
they receive a `NameError`
when they run the `administrate:install` generator:

```
/gems/administrate-0.1.0/lib/generators/administrate/dashboard/dashboard_generator.rb:87:
in `const_get': wrong constant name #<class:0x007fcf52bfe248> (NameError)
```

Explanation:

This bug has shown up in a few users' applications
when they are using the `spring` gem
and have `has_and_belongs_to_many` relationships defined.
The problem is two-fold:

1. There's a quirk in Rails,
  where if you define the same `has_and_belongs_to_many` relationship
  multiple times,
  Rails generates an unnamed subclass of `ActiveRecord::Base`.
2. There's a bug in Spring,
  where the generated classes for `HABTM` relationships
  don't get cleaned up correctly.
  When Spring reloads the HABTM relationship,
  it has the same effect as defining the relationship a second time.

Solution:

- In the install generator, ignore any models that are not constants.
  We determine constants by checking that the model's `#name`
  matches its `#to_s`.
- Print a warning for the ignored models.
@c-lliope c-lliope force-pushed the gw-ignore-unnamed-classes branch from 8422e3e to a14e388 Compare November 21, 2015 01:29
@c-lliope c-lliope merged commit a14e388 into master Nov 21, 2015
@c-lliope c-lliope deleted the gw-ignore-unnamed-classes branch November 21, 2015 01:29
c-lliope added a commit that referenced this pull request Dec 9, 2015
Changes:

```
* [#251] [FEATURE] Raise a helpful error when an attribute is missing from
  `ATTRIBUTE_TYPES`
* [#298] [FEATURE] Support ActiveRecord model I18n translations
* [#312] [FEATURE] Add a `nil` option to `belongs_to` form fields
* [#231] [UI] Fix layout issue on show page where a long label next to an empty
  value would cause following fields on the page to be mis-aligned.
* [#309] [UI] Fix layout issue in datetime pickers where months and years
  would not wrap correctly.
* [#306] [UI] Wrap long text lines (on word breaks) on show pages
* [#214] [UI] Improve header layout when there is a long page title
* [#198] [UI] Improve spacing around bottom link in sidebar
* [#206] [UI] Left-align checkboxes in boolean form fields
* [#315] [UI] Remove the `IDS` suffix for `HasMany` form field labels
* [#259] [BUGFIX] Make installation generator more robust
  by ignoring dynamically generated, unnamed models
* [#243] [BUGFIX] Fix up a "Show" button on the edit page that was not using the
  `display_resource` method.
* [#248] [BUGFIX] Improve polymorphic relationship's dashboard class detection.
* [#247] [BUGFIX] Populate `has_many` and `belongs_to` select boxes
  with the current value of the relationship.
* [#217] [I18n] Dutch
* [#263] [I18n] Swedish
* [#272] [I18n] Danish
* [#270] [I18n] Don't apologize about missing relationship support.
* [#237] [I18n] Fix broken paths for several I18n files (de, es, fr, pt-BR, vi).
* [#266] [OPTIM] Save a few database queries by using cached counts
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants