warped 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +25 -0
- data/Gemfile +0 -2
- data/Gemfile.lock +25 -19
- data/README.md +116 -270
- data/app/assets/config/warped_manifest.js +2 -0
- data/app/assets/javascript/warped/controllers/filter_controller.js +76 -0
- data/app/assets/javascript/warped/controllers/filters_controller.js +21 -0
- data/app/assets/javascript/warped/index.js +2 -0
- data/app/assets/stylesheets/warped/application.css +15 -0
- data/app/assets/stylesheets/warped/base.css +23 -0
- data/app/assets/stylesheets/warped/filters.css +115 -0
- data/app/assets/stylesheets/warped/pagination.css +74 -0
- data/app/assets/stylesheets/warped/search.css +33 -0
- data/app/assets/stylesheets/warped/table.css +114 -0
- data/app/views/warped/_actions.html.erb +9 -0
- data/app/views/warped/_cell.html.erb +3 -0
- data/app/views/warped/_column.html.erb +35 -0
- data/app/views/warped/_filters.html.erb +21 -0
- data/app/views/warped/_hidden_fields.html.erb +19 -0
- data/app/views/warped/_pagination.html.erb +34 -0
- data/app/views/warped/_row.html.erb +19 -0
- data/app/views/warped/_search.html.erb +21 -0
- data/app/views/warped/_table.html.erb +52 -0
- data/app/views/warped/filters/_filter.html.erb +40 -0
- data/config/importmap.rb +3 -0
- data/docs/controllers/FILTERABLE.md +193 -0
- data/docs/controllers/PAGEABLE.md +70 -0
- data/docs/controllers/README.md +8 -0
- data/docs/controllers/SEARCHABLE.md +95 -0
- data/docs/controllers/SORTABLE.md +94 -0
- data/docs/controllers/TABULATABLE.md +28 -0
- data/docs/controllers/views/PARTIALS.md +285 -0
- data/docs/jobs/README.md +22 -0
- data/docs/services/README.md +81 -0
- data/lib/generators/warped/install_generator.rb +1 -1
- data/lib/warped/api/filter/base/value.rb +52 -0
- data/lib/warped/api/filter/base.rb +84 -0
- data/lib/warped/api/filter/boolean.rb +41 -0
- data/lib/warped/api/filter/date.rb +26 -0
- data/lib/warped/api/filter/date_time.rb +32 -0
- data/lib/warped/api/filter/decimal.rb +31 -0
- data/lib/warped/api/filter/factory.rb +38 -0
- data/lib/warped/api/filter/integer.rb +38 -0
- data/lib/warped/api/filter/string.rb +25 -0
- data/lib/warped/api/filter/time.rb +25 -0
- data/lib/warped/api/filter.rb +14 -0
- data/lib/warped/api/sort/value.rb +40 -0
- data/lib/warped/api/sort.rb +65 -0
- data/lib/warped/controllers/filterable/ui.rb +46 -0
- data/lib/warped/controllers/filterable.rb +79 -42
- data/lib/warped/controllers/pageable/ui.rb +70 -0
- data/lib/warped/controllers/pageable.rb +11 -11
- data/lib/warped/controllers/searchable/ui.rb +37 -0
- data/lib/warped/controllers/searchable.rb +2 -0
- data/lib/warped/controllers/sortable/ui.rb +53 -0
- data/lib/warped/controllers/sortable.rb +53 -33
- data/lib/warped/controllers/tabulatable/ui.rb +54 -0
- data/lib/warped/controllers/tabulatable.rb +13 -27
- data/lib/warped/emails/components/align.rb +21 -0
- data/lib/warped/emails/components/base.rb +116 -0
- data/lib/warped/emails/components/button.rb +58 -0
- data/lib/warped/emails/components/divider.rb +15 -0
- data/lib/warped/emails/components/heading.rb +65 -0
- data/lib/warped/emails/components/layouts/columns.rb +36 -0
- data/lib/warped/emails/components/layouts/cta.rb +38 -0
- data/lib/warped/emails/components/layouts/main.rb +34 -0
- data/lib/warped/emails/components/link.rb +36 -0
- data/lib/warped/emails/components/spacer.rb +15 -0
- data/lib/warped/emails/components/stepper.rb +104 -0
- data/lib/warped/emails/components/table.rb +37 -0
- data/lib/warped/emails/components/text.rb +67 -0
- data/lib/warped/emails/helpers.rb +26 -0
- data/lib/warped/emails/slottable.rb +61 -0
- data/lib/warped/emails/styleable.rb +160 -0
- data/lib/warped/engine.rb +19 -0
- data/lib/warped/queries/filter.rb +32 -12
- data/lib/warped/table/action.rb +33 -0
- data/lib/warped/table/column.rb +34 -0
- data/lib/warped/version.rb +1 -1
- data/lib/warped.rb +2 -0
- data/warped.gemspec +1 -1
- metadata +73 -7
- data/lib/warped/emails/.keep +0 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
<%# locals: (collection:, path:, columns:, turbo_action: :replace, actions: [], **options) %>
|
2
|
+
|
3
|
+
|
4
|
+
<%= tag.div class: 'warped-table' do %>
|
5
|
+
<div class="warped-table--controls">
|
6
|
+
<% if controller.class.include?(Warped::Controllers::Searchable::Ui) %>
|
7
|
+
<%= render "warped/search", path:, turbo_action:, **(options[:search].presence || {}) %>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<% if controller.class.include?(Warped::Controllers::Filterable::Ui) %>
|
11
|
+
<%= render "warped/filters", path:, turbo_action:, **(options[:filters].presence || {}) %>
|
12
|
+
<% end %>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
<% table_opts = options[:table]&.deep_dup.presence || {} %>
|
18
|
+
<% table_class = "warped-table--table #{table_opts.delete(:class)}" %>
|
19
|
+
|
20
|
+
<div class="warped-table--container">
|
21
|
+
<%= tag.div class: table_class, **table_opts do %>
|
22
|
+
<div class="warped-table--table--header">
|
23
|
+
<div class="warped-table--table--row">
|
24
|
+
<% columns.each do |column| %>
|
25
|
+
<%= render "warped/column", column:, path:, turbo_action: %>
|
26
|
+
<% end %>
|
27
|
+
|
28
|
+
<% if actions.any? %>
|
29
|
+
<div class="warped-table--table--cell">
|
30
|
+
Actions
|
31
|
+
</div>
|
32
|
+
<% end %>
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
<div class="warped-table--table--row-group">
|
36
|
+
<div class="warped-table--table--empty-row">
|
37
|
+
<div class="warped-table--table--cell">
|
38
|
+
Whoops! Nothing over here!
|
39
|
+
</div>
|
40
|
+
</div>
|
41
|
+
<% if (block = yield).present? %>
|
42
|
+
<%= block %>
|
43
|
+
<% else %>
|
44
|
+
<% collection.each do |resource| %>
|
45
|
+
<%= render "warped/row", resource:, columns:, actions: %>
|
46
|
+
<% end %>
|
47
|
+
<% end %>
|
48
|
+
</div>
|
49
|
+
<% end %>
|
50
|
+
</div>
|
51
|
+
<%= render("warped/pagination", path:, turbo_action:, **(options[:pagination].presence || {})) if controller.class.include?(Warped::Controllers::Pageable::Ui) %>
|
52
|
+
<% end %>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<%# locals: (form:, filter:) %>
|
2
|
+
|
3
|
+
<% filter_value = current_action_filter_values.find { |v| v.filter.name == filter.name } %>
|
4
|
+
|
5
|
+
<div class="warped-filters--filter <%= 'warped-filters--filter-inactive' if filter_value.blank? %>" data-controller="filter" data-filter-filter-outlet=".warped-filters--filter" data-filter-empty-class="warped-filters--filter-inactive" data-filter-collapsed-class="warped-filters--filter--panel-collapsed" data-action="keydown.esc@window->filter#close click@window->filter#clickOutside">
|
6
|
+
<div class="warped-filters--filter--icon" data-action="click->filter#toggle">
|
7
|
+
<% if !filter_value.nil? %>
|
8
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
9
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
10
|
+
</svg>
|
11
|
+
<% else %>
|
12
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
13
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v6m3-3H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
14
|
+
</svg>
|
15
|
+
<% end %>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<%= form.label filter.parameter_name, class: "warped-filters--filter--label" %>
|
19
|
+
|
20
|
+
<div data-filter-target="badgeValue" class="warped-filters--filter--value">
|
21
|
+
<span class="warped-filters--filter--value--values" data-filter-target="relation">
|
22
|
+
<%= filter_value&.relation if !filter_value&.empty? %>
|
23
|
+
</span>
|
24
|
+
|
25
|
+
<span class="warped-filters--filter--value--values" data-filter-target="value">
|
26
|
+
<%= ": #{filter_value&.value}" if !filter_value&.html_value.nil? %>
|
27
|
+
</span>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<div class="warped-filters--filter--panel-collapsed warped-filters--filter--panel" data-filter-target="panel">
|
31
|
+
<%= form.select "#{filter.parameter_name}.rel", options_for_select(filter.relations, include_blank: true, selected: filter_value&.relation), {}, class: "warped-filters--filter--panel--select", data: { filter_target: "relationInput", action: "change->filter#changeRelation" } %>
|
32
|
+
<%= tag :input, type: filter.html_type, name: filter.parameter_name, value: filter_value&.html_value, class: "warped-filters--filter--panel--input", data: { filter_target: "valueInput", action: "keyup->filter#changeValue" } %>
|
33
|
+
|
34
|
+
<span class="warped-filters--filter--panel--remove" data-action="click->filter#clear click->filter#close">
|
35
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
36
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
37
|
+
</svg>
|
38
|
+
</span>
|
39
|
+
</div>
|
40
|
+
</div>
|
data/config/importmap.rb
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
# Warped::Controllers::Filterable
|
2
|
+
|
3
|
+
The `Filterable` concern provides a method to filter the records in a controller's action.
|
4
|
+
The method `filterable_by` is used to define the filterable fields and the filter method to use.
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
class UsersController < ApplicationController
|
8
|
+
include Warped::Controllers::Filterable
|
9
|
+
|
10
|
+
filterable_by :name, :email, :created_at
|
11
|
+
|
12
|
+
def index
|
13
|
+
users = filter(User.all)
|
14
|
+
render json: users
|
15
|
+
end
|
16
|
+
end
|
17
|
+
```
|
18
|
+
The `filter` method will use the query parameters to filter the records. For example, to filter the users by name, email, and created_at, the following query parameters can be used:
|
19
|
+
|
20
|
+
```
|
21
|
+
GET /users?name=John
|
22
|
+
GET /users?email=john@example.com
|
23
|
+
GET /users?created_at=2021-01-01
|
24
|
+
```
|
25
|
+
|
26
|
+
## Adding type-safety to the fields
|
27
|
+
|
28
|
+
The `filterable_by` method, accepts keyword arguments to be passed. The keyword arguments are the field names and the type to cast the query parameter to. This is useful to prevent invalid queries from being executed on the database.
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
class UsersController < ApplicationController
|
32
|
+
include Warped::Controllers::Filterable
|
33
|
+
|
34
|
+
filterable_by name: { kind: :string }, email: { kind: :string }, created_at: { kind: :date }
|
35
|
+
|
36
|
+
def index
|
37
|
+
users = filter(User.all)
|
38
|
+
render json: users
|
39
|
+
end
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
When passing a value to the query parameter, the value will be cast to the specified type. If casting fails, the query parameter will be ignored.
|
44
|
+
|
45
|
+
If the strict flag is passed to the filterable_by method, then it will raise a `Filter::ValueError`.
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
class UsersController < ApplicationController
|
49
|
+
include Warped::Controllers::Filterable
|
50
|
+
|
51
|
+
filterable_by created_at: { kind: :date }, strict: true
|
52
|
+
|
53
|
+
def index
|
54
|
+
users = filter(User.all)
|
55
|
+
render json: users
|
56
|
+
end
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
Request examples:
|
61
|
+
```
|
62
|
+
GET /users?created_at=2021-01-01 # returns users created at 2021-01-01
|
63
|
+
GET /users?created_at=not_a_date # Raises a Filter::ValueError, with the message 'not_a_date' cannot be casted to date
|
64
|
+
```
|
65
|
+
|
66
|
+
## Handling invalid filter values in strict mode
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
class UsersController < ApplicationController
|
70
|
+
include Warped::Controllers::Filterable
|
71
|
+
|
72
|
+
rescue_from Filter::ValueError, with: :render_invalid_filter_value
|
73
|
+
rescue_from Filter::RelationError, with: :render_invalid_filter_relation
|
74
|
+
|
75
|
+
filterable_by age: { kind: :integer }, strict: true
|
76
|
+
|
77
|
+
def index
|
78
|
+
users = filter(User.all)
|
79
|
+
render json: users
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
# default handler for invalid filter values
|
85
|
+
def render_invalid_filter_value(exception)
|
86
|
+
render json: { error: exception.message }, status: :bad_request
|
87
|
+
end
|
88
|
+
|
89
|
+
def render_invalid_filter_relation(exception)
|
90
|
+
render json: { error: exception.message }, status: :bad_request
|
91
|
+
end
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
Request examples:
|
96
|
+
```
|
97
|
+
GET /users?age=18 # returns users with age 18
|
98
|
+
GET /users?age=not_an_integer
|
99
|
+
# returns a 400 (Bad Request), with the message "'not_an_integer' cannot be casted to integer"
|
100
|
+
```
|
101
|
+
|
102
|
+
## Referencing tables in the filterable fields
|
103
|
+
|
104
|
+
The `filterable_by` method can also be used to reference fields in associated tables. For example, to filter the users by the name of the company they work for, the following can be done:
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
class UsersController < ApplicationController
|
108
|
+
include Warped::Controllers::Filterable
|
109
|
+
|
110
|
+
filterable_by :name, 'companies.name', "companies.created_at" => { kind: :date_time }
|
111
|
+
|
112
|
+
def index
|
113
|
+
users = filter(User.joins(:company))
|
114
|
+
render json: users
|
115
|
+
end
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
Request examples:
|
120
|
+
```
|
121
|
+
GET /users?name=John
|
122
|
+
GET /users?companies.name=Acme
|
123
|
+
GET /users?companies.created_at=2021-01-01T00:00:00
|
124
|
+
```
|
125
|
+
|
126
|
+
## Renaming the filter query parameters
|
127
|
+
|
128
|
+
If you don't want to use the field name as the query parameter (as to not expose the database schema, or when joining the same table multiple times),
|
129
|
+
you can specify the query parameter to use for each field:
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
class UsersController < ApplicationController
|
133
|
+
include Warped::Controllers::Filterable
|
134
|
+
|
135
|
+
filterable_by 'companies.name' => { kind: :string, alias_name: :company_name },
|
136
|
+
'users.name' => { kind: :string, alias_name: :user_name }
|
137
|
+
|
138
|
+
|
139
|
+
def index
|
140
|
+
users = filter(User.join(:company))
|
141
|
+
render json: users
|
142
|
+
end
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
Request examples:
|
147
|
+
```
|
148
|
+
GET /users?user_name=John
|
149
|
+
GET /users?company_name=Acme
|
150
|
+
```
|
151
|
+
|
152
|
+
## Using filters other than `eq`
|
153
|
+
|
154
|
+
By default, the `filter` method will use the `eq` filter method to filter the records. If you want to use a different filter method, you can specify the filter "relation" in the query parameter:
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
class UsersController < ApplicationController
|
158
|
+
include Warped::Controllers::Filterable
|
159
|
+
|
160
|
+
filterable_by name: { kind: :string }, age: { kind: :integer }
|
161
|
+
|
162
|
+
def index
|
163
|
+
users = filter(User.all)
|
164
|
+
render json: users
|
165
|
+
end
|
166
|
+
end
|
167
|
+
```
|
168
|
+
|
169
|
+
Request examples:
|
170
|
+
```
|
171
|
+
GET /users?name=John # returns users with name John
|
172
|
+
GET /users?name[]=John&name[]=Jane # returns users where the name is in ('John', 'Jane')
|
173
|
+
GET /users?age.rel=is_null # returns users where the age is null
|
174
|
+
GET /users?age.rel=is_not_null # returns users where the age is not null
|
175
|
+
GET /users?age.rel=between&age[]=18&age[]=30 # returns users with age between 18 and 30
|
176
|
+
GET /users?age.rel=gt&age=18 # returns users with age greater than 18
|
177
|
+
```
|
178
|
+
|
179
|
+
The full list of filter relations is:
|
180
|
+
- `eq` (default) - equals
|
181
|
+
- `neq` - not equals
|
182
|
+
- `gt` - greater than
|
183
|
+
- `gte` - greater than or equals
|
184
|
+
- `lt` - less than
|
185
|
+
- `lte` - less than or equals
|
186
|
+
- `between` - between (requires two values)
|
187
|
+
- `in` - in (default when multiple values are provided)
|
188
|
+
- `not_in` - not in (requires multiple values)
|
189
|
+
- `starts_with` - starts with
|
190
|
+
- `ends_with` - ends with
|
191
|
+
- `contains` - contains
|
192
|
+
- `is_null` - is null (does not require a value)
|
193
|
+
- `is_not_null` - is not null (does not require a value)
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# Warped::Controllers::Pageable
|
2
|
+
|
3
|
+
The `Pageable` concern provides a method to paginate the records in a controller's action.
|
4
|
+
|
5
|
+
The method `paginate` is used to paginate the records.
|
6
|
+
It will use the query parameters `page` and `per_page` to paginate the records.
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
class UsersController < ApplicationController
|
10
|
+
include Warped::Controllers::Pageable
|
11
|
+
|
12
|
+
def index
|
13
|
+
users = paginate(User.all)
|
14
|
+
render json: users, meta: pagination
|
15
|
+
end
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
Request examples:
|
20
|
+
```
|
21
|
+
GET /users?page=1&per_page=10 # returns the first page of users with 10 records per page
|
22
|
+
GET /users?per_page=25 # returns the first page of users with 25 records per page
|
23
|
+
GET /users?page=2&per_page=25 # returns the second page of users with 25 records per page
|
24
|
+
```
|
25
|
+
|
26
|
+
## Accessing the pagination information
|
27
|
+
|
28
|
+
The `pagination` method can be used to access the pagination information.
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
class UsersController < ApplicationController
|
32
|
+
include Warped::Controllers::Pageable
|
33
|
+
|
34
|
+
def index
|
35
|
+
users = paginate(User.all)
|
36
|
+
render json: users, meta: pagination
|
37
|
+
end
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
`pagination` returns a hash with
|
42
|
+
- `page` - the current page
|
43
|
+
- `per_page` - the number of records per page
|
44
|
+
- `total_pages` - the total number of pages
|
45
|
+
- `total_count` - the number of records in the scope
|
46
|
+
- `next-page` - the next page number
|
47
|
+
- `prev-page` - the previous page number
|
48
|
+
|
49
|
+
|
50
|
+
## Customizing the pagination behavior
|
51
|
+
|
52
|
+
By default, the `paginate` method will paginate the scope in pages of size 10, and will return the first page if the `page` query parameter is not provided.
|
53
|
+
|
54
|
+
Additionally, there's a limit of `100` records per page. So, if the `per_page` query parameter is greater than `100`, the pagination will use `100` as the page size.
|
55
|
+
|
56
|
+
You can customize the default page size and the default page number by overriding the `default_per_page` value in the controller.
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
class UsersController < ApplicationController
|
60
|
+
include Warped::Controllers::Pageable
|
61
|
+
|
62
|
+
# This will set the default page size to 25 when the `per_page` query parameter is not provided
|
63
|
+
self.default_per_page = 25
|
64
|
+
|
65
|
+
def index
|
66
|
+
users = paginate(User.all)
|
67
|
+
render json: users, meta: pagination
|
68
|
+
end
|
69
|
+
end
|
70
|
+
```
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# Warped::Controllers
|
2
|
+
|
3
|
+
The `Warped::Controllers` module defines five concerns that can be included in a controller to provide additional functionality:
|
4
|
+
- [Warped::Controllers::Filterable](FILTERABLE.md)
|
5
|
+
- [Warped::Controllers::Searchable](SEARCHABLE.md)
|
6
|
+
- [Warped::Controllers::Sortable](SORTABLE.md)
|
7
|
+
- [Warped::Controllers::Pageable](PAGEABLE.md)
|
8
|
+
- [Warped::Controllers::Tabulatable](TABULATABLE.md)
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# Warped::Controllers::Searchable
|
2
|
+
|
3
|
+
The `Searchable` concern provides a method to search the records in a controller's action.
|
4
|
+
|
5
|
+
By default it calls the scope `search` on the model, and uses the query parameter `q` to search the records.
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
# app/models/user.rb
|
9
|
+
class User < ApplicationRecord
|
10
|
+
# You can define your own search scope and use standard sql
|
11
|
+
# scope :search, ->(query) { where('name LIKE ?', "%#{query}%") }
|
12
|
+
|
13
|
+
# Or use pg_search
|
14
|
+
include PgSearch::Model
|
15
|
+
pg_search_scope :search, against: [:name, :email]
|
16
|
+
end
|
17
|
+
|
18
|
+
# app/controllers/users_controller.rb
|
19
|
+
class UsersController < ApplicationController
|
20
|
+
include Warped::Controllers::Searchable
|
21
|
+
|
22
|
+
def index
|
23
|
+
users = search(User.all)
|
24
|
+
render json: users
|
25
|
+
end
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
Request examples:
|
30
|
+
```
|
31
|
+
GET /users?q=John
|
32
|
+
# calls #search(User.all, search_term: 'John', model_search_scope: :search) in the controller
|
33
|
+
```
|
34
|
+
|
35
|
+
## Customizing the search query parameter
|
36
|
+
|
37
|
+
You can customize the default query parameter by:
|
38
|
+
1. Passing fetching the fetch term from the params hash, and passing it directly to the search method:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
class UsersController < ApplicationController
|
42
|
+
include Warped::Controllers::Searchable
|
43
|
+
|
44
|
+
def index
|
45
|
+
# This will use the query parameter `term` instead of `q`
|
46
|
+
users = search(User.all, search_term: params[:term])
|
47
|
+
render json: users
|
48
|
+
end
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
2. Overriding the `search_param` method in the controller
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
class UsersController < ApplicationController
|
56
|
+
include Warped::Controllers::Searchable
|
57
|
+
|
58
|
+
def index
|
59
|
+
# This will use the query parameter `term` instead of `q`
|
60
|
+
users = search(User.all)
|
61
|
+
render json: users
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def search_param
|
67
|
+
:term
|
68
|
+
end
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
72
|
+
3. Calling #searchable_by in the controller and overriding the default query parameter
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
# app/models/user.rb
|
76
|
+
class User < ApplicationRecord
|
77
|
+
include PgSearch::Model
|
78
|
+
pg_search_scope :search_by_word, against: :name, using: { tsearch: { any_word: true } }
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
# app/controllers/users_controller.rb
|
83
|
+
class UsersController < ApplicationController
|
84
|
+
include Warped::Controllers::Searchable
|
85
|
+
|
86
|
+
# This will use the query parameter `term` instead of `q`
|
87
|
+
# and the search scope `search_by_word` instead of the default
|
88
|
+
searchable_by :search_by_word, param: :term
|
89
|
+
|
90
|
+
def index
|
91
|
+
users = search(User.all)
|
92
|
+
render json: users
|
93
|
+
end
|
94
|
+
end
|
95
|
+
```
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# Warped::Controllers::Sortable
|
2
|
+
|
3
|
+
The `Sortable` concern provides a method to sort the records in a controller's action.
|
4
|
+
|
5
|
+
The method `sortable_by` is used to define the sortable fields.
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
class UsersController < ApplicationController
|
9
|
+
include Warped::Controllers::Sortable
|
10
|
+
|
11
|
+
sortable_by :name, :created_at
|
12
|
+
|
13
|
+
def index
|
14
|
+
users = sort(User.all)
|
15
|
+
render json: users
|
16
|
+
end
|
17
|
+
end
|
18
|
+
```
|
19
|
+
|
20
|
+
This will use the query parameter `sort_key` and `sort_direction` to sort the records.
|
21
|
+
- The default sort direction is `desc`.
|
22
|
+
- The default sort key is `:id`.
|
23
|
+
|
24
|
+
Example requests:
|
25
|
+
```
|
26
|
+
GET /users?sort_key=name # sort by name in descending order
|
27
|
+
GET /users?sort_key=created_at&sort_direction=asc # sort by created_at in ascending order
|
28
|
+
```
|
29
|
+
|
30
|
+
When calling sort in a controller action, and the sort parameters are not provided, the default sort key and direction will be used.
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
class UsersController < ApplicationController
|
34
|
+
include Warped::Controllers::Sortable
|
35
|
+
|
36
|
+
sortable_by :name, :created_at
|
37
|
+
|
38
|
+
def index
|
39
|
+
users = sort(User.all)
|
40
|
+
render json: users
|
41
|
+
end
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
Request examples:
|
46
|
+
```
|
47
|
+
GET /users # sort by id in descending order
|
48
|
+
```
|
49
|
+
## Referencing tables in the sortable fields
|
50
|
+
|
51
|
+
Like the `filterable_by` method, the `sortable_by` method can also be used to reference fields in associated tables.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
class UsersController < ApplicationController
|
55
|
+
include Warped::Controllers::Sortable
|
56
|
+
|
57
|
+
sortable_by :name, 'companies.name'
|
58
|
+
|
59
|
+
def index
|
60
|
+
users = sort(User.joins(:company))
|
61
|
+
render json: users
|
62
|
+
end
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
Request examples:
|
67
|
+
```
|
68
|
+
GET /users?sort_key=name # sort by name in descending order
|
69
|
+
GET /users?sort_key=companies.name&sort_direction=asc # sort by company name in ascending order
|
70
|
+
```
|
71
|
+
|
72
|
+
## Renaming the sort query parameters
|
73
|
+
|
74
|
+
If you don't want to use the field name as the query parameter (as to not expose the database schema, or when joining the same table multiple times),
|
75
|
+
you can specify the query parameter to use for each field:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
class UsersController < ApplicationController
|
79
|
+
include Warped::Controllers::Sortable
|
80
|
+
|
81
|
+
sortable_by 'companies.name' => :company_name, 'users.name' => :user_name
|
82
|
+
|
83
|
+
def index
|
84
|
+
users = sort(User.join(:company))
|
85
|
+
render json: users
|
86
|
+
end
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
Request examples:
|
91
|
+
```
|
92
|
+
GET /users?sort_key=user_name # sort by name in descending order
|
93
|
+
GET /users?sort_key=company_name&sort_direction=asc # sort by company name in ascending order
|
94
|
+
```
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Warped::Controllers::Tabulatable
|
2
|
+
|
3
|
+
The `Tabulatable` concern provides a method to filter, sort, search, and paginate the records in a controller's action.
|
4
|
+
|
5
|
+
The method `tabulate` is used to filter, sort, search, and paginate the records. So, in the case that the controller action needs to filter, sort, search, and paginate the records, the `tabulate` method can be used.
|
6
|
+
|
7
|
+
The tabulatable concern provides the `tabulatable_by` method, which passes the values to `filterable_by` and `sortable_by`.
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
class UsersController < ApplicationController
|
11
|
+
include Warped::Controllers::Tabulatable
|
12
|
+
|
13
|
+
tabulatable_by :name, :email, :created_at
|
14
|
+
|
15
|
+
def index
|
16
|
+
users = tabulate(User.all)
|
17
|
+
render json: users, meta: pagination
|
18
|
+
end
|
19
|
+
end
|
20
|
+
```
|
21
|
+
|
22
|
+
Request examples:
|
23
|
+
```
|
24
|
+
GET /users?age[]=18&age[]=30&age.rel=between&sort_key=name&sort_direction=asc&q=John&page=2&per_page=10
|
25
|
+
# returns the second page of users with 10 records per page, where the age is between 18 and 30, sorted by name in ascending order, and searched by the term John
|
26
|
+
```
|
27
|
+
|
28
|
+
Just like `paginate`, when calling the `tabulate` method in the controller action, the `pagination` method can be used to access the pagination information.
|