admino 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/admino.png)](http://badge.fury.io/rb/admino)
|
3
4
|
[![Build Status](https://travis-ci.org/cantierecreativo/admino.png?branch=v0.0.1)](https://travis-ci.org/cantierecreativo/admino)
|
4
5
|
[![Coverage Status](https://coveralls.io/repos/cantierecreativo/admino/badge.png?branch=master)](https://coveralls.io/r/cantierecreativo/admino?branch=master)
|
6
|
+
[![Code Climate](https://codeclimate.com/github/cantierecreativo/admino.png)](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
|