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

Fix Active Filter Labeling and Record Not Found #5223

Merged

Conversation

wspurgin
Copy link
Contributor

@wspurgin wspurgin commented Oct 24, 2017

👋

ActiveFilter was added to make the current filter sidebar a bit more humanized. It adds some cool things like smart, humanized filter names and, where possible, link to related resources with human readable names! Very cool. However, there's a few issues I saw when messing around a bit.

  1. If a filter with a related class (as ActiveFilter defines it) is given conditions that have no matching records in the related class, ActiveFilter produces an ActiveRecord::RecordNotFound when trying to generate a set of values to display in the "Current Filters" sidebar.
  2. If a filter is added to the resource that has a label specified, ActiveFilter ignores it in favor of pulling from i18n. This doesn't seem too terrible but can pose problems to the developer (see below)

The first issue I believe to be a regression. Even though, truthfully, there is no related record matching the filter criteria (and thus no resource records either), AA would display a blank slate in all other cases where a filter(s) produces no matching records.

The second issue, is more subtle. Most cases of custom labels are covered by the current implementation of ActiveFilter. After all, if you want the label of Post#title to be something other than "Title" just put it inside of en.yml, and ActiveFilter will do as you command. Here's the rub, imagine a more complex translation situation:

# en.yml
en:
   activerecord:
      attributes:
         user:
            external_id: "%{client_name} External ID" 
#...
ActiveAdmin.register User do
#...
filter :external_id, label: config.resource_class.human_attribute_name(:external_id, client_name: ENV["CLIENT"])
end

In this example, we have an interpolation within our translation because the translation changes based on who's bought our super shiny app. In this situation, ActiveFilter would produce a I18n::MissingInterpolationArgument when trying to render the Current Filter sidebar with this attribute.

While this example is a bit contrived, IMHO it's better to go with whatever the developer specifies as the attribute name. If the label on the filter is set, then that's takes precedence. If not, then the translations are certainly the next best thing.

Related issues:

@@ -19,7 +19,7 @@ def initialize(resource, condition)
def values
condition_values = condition.values.map(&:value)
if related_class
related_class.find(condition_values)
related_class.where(id: condition_values)
Copy link
Contributor

@zorab47 zorab47 Oct 25, 2017

Choose a reason for hiding this comment

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

Using id here isn't ideal because the primary key column may be some other field on the record.

Other locations dynamically lookup the primary key:

[:find_by, { resource_class.primary_key => id }]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup. Great catch. 🙈

zorab47
zorab47 previously approved these changes Nov 2, 2017
@wspurgin
Copy link
Contributor Author

wspurgin commented Nov 3, 2017

@varyonic @zorab47 The coverage failure for the last commit looks like a false positive to me. But if the ✅ is desired, I can rejig that line so it is "covered" by codecov pov. Just let me know.

@varyonic
Copy link
Contributor

varyonic commented Nov 3, 2017

Right, CodeCov doesn't get it when you split a method chain across more than two lines.

@wspurgin wspurgin force-pushed the fix-active-filter-labels-and-404ing branch from c17be84 to 76c5e68 Compare November 3, 2017 18:45
@wspurgin
Copy link
Contributor Author

wspurgin commented Nov 3, 2017

I amended the commit to put that method chain on one line then. 👍

@wspurgin
Copy link
Contributor Author

wspurgin commented Nov 6, 2017

@zorab47 I think you'll need to re-approve these changes (since I added the association primary key support). Also let me know if a rebase is necessary.

zorab47
zorab47 previously approved these changes Nov 7, 2017
Copy link
Contributor

@zorab47 zorab47 left a comment

Choose a reason for hiding this comment

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

Good catch on primary key

reject { |r| r.options[:polymorphic] }. #skip polymorphic
detect { |r| r.foreign_key.to_s == name.to_s }
assoc.class_name.constantize if assoc
predicate_association.class_name.constantize if predicate_association
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this become an elsif for easier reading?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It was like that previously, so I didn't know if it was convention to avoid if - elsif statements with no else.

