Closed Linuus closed 5 years ago
Some thoughts:
Clarify in the first example what is meant by my_identifier
.
Some nitpicks regarding the filter class syntax. Reason for naming the variable scope
? Currently it's resources
I believe. I don't feel strongly about it, just curious. Also, order of the arguments, currently it's (resources, value)
. Reason for changing it? Again, just curious.
Regarding filters to ship. How about String
, Select
and Multiselect
? That maps to what we have now. Or is there something different about belongs to and has many relationships? Also not sure Boolean
is useful if we have Select
, especially if you still need to pass a collection as in your example.
I think option 2 would be nice. Would be cool if we could get rid of our own partial override thing altogether. I don't think I have ever needed to override a filter anyway, seems like a very rare use case.
I like the wildcard: :post
thing! I assume there would be wildcard: :pre
and wildcard: :both
as well.
Actually, I can't really come up with a good use case for the identifier to be something other than the column... We talked about this yesterday though :)
One case is if you want to filter two columns and create a custom filter.
Filter::MyFilter.new(:title_or_name, column: ....)
But if you do that you will override the call
method anyway and don't have to care about the column value at all. Perhaps it's not needed?
No reason. I just named them something :) I will change it.
Sure, maybe that's enough. I was thinking if we could do something more for the user with specific filters for associations, but perhaps we can add that later if we need to. Also, you don't need to pass anything to the Boolean filter, it has a default, but I just wanted to show that it was possible :)
Actually, the reason I did the Boolean filter was because I couldn't get it to work without some specific conversions, but maybe I just messed something up. See: https://github.com/varvet/godmin/blob/04abc4cb7a8f070a1115394e384e8ee049b09d18/lib/godmin/filter/boolean.rb#L12
I had to explicitly convert "0" and "1" to false/true values before querying. I thought rails handled that somehow. Shouldn't it do that?
And yes, the options for wildcard
is :pre, :post, :both
with a default of :none
at the moment :)
Regarding the boolean conversion, yeah I recognise that as well...
I guess Boolean
could inherit from Select
then? That's a good question actually. Would it pick up the select filter partial if there is no boolean partial? I guess not, but then I guess the Boolean
filter could implement to_partial_path
and explicitly render the select partial.
Yes, Boolean can inherit from select, it can just do the conversion and call super
.
OK, so now I've implemented 4 simple filters.
First options is always an identifier.
The :column
option is to set a specific column. Defaults to identifier
. Not sure if we need to keep this option actually. I will not use it in all examples below.
Example usage:
Godmin::Filter::String.new(:title, column: :title, wildcard: :post)
Options:
:wildcard
: :pre
, :post
, :both
. Defaults to exact match.Example usage:
Godmin::Filter::Select.new(:author_id, collection: Author.all, option_text: :to_s, option_value: :id)
Options:
:collection
: the collection to show in the select box. May be an array of text/value pairs, like [["True", 1],["False", 0]]
, or an ActiveRecord::Relation
like Author.all
.:option_text
: the method to run on each object to get the option text (only used for AR:Relation
. Defaults to :to_s
.:option_value
: the method to run on each object to get the option value (only used for AR:Relation
. Defaults to :id
.This inherits from the Select
filter. It just renders a different partial which in turn renders a multi select instead of a regular select box.
Example usage:
Godmin::Filter::MultiSelect.new(:status, collection: Status.all, option_text: :to_s, option_value: :id)
Options:
:collection
: the collection to show in the select box. May be an array of selection, like [["True", 1],["False", 0]]
, or an ActiveRecord::Relation
like Status.all
.:option_text
: the method to run on each object to get the option text (only used for AR:Relation
. Defaults to :to_s
.:option_value
: the method to run on each object to get the option value (only used for AR:Relation
. Defaults to :id
.This is a variant of a select filter. Essentially it just allows the user to specify the true and false option texts, converts the value coming from the form (1 = true, 0 = false) and delegates to the select filter.
Example usage:
Godmin::Filter::Boolean.new(:published, true_label: "Published", false_label: "Unpublished")
Options:
:true_label
: the text to show for the true option. Defaults to "True"
.:false_label
: the text to show for the false option. Defaults to "False"
.Now that I write this, maybe I should rename true_label
to true_text
to be consistent with option_text
?
Can we make the select filters above support many-to-many associations or do we just let the user implement those by themselves?
I mean this case:
We have Article
and Tag
. An article can have many tags, a tag may have many articles.
I guess we would like to make a query like
def call(resources, value)
resources.includes(:tags).where(tags: { name: value })
end
So, the filter needs to know the association name, as well as the column name.
What do you think @jensljungblad ?
This is a PR where I will work on #218. I will first focus on the filters and maybe I'll do the actions, scopes and so on afterwards.
New way of defining filters in services
Instead of using a meta programming as before we use constant
FILTERS
. It is just an array which lists all filters you want. The filters themselves are now separated from the service as individual objects. There are a few good things about this separation.Example:
Update Clarification of
Godmin::Filter::String.new(:my_identifier, column: :title, wildcard: :post)
The first parameter to the filters is the identifier. This is used as the name of the filter, used when finding what filter to apply with what filter params etc. By default this value is also used as the name of the column to filter, so I could have done this:Godmin::Filter::String.new(:title, wildcard: :post)
which would use:title
as both the identifier and column name.Creating custom filters
Creating custom filters would be quite easy. You only need to create a class and implement a
call
method that takes a value and a scope.Example: (this filter doesn't make much sense but anyway 😸 )
Views
Each filter defines a
to_partial_path
which makes it easy to render with rails built in render method. This method renders a partial likefilters/_string.html.erb
by default (string is the name of the filter). So, the cat filter above would renderfilters/_cat.html.erb
.The views are simple, looking like this:
To the
*_filter_field
methods you can pass options as usual with rails. See: https://github.com/varvet/godmin/blob/04abc4cb7a8f070a1115394e384e8ee049b09d18/lib/godmin/helpers/filters.rbTo think about
What default filters do we want to ship with Godmin?
Let's start with:
View overriding
Out of the box it's possible to override a filter partial by just putting your own partial in the appropriate place. To override the string filter partial for instance: Godmin location:
views/godmin/filters/_string.html.erb
Your location:views/admin/filters/_string.html.erb
("admin" is your engine name if you have any)However, this would override it for all filters in your app. Perhaps you want to override for a specific resource. We have two options here, that I can think of.
override_partial
-thing as we do with other partials at the moment.Let's say I want a custom view for a my article string filter.
Or if you want the view somewhere else, override
to_partial_path
:Any thoughts here @jensljungblad ?