admino 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -1
- data/CONTRIBUTING.md +40 -0
- data/README.md +198 -11
- data/TODO.md +4 -0
- data/lib/admino/query.rb +2 -2
- data/lib/admino/query/base.rb +11 -11
- data/lib/admino/query/base_presenter.rb +1 -1
- data/lib/admino/query/configuration.rb +6 -6
- data/lib/admino/query/dsl.rb +2 -2
- data/lib/admino/query/{group.rb → filter_group.rb} +4 -4
- data/lib/admino/query/{group_presenter.rb → filter_group_presenter.rb} +6 -7
- data/lib/admino/query/sorting.rb +2 -6
- data/lib/admino/table/head_row.rb +2 -2
- data/lib/admino/version.rb +1 -1
- data/spec/admino/query/base_spec.rb +12 -12
- data/spec/admino/query/dsl_spec.rb +4 -4
- data/spec/admino/query/{group_presenter_spec.rb → filter_group_presenter_spec.rb} +29 -21
- data/spec/admino/query/{group_spec.rb → filter_group_spec.rb} +9 -9
- data/spec/admino/query/sorting_spec.rb +8 -0
- data/spec/spec_helper.rb +1 -1
- metadata +10 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e26dee36252bba0db083897fc29ced0358ab2046
|
4
|
+
data.tar.gz: 0efed84c06512abd26698ea33479d7ed1a7b4746
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f036deab369232c67daf274146a06d1173a82ec26f77d444faf2e4bf66d550adce7224a6f0fe92d486b4eba44b5b9880a8fd216dd39309d3182178d083c69d19
|
7
|
+
data.tar.gz: 1342bbb40cea481a7e4459f74381746a64f6d12c54809a87aad7315fd247d602c5f60ce74bb2ab1fef9aca5ec1e68c51733f62a26aeeea659255fb769478a317
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,12 @@
|
|
1
|
-
# v0.0.
|
1
|
+
# v0.0.4
|
2
|
+
|
3
|
+
* Rename Group into FilterGroup
|
4
|
+
* Rename FilterGroup#available_scopes into #scopes
|
5
|
+
* Rename Sorting#available_scopes into #scopes
|
6
|
+
* Removed nil scope in FilterGroup
|
7
|
+
* Clicking on an active filter scope link will deactivate it
|
8
|
+
|
9
|
+
# v0.0.3
|
2
10
|
|
3
11
|
* Fixed bug in SortingPresenter with default scope
|
4
12
|
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Contributing
|
2
|
+
|
3
|
+
We love pull requests. Here's a quick guide:
|
4
|
+
|
5
|
+
1. Fork the repo.
|
6
|
+
2. Run the tests. We only take pull requests with passing tests, and it's great
|
7
|
+
to know that you have a clean slate: `bundle && rake`
|
8
|
+
3. Add a test for your change. Only refactoring and documentation changes
|
9
|
+
require no new tests. If you are adding functionality or fixing a bug, we need
|
10
|
+
a test!
|
11
|
+
4. Make the test pass.
|
12
|
+
5. Make sure your changes adhere to the [thoughtbot Style
|
13
|
+
Guide](https://github.com/thoughtbot/guides/tree/master/style)
|
14
|
+
6. Push to your fork and submit a pull request.
|
15
|
+
7. At this point you're waiting on us. We like to at least comment on, if not
|
16
|
+
accept, pull requests within three business days (and, typically, one business
|
17
|
+
day). [We may suggest some changes or improvements or
|
18
|
+
alternatives](https://github.com/thoughtbot/guides/tree/master/code-review).
|
19
|
+
|
20
|
+
## Increase your chances of getting merged
|
21
|
+
|
22
|
+
Some things that will increase the chance that your pull request is accepted,
|
23
|
+
taken straight from the Ruby on Rails guide:
|
24
|
+
|
25
|
+
* Use Rails idioms and helpers
|
26
|
+
* Include tests that fail without your code, and pass with it
|
27
|
+
* Update the documentation: that surrounding your change, examples elsewhere,
|
28
|
+
guides, and whatever else is affected by your contribution
|
29
|
+
* Syntax:
|
30
|
+
* Two spaces, no tabs.
|
31
|
+
* No trailing whitespace. Blank lines should not have any space.
|
32
|
+
* Make sure your [source files end with newline
|
33
|
+
characters](http://stackoverflow.com/questions/729692/why-should-files-end-with-a-newline#answer-729725).
|
34
|
+
* Prefer `&&`/`||` over `and`/`or`.
|
35
|
+
* `MyClass.my_method(my_arg)` not `my_method( my_arg )` or
|
36
|
+
`my_method my_arg`.
|
37
|
+
* `a = b` and not `a=b`.
|
38
|
+
* Follow the conventions you see used in the source already.
|
39
|
+
* And in case we didn't emphasize it enough: *We love tests!*
|
40
|
+
|
data/README.md
CHANGED
@@ -1,9 +1,30 @@
|
|
1
1
|
# Admino
|
2
2
|
|
3
|
+
[](http://badge.fury.io/rb/admino)
|
3
4
|
[](https://travis-ci.org/cantierecreativo/admino)
|
4
5
|
[](https://coveralls.io/r/cantierecreativo/admino?branch=master)
|
6
|
+
[](https://codeclimate.com/github/cantierecreativo/admino)
|
5
7
|
|
6
|
-
|
8
|
+
A minimal, object-oriented solution to generate Rails administrative index views. Through query objects and presenters, it features a customizable table generator and search forms with filtering/sorting.
|
9
|
+
|
10
|
+
## The philosophy behind it
|
11
|
+
|
12
|
+
The Rails ecosystem has many [full-fledged solutions to generate administrative interfaces](https://www.ruby-toolbox.com/categories/rails_admin_interfaces).
|
13
|
+
|
14
|
+
Although these tools are very handy to bootstrap a project quickly, they all obey the [80%-20% rule](http://en.wikipedia.org/wiki/Pareto_principle) and tend to be very invasive, often mixing up different concerns on a single responsibility level, thus making tests unbelievably difficult to setup and write.
|
15
|
+
|
16
|
+
A time comes when these all-encompassing tools get in the way. And that will be the moment where all the cumulated saved time will be wasted to solve a single, trivial problem with ugly workarounds and [epic facepalms](http://i.imgur.com/ghKDGyv.jpg).
|
17
|
+
|
18
|
+
So yes, if you're starting a small, short-lived project, go ahead with them, it will be fine! If you're building something that's more valuable or is meant to last longer, there are better alternatives.
|
19
|
+
|
20
|
+
### A modular approach to the problem
|
21
|
+
|
22
|
+
The great thing is that you don't need to write a lot of code to get a more maintainable and modular administrative area.
|
23
|
+
Gems like [Inherited Resources](https://github.com/josevalim/inherited_resources) and [Simple Form](https://github.com/plataformatec/simple_form), combined with [Rails 3.1+ template-inheritance](http://railscasts.com/episodes/269-template-inheritance) already give you ~90% of the time-saving features and the same super-DRY, declarative code that administrative interfaces offer, but with a far more relaxed contract.
|
24
|
+
|
25
|
+
If a particular controller or view needs something different from the standard CRUD/REST treatment, you can just avoid using those gems in that specific context, and fall back to standard Rails code. No workarounds, no facepalms. It seems easy, right? It is.
|
26
|
+
|
27
|
+
So what about Admino? Well, it complements the above-mentioned gems, giving you the the missing ~10%: a fast way to generate administrative index views.
|
7
28
|
|
8
29
|
## Installation
|
9
30
|
|
@@ -15,19 +36,185 @@ And then execute:
|
|
15
36
|
|
16
37
|
$ bundle
|
17
38
|
|
18
|
-
|
39
|
+
## Admino::Query::Base
|
40
|
+
|
41
|
+
A subclass of `Admino::Query::Base` represents a [Query object](http://martinfowler.com/eaaCatalog/queryObject.html), that is, an object responsible for returning a result set (ie. an `ActiveRecord::Relation`) based on business rules (ie. action params).
|
42
|
+
|
43
|
+
Given a `Task` model with the following scopes:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
class Task < ActiveRecord::Base
|
47
|
+
scope :text_matches, ->(text) { where(...) }
|
48
|
+
|
49
|
+
scope :completed, -> { where(completed: true) }
|
50
|
+
scope :pending, -> { where(completed: false) }
|
51
|
+
|
52
|
+
scope :by_due_date, ->(direction) { order(due_date: direction) }
|
53
|
+
scope :by_title, ->(direction) { order(title: direction) }
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
The following `TasksQuery` class can be created:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
class TasksQuery < Admino::Query::Base
|
61
|
+
starting_scope { ProjectTask.all }
|
62
|
+
|
63
|
+
field :text_matches
|
64
|
+
filter_by :status, [:completed, :pending]
|
65
|
+
sorting :by_due_date, :by_title
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
Every query object can declare:
|
70
|
+
|
71
|
+
* a **starting scope**, that is, the scope that will start the filtering/ordering chain;
|
72
|
+
* a set of **search fields**, which represent model scopes that require an input to filter the result set;
|
73
|
+
* a set of **filtering groups**, each of which is composed by a set of scopes that take no argument;
|
74
|
+
* a set of **sorting scopes** that take a sigle argument (`:asc` or `:desc`) and thus are able to order the result set in both directions;
|
75
|
+
|
76
|
+
Each query object instance gets initialized with a hash of params. The `#scope` method will then perform the chaining of the scopes based on the given params, returning the final result set:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
params = {
|
80
|
+
query: {
|
81
|
+
text_matches: 'ASAP'
|
82
|
+
},
|
83
|
+
status: 'pending',
|
84
|
+
sorting: 'by_title',
|
85
|
+
sort_order: 'desc'
|
86
|
+
}
|
87
|
+
|
88
|
+
tasks = TasksQuery.new(params).scope
|
89
|
+
```
|
90
|
+
|
91
|
+
As you may have guessed, query objects can be great companions to index controller actions:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
class ProjectTasksController < ApplicationController
|
95
|
+
def index
|
96
|
+
@query = TasksQuery.new(params)
|
97
|
+
@project_tasks = @query.scope
|
98
|
+
end
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
102
|
+
But that's not all.
|
103
|
+
|
104
|
+
### Presenting search form and filters to the user
|
105
|
+
|
106
|
+
Admino also offers a [Showcase presenter](https://github.com/stefanoverna/showcase) that makes it really easy to generate search forms and filtering links:
|
107
|
+
|
108
|
+
```erb
|
109
|
+
<%# instanciate the the query object presenter %>
|
110
|
+
<% query = present(@query) %>
|
111
|
+
|
112
|
+
<%# generate the search form %>
|
113
|
+
<%= query.form do |q| %>
|
114
|
+
<p>
|
115
|
+
<%= q.label :text_matches %>
|
116
|
+
<%= q.text_field :text_matches %>
|
117
|
+
</p>
|
118
|
+
<p>
|
119
|
+
<%= q.submit %>
|
120
|
+
</p>
|
121
|
+
<% end %>
|
122
|
+
|
123
|
+
<%# generate the filtering links %>
|
124
|
+
<% query.filter_groups.each do |filter_group| %>
|
125
|
+
<h6><%= filter_group.name %></h6>
|
126
|
+
<ul>
|
127
|
+
<% filter_group.scopes.each do |scope| %>
|
128
|
+
<li>
|
129
|
+
<%= filter_group.scope_link(scope) %>
|
130
|
+
<li>
|
131
|
+
<% end %>
|
132
|
+
</ul>
|
133
|
+
<% end %>
|
134
|
+
```
|
135
|
+
|
136
|
+
The great thing is that the search form gets automatically filled in with the last input the user submitted, and a CSS class `is-active` gets added to the currently active filter scopes.
|
137
|
+
|
138
|
+
If a particular filter has been clicked and is now active, it is possible to deactivate it by clicking it again.
|
139
|
+
|
140
|
+
### Simple Form support
|
141
|
+
|
142
|
+
The presenter also offers a `#simple_form` method to make it work with [Simple Form](https://github.com/plataformatec/simple_form) out of the box.
|
143
|
+
|
144
|
+
### I18n
|
145
|
+
|
146
|
+
To localize the search form labels, as well as the group filter names and scope links, please refer to the following YAML file:
|
147
|
+
|
148
|
+
```yaml
|
149
|
+
en:
|
150
|
+
query:
|
151
|
+
attributes:
|
152
|
+
tasks_query:
|
153
|
+
text_matches: 'Contains text'
|
154
|
+
filter_groups:
|
155
|
+
tasks_query:
|
156
|
+
status:
|
157
|
+
name: 'Filter by status'
|
158
|
+
scopes:
|
159
|
+
completed: 'Completed'
|
160
|
+
pending: 'Pending'
|
161
|
+
```
|
162
|
+
|
163
|
+
### Output customisation
|
164
|
+
|
165
|
+
The query object and its presenter implement a number of additional methods and optional arguments that allow a great amount of flexibility: please refer to the tests to see all the possibile customisations available.
|
166
|
+
|
167
|
+
#### Overwriting the starting scope
|
168
|
+
|
169
|
+
Suppose you have to filter the tasks based on the `@current_user` work group. You can easily provide an alternative starting scope from the controller passing it as an argument to the `#scope` method:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
def index
|
173
|
+
@query = TasksQuery.new(params)
|
174
|
+
@project_tasks = @query.scope(@current_user.team.tasks)
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
178
|
+
### Default sortings
|
179
|
+
|
180
|
+
#### Coertions
|
181
|
+
|
182
|
+
Admino can perform automatic coertions from a param string input to the type needed by the model named scope:
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
class TasksQuery < Admino::Query::Base
|
186
|
+
# ...
|
187
|
+
field :due_date_from, coerce: :to_date
|
188
|
+
field :due_date_to, coerce: :to_date
|
189
|
+
end
|
190
|
+
```
|
191
|
+
The following coertions are available:
|
192
|
+
|
193
|
+
* `:to_boolean`
|
194
|
+
* `:to_constant`
|
195
|
+
* `:to_date`
|
196
|
+
* `:to_datetime`
|
197
|
+
* `:to_decimal`
|
198
|
+
* `:to_float`
|
199
|
+
* `:to_integer`
|
200
|
+
* `:to_symbol`
|
201
|
+
* `:to_time`
|
202
|
+
|
203
|
+
If a specific coercion cannot be performed with the provided input, the scope won't be chained.
|
204
|
+
|
205
|
+
Please see the [`Coercible::Coercer::String`](https://github.com/solnic/coercible/blob/master/lib/coercible/coercer/string.rb) class for details.
|
19
206
|
|
20
|
-
|
207
|
+
### Ending the scope chain
|
21
208
|
|
22
|
-
|
209
|
+
It's very common ie. to paginate the result set. `Admino::Query::Base` DSL makes it easy to append any scope to the end of the chain:
|
23
210
|
|
24
|
-
|
211
|
+
```ruby
|
212
|
+
class TasksQuery < Admino::Query::Base
|
213
|
+
ending_scope { |q| page(q.params[:page]) }
|
214
|
+
end
|
215
|
+
```
|
25
216
|
|
26
|
-
##
|
217
|
+
## Admino::Table::Presenter
|
27
218
|
|
28
|
-
|
29
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
30
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
31
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
32
|
-
5. Create new Pull Request
|
219
|
+
WIP
|
33
220
|
|
data/TODO.md
ADDED
data/lib/admino/query.rb
CHANGED
@@ -2,8 +2,8 @@ require 'admino/query/base'
|
|
2
2
|
require 'admino/query/base_presenter'
|
3
3
|
require 'admino/query/configuration'
|
4
4
|
require 'admino/query/field'
|
5
|
-
require 'admino/query/
|
6
|
-
require 'admino/query/
|
5
|
+
require 'admino/query/filter_group'
|
6
|
+
require 'admino/query/filter_group_presenter'
|
7
7
|
require 'admino/query/sorting'
|
8
8
|
require 'admino/query/sorting_presenter'
|
9
9
|
|
data/lib/admino/query/base.rb
CHANGED
@@ -13,7 +13,7 @@ module Admino
|
|
13
13
|
extend Dsl
|
14
14
|
|
15
15
|
attr_reader :params
|
16
|
-
attr_reader :
|
16
|
+
attr_reader :filter_groups
|
17
17
|
attr_reader :fields
|
18
18
|
attr_reader :sorting
|
19
19
|
|
@@ -25,7 +25,7 @@ module Admino
|
|
25
25
|
@params = ActiveSupport::HashWithIndifferentAccess.new(params)
|
26
26
|
@config = config
|
27
27
|
|
28
|
-
|
28
|
+
init_filter_groups
|
29
29
|
init_fields
|
30
30
|
init_sorting
|
31
31
|
end
|
@@ -39,7 +39,7 @@ module Admino
|
|
39
39
|
|
40
40
|
scope_builder = starting_scope
|
41
41
|
|
42
|
-
scope_augmenters = fields +
|
42
|
+
scope_augmenters = fields + filter_groups
|
43
43
|
scope_augmenters << sorting if sorting
|
44
44
|
|
45
45
|
scope_augmenters.each do |field|
|
@@ -61,12 +61,12 @@ module Admino
|
|
61
61
|
@config || self.class.config
|
62
62
|
end
|
63
63
|
|
64
|
-
def
|
65
|
-
@
|
64
|
+
def filter_groups
|
65
|
+
@filter_groups.values
|
66
66
|
end
|
67
67
|
|
68
|
-
def
|
69
|
-
@
|
68
|
+
def filter_group_by_name(name)
|
69
|
+
@filter_groups[name]
|
70
70
|
end
|
71
71
|
|
72
72
|
def fields
|
@@ -79,11 +79,11 @@ module Admino
|
|
79
79
|
|
80
80
|
private
|
81
81
|
|
82
|
-
def
|
83
|
-
@
|
82
|
+
def init_filter_groups
|
83
|
+
@filter_groups = {}
|
84
84
|
i18n_key = self.class.model_name.i18n_key
|
85
|
-
config.
|
86
|
-
@
|
85
|
+
config.filter_groups.each do |config|
|
86
|
+
@filter_groups[config.name] = FilterGroup.new(config, params, i18n_key)
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
@@ -17,7 +17,7 @@ module Admino
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
class
|
20
|
+
class FilterGroup
|
21
21
|
attr_reader :name
|
22
22
|
attr_reader :scopes
|
23
23
|
|
@@ -48,14 +48,14 @@ module Admino
|
|
48
48
|
end
|
49
49
|
|
50
50
|
attr_reader :fields
|
51
|
-
attr_reader :
|
51
|
+
attr_reader :filter_groups
|
52
52
|
attr_reader :sorting
|
53
53
|
attr_accessor :starting_scope_callable
|
54
54
|
attr_accessor :ending_scope_callable
|
55
55
|
|
56
56
|
def initialize
|
57
57
|
@fields = []
|
58
|
-
@
|
58
|
+
@filter_groups = []
|
59
59
|
end
|
60
60
|
|
61
61
|
def add_field(name, options = {})
|
@@ -64,9 +64,9 @@ module Admino
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
-
def
|
68
|
-
|
69
|
-
self.
|
67
|
+
def add_filter_group(name, scopes)
|
68
|
+
FilterGroup.new(name, scopes).tap do |filter_group|
|
69
|
+
self.filter_groups << filter_group
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
data/lib/admino/query/dsl.rb
CHANGED
@@ -3,7 +3,7 @@ require 'active_support/core_ext/hash'
|
|
3
3
|
|
4
4
|
module Admino
|
5
5
|
module Query
|
6
|
-
class
|
6
|
+
class FilterGroup
|
7
7
|
attr_reader :params
|
8
8
|
attr_reader :config
|
9
9
|
attr_reader :query_i18n_key
|
@@ -23,7 +23,7 @@ module Admino
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def active_scope
|
26
|
-
if param_value &&
|
26
|
+
if param_value && scopes.include?(param_value.to_sym)
|
27
27
|
param_value.to_sym
|
28
28
|
else
|
29
29
|
nil
|
@@ -42,8 +42,8 @@ module Admino
|
|
42
42
|
config.name
|
43
43
|
end
|
44
44
|
|
45
|
-
def
|
46
|
-
|
45
|
+
def scopes
|
46
|
+
config.scopes
|
47
47
|
end
|
48
48
|
|
49
49
|
def i18n_key
|
@@ -7,7 +7,7 @@ require 'active_support/core_ext/hash'
|
|
7
7
|
|
8
8
|
module Admino
|
9
9
|
module Query
|
10
|
-
class
|
10
|
+
class FilterGroupPresenter < Showcase::Presenter
|
11
11
|
def scope_link(scope, *args)
|
12
12
|
options = args.extract_options!
|
13
13
|
|
@@ -30,20 +30,19 @@ module Admino
|
|
30
30
|
def scope_params(scope)
|
31
31
|
params = ActiveSupport::HashWithIndifferentAccess.new(h.request.query_parameters)
|
32
32
|
|
33
|
-
if scope
|
34
|
-
params.merge!(param_name => scope.to_s)
|
35
|
-
else
|
33
|
+
if is_scope_active?(scope)
|
36
34
|
params.delete(param_name)
|
35
|
+
else
|
36
|
+
params.merge!(param_name => scope.to_s)
|
37
37
|
end
|
38
38
|
|
39
39
|
params
|
40
40
|
end
|
41
41
|
|
42
42
|
def scope_name(scope)
|
43
|
-
scope ||= 'none'
|
44
43
|
I18n.t(
|
45
44
|
:"#{query_i18n_key}.#{i18n_key}.scopes.#{scope}",
|
46
|
-
scope: 'query.
|
45
|
+
scope: 'query.filter_groups',
|
47
46
|
default: [
|
48
47
|
:"#{i18n_key}.scopes.#{scope}",
|
49
48
|
scope.to_s.titleize
|
@@ -54,7 +53,7 @@ module Admino
|
|
54
53
|
def name
|
55
54
|
I18n.t(
|
56
55
|
:"#{query_i18n_key}.#{i18n_key}.name",
|
57
|
-
scope: 'query.
|
56
|
+
scope: 'query.filter_groups',
|
58
57
|
default: [
|
59
58
|
:"#{i18n_key}.name",
|
60
59
|
i18n_key.to_s.titleize
|
data/lib/admino/query/sorting.rb
CHANGED
@@ -36,7 +36,7 @@ module Admino
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def active_scope
|
39
|
-
if param_value &&
|
39
|
+
if param_value && scopes.include?(param_value.to_sym)
|
40
40
|
param_value.to_sym
|
41
41
|
elsif default_scope
|
42
42
|
default_scope
|
@@ -65,13 +65,9 @@ module Admino
|
|
65
65
|
:sorting
|
66
66
|
end
|
67
67
|
|
68
|
-
def
|
68
|
+
def scopes
|
69
69
|
config.scopes
|
70
70
|
end
|
71
|
-
|
72
|
-
def scope_name
|
73
|
-
config.name
|
74
|
-
end
|
75
71
|
end
|
76
72
|
end
|
77
73
|
end
|
@@ -43,13 +43,13 @@ module Admino
|
|
43
43
|
html_options = html_options.to_h
|
44
44
|
|
45
45
|
sorting_scope = html_options.delete(:sorting)
|
46
|
+
sorting_html_options = html_options.delete(:sorting_html_options) { {} }
|
46
47
|
if sorting_scope
|
47
48
|
raise ArgumentError, 'query object is required' unless query
|
48
|
-
sorting_html_options = html_options.delete(:sorting_html_options) { {} }
|
49
49
|
label = query.sorting.scope_link(sorting_scope, label, sorting_html_options)
|
50
50
|
end
|
51
51
|
|
52
|
-
@columns << h.content_tag(:th, label.to_s, html_options
|
52
|
+
@columns << h.content_tag(:th, label.to_s, html_options)
|
53
53
|
end
|
54
54
|
|
55
55
|
def to_html
|
data/lib/admino/version.rb
CHANGED
@@ -34,19 +34,19 @@ module Admino
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
context 'with a declared
|
37
|
+
context 'with a declared filter_group' do
|
38
38
|
let(:config) { Configuration.new }
|
39
|
-
let(:
|
39
|
+
let(:filter_group_config) { config.add_filter_group(:filter_group, [:one, :two]) }
|
40
40
|
|
41
41
|
before do
|
42
|
-
|
42
|
+
filter_group_config
|
43
43
|
end
|
44
44
|
|
45
|
-
it 'returns a configured
|
46
|
-
|
47
|
-
expect(
|
48
|
-
expect(
|
49
|
-
expect(
|
45
|
+
it 'returns a configured FilterGroup' do
|
46
|
+
filter_group = query.filter_group_by_name(:filter_group)
|
47
|
+
expect(filter_group.config).to eq filter_group_config
|
48
|
+
expect(filter_group.params).to eq params
|
49
|
+
expect(filter_group.i18n_key).to eq :filter_group
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
@@ -107,15 +107,15 @@ module Admino
|
|
107
107
|
end
|
108
108
|
end
|
109
109
|
|
110
|
-
context 'with a set of fields and
|
110
|
+
context 'with a set of fields and filter_groups' do
|
111
111
|
let(:field_config) { config.add_field(:field) }
|
112
|
-
let(:
|
112
|
+
let(:filter_group_config) { config.add_filter_group(:filter_group, [:one, :two]) }
|
113
113
|
let(:scope_chained_with_field) { double('scope 1') }
|
114
114
|
let(:final_chain) { double('scope 2') }
|
115
115
|
|
116
116
|
before do
|
117
117
|
field_config
|
118
|
-
|
118
|
+
filter_group_config
|
119
119
|
query
|
120
120
|
|
121
121
|
query.field_by_name(:field).
|
@@ -123,7 +123,7 @@ module Admino
|
|
123
123
|
with(starting_scope).
|
124
124
|
and_return(scope_chained_with_field)
|
125
125
|
|
126
|
-
query.
|
126
|
+
query.filter_group_by_name(:filter_group).
|
127
127
|
stub(:augment_scope).
|
128
128
|
with(scope_chained_with_field).
|
129
129
|
and_return(final_chain)
|
@@ -12,10 +12,10 @@ module Admino
|
|
12
12
|
expect(field.coerce_to).to eq :to_date
|
13
13
|
end
|
14
14
|
|
15
|
-
it 'allows #
|
16
|
-
|
17
|
-
expect(
|
18
|
-
expect(
|
15
|
+
it 'allows #filter_by declaration' do
|
16
|
+
filter_group = config.filter_groups.first
|
17
|
+
expect(filter_group.name).to eq :bar
|
18
|
+
expect(filter_group.scopes).to eq [:one, :two]
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'allows #sortings declaration' do
|
@@ -2,21 +2,21 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module Admino
|
4
4
|
module Query
|
5
|
-
describe
|
6
|
-
subject(:presenter) {
|
5
|
+
describe FilterGroupPresenter do
|
6
|
+
subject(:presenter) { FilterGroupPresenter.new(filter_group, view) }
|
7
7
|
let(:view) { RailsViewContext.new }
|
8
|
-
let(:
|
8
|
+
let(:filter_group) do
|
9
9
|
double(
|
10
|
-
'
|
10
|
+
'FilterGroup',
|
11
11
|
query_i18n_key: :query_name,
|
12
|
-
i18n_key: :
|
13
|
-
param_name: :
|
12
|
+
i18n_key: :filter_group,
|
13
|
+
param_name: :filter_group
|
14
14
|
)
|
15
15
|
end
|
16
16
|
let(:request_object) do
|
17
17
|
double(
|
18
18
|
'ActionDispatch::Request',
|
19
|
-
query_parameters: { '
|
19
|
+
query_parameters: { 'filter_group' => 'bar' },
|
20
20
|
path: '/'
|
21
21
|
)
|
22
22
|
end
|
@@ -27,15 +27,15 @@ module Admino
|
|
27
27
|
|
28
28
|
describe '#scope_link' do
|
29
29
|
subject { presenter.scope_link(:foo) }
|
30
|
+
let(:scope_active) { false }
|
30
31
|
|
31
32
|
before do
|
32
|
-
|
33
|
+
filter_group.stub(:is_scope_active?).
|
34
|
+
with(:foo).and_return(scope_active)
|
33
35
|
end
|
34
36
|
|
35
37
|
context 'active CSS class' do
|
36
|
-
|
37
|
-
group.stub(:is_scope_active?).with(:foo).and_return(true)
|
38
|
-
end
|
38
|
+
let(:scope_active) { true }
|
39
39
|
|
40
40
|
it 'adds an is-active class' do
|
41
41
|
should have_tag(:a, with: { class: 'is-active' })
|
@@ -86,15 +86,23 @@ module Admino
|
|
86
86
|
end
|
87
87
|
|
88
88
|
describe '#scope_params' do
|
89
|
-
|
90
|
-
|
91
|
-
|
89
|
+
before do
|
90
|
+
filter_group.stub(:is_scope_active?).with(:foo).and_return(scope_active)
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'if scope is active' do
|
94
|
+
let(:scope_active) { true }
|
95
|
+
|
96
|
+
it 'deletes the filter_group param' do
|
97
|
+
expect(presenter.scope_params(:foo)).not_to have_key 'filter_group'
|
92
98
|
end
|
93
99
|
end
|
94
100
|
|
95
101
|
context 'else' do
|
96
|
-
|
97
|
-
|
102
|
+
let(:scope_active) { false }
|
103
|
+
|
104
|
+
it 'is set as filter group value' do
|
105
|
+
expect(presenter.scope_params(:foo)[:filter_group]).to eq 'foo'
|
98
106
|
end
|
99
107
|
end
|
100
108
|
end
|
@@ -104,18 +112,18 @@ module Admino
|
|
104
112
|
before do
|
105
113
|
I18n.backend.store_translations(
|
106
114
|
:en,
|
107
|
-
query: {
|
115
|
+
query: { filter_groups: { query_name: { filter_group: { name: 'NAME' } } } }
|
108
116
|
)
|
109
117
|
end
|
110
118
|
|
111
|
-
it 'returns a I18n translatable name for the
|
119
|
+
it 'returns a I18n translatable name for the filter_group' do
|
112
120
|
expect(presenter.name).to eq 'NAME'
|
113
121
|
end
|
114
122
|
end
|
115
123
|
|
116
124
|
context 'if no translation is available' do
|
117
|
-
it 'falls back to a titleized version of the
|
118
|
-
expect(presenter.name).to eq 'Group'
|
125
|
+
it 'falls back to a titleized version of the filter_group name' do
|
126
|
+
expect(presenter.name).to eq 'Filter Group'
|
119
127
|
end
|
120
128
|
end
|
121
129
|
end
|
@@ -125,7 +133,7 @@ module Admino
|
|
125
133
|
before do
|
126
134
|
I18n.backend.store_translations(
|
127
135
|
:en,
|
128
|
-
query: {
|
136
|
+
query: { filter_groups: { query_name: { filter_group: { scopes: { bar: 'NAME' } } } } }
|
129
137
|
)
|
130
138
|
end
|
131
139
|
|
@@ -2,9 +2,9 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module Admino
|
4
4
|
module Query
|
5
|
-
describe
|
6
|
-
subject(:
|
7
|
-
let(:config) { Configuration::
|
5
|
+
describe FilterGroup do
|
6
|
+
subject(:filter_group) { FilterGroup.new(config, params) }
|
7
|
+
let(:config) { Configuration::FilterGroup.new(:foo, [:bar]) }
|
8
8
|
let(:params) { {} }
|
9
9
|
|
10
10
|
describe '#active_scope' do
|
@@ -12,7 +12,7 @@ module Admino
|
|
12
12
|
let(:params) { {} }
|
13
13
|
|
14
14
|
it 'returns nil' do
|
15
|
-
expect(
|
15
|
+
expect(filter_group.active_scope).to be_nil
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -20,7 +20,7 @@ module Admino
|
|
20
20
|
let(:params) { { 'foo' => 'qux' } }
|
21
21
|
|
22
22
|
it 'returns nil' do
|
23
|
-
expect(
|
23
|
+
expect(filter_group.active_scope).to be_nil
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -28,19 +28,19 @@ module Admino
|
|
28
28
|
let(:params) { { 'foo' => 'bar' } }
|
29
29
|
|
30
30
|
it 'returns the scope name' do
|
31
|
-
expect(
|
31
|
+
expect(filter_group.active_scope).to eq :bar
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
36
|
describe '#augment_scope' do
|
37
|
-
let(:result) {
|
37
|
+
let(:result) { filter_group.augment_scope(scope) }
|
38
38
|
let(:scope) { ScopeMock.new('original') }
|
39
39
|
|
40
40
|
context 'if the field has a value' do
|
41
41
|
let(:params) { { 'foo' => 'bar' } }
|
42
42
|
|
43
|
-
it 'returns the original scope chained with the
|
43
|
+
it 'returns the original scope chained with the filter_group scope' do
|
44
44
|
expect(result.chain).to eq [:bar, []]
|
45
45
|
end
|
46
46
|
end
|
@@ -56,7 +56,7 @@ module Admino
|
|
56
56
|
let(:params) { { 'foo' => 'bar' } }
|
57
57
|
|
58
58
|
it 'returns true if the provided scope is the one currently active' do
|
59
|
-
expect(
|
59
|
+
expect(filter_group.is_scope_active?(:bar)).to be_true
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
@@ -123,6 +123,14 @@ module Admino
|
|
123
123
|
end
|
124
124
|
end
|
125
125
|
end
|
126
|
+
|
127
|
+
describe '#is_scope_active?' do
|
128
|
+
let(:params) { { 'sorting' => 'by_date' } }
|
129
|
+
|
130
|
+
it 'returns true if the provided scope is the one currently active' do
|
131
|
+
expect(sorting.is_scope_active?(:by_date)).to be_true
|
132
|
+
end
|
133
|
+
end
|
126
134
|
end
|
127
135
|
end
|
128
136
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: admino
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefano Verna
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-03-
|
11
|
+
date: 2014-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: showcase
|
@@ -174,10 +174,12 @@ files:
|
|
174
174
|
- .gitignore
|
175
175
|
- .travis.yml
|
176
176
|
- CHANGELOG.md
|
177
|
+
- CONTRIBUTING.md
|
177
178
|
- Gemfile
|
178
179
|
- LICENSE.txt
|
179
180
|
- README.md
|
180
181
|
- Rakefile
|
182
|
+
- TODO.md
|
181
183
|
- admino.gemspec
|
182
184
|
- lib/admino.rb
|
183
185
|
- lib/admino/query.rb
|
@@ -186,8 +188,8 @@ files:
|
|
186
188
|
- lib/admino/query/configuration.rb
|
187
189
|
- lib/admino/query/dsl.rb
|
188
190
|
- lib/admino/query/field.rb
|
189
|
-
- lib/admino/query/
|
190
|
-
- lib/admino/query/
|
191
|
+
- lib/admino/query/filter_group.rb
|
192
|
+
- lib/admino/query/filter_group_presenter.rb
|
191
193
|
- lib/admino/query/sorting.rb
|
192
194
|
- lib/admino/query/sorting_presenter.rb
|
193
195
|
- lib/admino/table.rb
|
@@ -200,8 +202,8 @@ files:
|
|
200
202
|
- spec/admino/query/base_spec.rb
|
201
203
|
- spec/admino/query/dsl_spec.rb
|
202
204
|
- spec/admino/query/field_spec.rb
|
203
|
-
- spec/admino/query/
|
204
|
-
- spec/admino/query/
|
205
|
+
- spec/admino/query/filter_group_presenter_spec.rb
|
206
|
+
- spec/admino/query/filter_group_spec.rb
|
205
207
|
- spec/admino/query/sorting_presenter_spec.rb
|
206
208
|
- spec/admino/query/sorting_spec.rb
|
207
209
|
- spec/admino/table/head_row_spec.rb
|
@@ -238,8 +240,8 @@ test_files:
|
|
238
240
|
- spec/admino/query/base_spec.rb
|
239
241
|
- spec/admino/query/dsl_spec.rb
|
240
242
|
- spec/admino/query/field_spec.rb
|
241
|
-
- spec/admino/query/
|
242
|
-
- spec/admino/query/
|
243
|
+
- spec/admino/query/filter_group_presenter_spec.rb
|
244
|
+
- spec/admino/query/filter_group_spec.rb
|
243
245
|
- spec/admino/query/sorting_presenter_spec.rb
|
244
246
|
- spec/admino/query/sorting_spec.rb
|
245
247
|
- spec/admino/table/head_row_spec.rb
|