if predicate_association
predicate_association.active_record_primary_key
else
related_class.primary_key if related_class
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this become an elsif for easier reading?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Like line 93, since a there were other places where it seemed that dangling if - elsif weren't preferred, I stuck with the if - else. But IMHO I agree that it should just be an if - elsif block

end
end

def predicate_association
Copy link
Member

Choose a reason for hiding this comment

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

@wspurgin maybe you can add cashing this method using instance_variable_defined?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would you prefer instance_variable_defined? or just:

def predicate_association
  @predicate_association ||= find_predicate_association
end

def find_predicate_association
  #...
end

@wspurgin
Copy link
Contributor Author

wspurgin commented Nov 7, 2017

@zorab47 @Fivell I updated the PR with clearer if-elsif blocks, and caching of the predicate association. Appreciate the feedback 👍


filter[:label]
end

Copy link
Contributor

Choose a reason for hiding this comment

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

def filter_label
  filter && filter[:label]
end

if predicate_association
predicate_association.active_record_primary_key
elsif related_class
related_class.primary_key
Copy link
Contributor

Choose a reason for hiding this comment

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

Personally I like terse boolean operators over if/else:

      def related_primary_key
        predicate_association && predicate_association.active_record_primary_key ||
          related_class && related_class.primary_key
      end

Copy link
Contributor Author

@wspurgin wspurgin Nov 8, 2017

Choose a reason for hiding this comment

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

Hurts readability though. You'll still need a block to return the proper primary key (as that only returns a true or false. Scratch that. It returns the proper value, but I still think it hurts readability.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's a personal preference, maybe from writing too much C or perl. :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Definitely understand. I think I leave it as is for now though. It'll help me understand what's going on more quickly if I look at in a month than chained binary operators. 🙈

Copy link
Contributor

@varyonic varyonic left a comment

Choose a reason for hiding this comment

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

Looks reasonable, though I've not worked on this before. Yes, a rebase and squash corrections would be good.

end
end

def predicate_association
@predicate_association ||= find_predicate_association
Copy link
Member

Choose a reason for hiding this comment

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

@wspurgin since find_predicate_association can return nil,

method can look like

return @predicate_association if defined? @predicate_association
@predicate_association ||= find_predicate_association

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Gotcha. That way we don't iterate every time if their is no predicate association. I'll make that update.

Copy link
Contributor

Choose a reason for hiding this comment

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

= is sufficient if you know it's not defined. Alternately:

        @predicate_association = find_predicate_association unless defined?(@predicate_association)
        @predicate_association

@wspurgin wspurgin force-pushed the fix-active-filter-labels-and-404ing branch from 32308be to ab9ca47 Compare November 8, 2017 23:09
@wspurgin
Copy link
Contributor Author

wspurgin commented Nov 8, 2017

@varyonic Rebased.

@wspurgin
Copy link
Contributor Author

wspurgin commented Nov 9, 2017

@varyonic That failure in the CI looks completely unrelated to this PR. Looks broken elsewhere too. I'm guessing that's what #5237 is for?

Will Spurgin added 3 commits November 9, 2017 13:34
Use label on the filter as the prefix (if given)

Also move other prefixes' logic into separate methods for cleanliness.

Support rails 4.2 in specs

Use related class' primary key
Use association primary key when possible

Closes activeadmin#5164

Use elsif to enhance readability

Cache predicate_association

Only search for predicate once
@wspurgin wspurgin force-pushed the fix-active-filter-labels-and-404ing branch from ab9ca47 to 3b154b1 Compare November 9, 2017 19:43
@wspurgin
Copy link
Contributor Author

wspurgin commented Nov 9, 2017

@varyonic rebased and squashed. Thanks for the fixing the builds! 💯

@varyonic varyonic merged commit dc7aeb0 into activeadmin:master Nov 10, 2017
zorab47 added a commit to zorab47/active_admin that referenced this pull request Dec 4, 2017
@zorab47 zorab47 mentioned this pull request Dec 4, 2017
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