datagrid 1.8.2 → 2.0.0.pre.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -1
- data/{Readme.markdown → README.md} +44 -27
- data/app/assets/stylesheets/datagrid.css +145 -0
- data/app/views/datagrid/_enum_checkboxes.html.erb +5 -3
- data/app/views/datagrid/_form.html.erb +4 -4
- data/app/views/datagrid/_head.html.erb +26 -3
- data/app/views/datagrid/_range_filter.html.erb +5 -3
- data/app/views/datagrid/_row.html.erb +12 -1
- data/app/views/datagrid/_table.html.erb +4 -4
- data/datagrid.gemspec +6 -6
- data/lib/datagrid/active_model.rb +9 -17
- data/lib/datagrid/base.rb +39 -0
- data/lib/datagrid/column_names_attribute.rb +9 -11
- data/lib/datagrid/columns/column.rb +155 -133
- data/lib/datagrid/columns.rb +254 -45
- data/lib/datagrid/configuration.rb +23 -10
- data/lib/datagrid/core.rb +89 -54
- data/lib/datagrid/deprecated_object.rb +20 -0
- data/lib/datagrid/drivers/abstract_driver.rb +12 -23
- data/lib/datagrid/drivers/active_record.rb +24 -26
- data/lib/datagrid/drivers/array.rb +22 -14
- data/lib/datagrid/drivers/mongo_mapper.rb +15 -14
- data/lib/datagrid/drivers/mongoid.rb +15 -17
- data/lib/datagrid/drivers/sequel.rb +14 -19
- data/lib/datagrid/drivers.rb +2 -1
- data/lib/datagrid/engine.rb +11 -3
- data/lib/datagrid/filters/base_filter.rb +166 -142
- data/lib/datagrid/filters/boolean_filter.rb +19 -5
- data/lib/datagrid/filters/date_filter.rb +33 -35
- data/lib/datagrid/filters/date_time_filter.rb +24 -16
- data/lib/datagrid/filters/default_filter.rb +9 -3
- data/lib/datagrid/filters/dynamic_filter.rb +151 -105
- data/lib/datagrid/filters/enum_filter.rb +43 -19
- data/lib/datagrid/filters/extended_boolean_filter.rb +39 -30
- data/lib/datagrid/filters/float_filter.rb +16 -5
- data/lib/datagrid/filters/integer_filter.rb +21 -10
- data/lib/datagrid/filters/ranged_filter.rb +66 -45
- data/lib/datagrid/filters/select_options.rb +58 -49
- data/lib/datagrid/filters/string_filter.rb +9 -4
- data/lib/datagrid/filters.rb +190 -57
- data/lib/datagrid/form_builder.rb +116 -128
- data/lib/datagrid/generators/scaffold.rb +185 -0
- data/lib/datagrid/generators/views.rb +20 -0
- data/lib/datagrid/helper.rb +397 -22
- data/lib/datagrid/ordering.rb +26 -29
- data/lib/datagrid/rspec.rb +6 -10
- data/lib/datagrid/utils.rb +37 -30
- data/lib/datagrid/version.rb +3 -1
- data/lib/datagrid.rb +18 -28
- data/templates/base.rb.erb +6 -4
- data/templates/grid.rb.erb +1 -1
- metadata +15 -16
- data/app/assets/stylesheets/datagrid.sass +0 -134
- data/lib/datagrid/filters/composite_filters.rb +0 -49
- data/lib/datagrid/renderer.rb +0 -157
- data/lib/datagrid/scaffold.rb +0 -129
- data/lib/tasks/datagrid_tasks.rake +0 -15
- data/templates/controller.rb.erb +0 -6
- data/templates/index.html.erb +0 -5
data/lib/datagrid/helper.rb
CHANGED
@@ -1,9 +1,245 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "action_view"
|
3
4
|
|
4
5
|
module Datagrid
|
6
|
+
# # Datagrid Frontend Guide
|
7
|
+
#
|
8
|
+
# ## Description
|
9
|
+
#
|
10
|
+
# The easiest way to start with Datagrid frontend is by using the generator:
|
11
|
+
#
|
12
|
+
# ``` sh
|
13
|
+
# rails generate datagrid:scaffold users
|
14
|
+
# ```
|
15
|
+
#
|
16
|
+
# This command builds the controller, view, route, and adds
|
17
|
+
# [built-in CSS](https://github.com/bogdan/datagrid/blob/master/app/assets/stylesheets/datagrid.sass).
|
18
|
+
#
|
19
|
+
# Datagrid includes helpers and a form builder for easy frontend generation.
|
20
|
+
# If you need a fully-featured custom GUI, create your templates manually with the help of the {Datagrid::Columns} API.
|
21
|
+
#
|
22
|
+
# ## Controller and Routing
|
23
|
+
#
|
24
|
+
# Grids usually implement the `index` action of a Rails REST resource. Here's an example:
|
25
|
+
#
|
26
|
+
# resources :models, only: [:index]
|
27
|
+
#
|
28
|
+
# Use the `GET` method in the form, and the controller becomes straightforward:
|
29
|
+
#
|
30
|
+
# class ModelsController < ApplicationController
|
31
|
+
# def index
|
32
|
+
# @grid = ModelsGrid.new(params[:my_report]) do |scope|
|
33
|
+
# scope.page(params[:page]) # See pagination section
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# To apply additional scoping conditions, such as visibility based on the current user:
|
39
|
+
#
|
40
|
+
# ModelsGrid.new(params[:my_report]) do |scope|
|
41
|
+
# scope.where(owner_id: current_user.id).page(params[:page])
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# To pass an object to a grid instance, define it as an accessible attribute:
|
45
|
+
#
|
46
|
+
# class ModelsGrid
|
47
|
+
# attr_accessor :current_user
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# Then pass it when initializing the grid:
|
51
|
+
#
|
52
|
+
# ModelsGrid.new(params[:models_grid].merge(current_user: current_user))
|
53
|
+
#
|
54
|
+
# ## Form Builder
|
55
|
+
#
|
56
|
+
# ### Basic Method
|
57
|
+
#
|
58
|
+
# Use the built-in partial:
|
59
|
+
#
|
60
|
+
# = datagrid_form_with model: @grid, url: report_path, other_form_for_option: value
|
61
|
+
#
|
62
|
+
# {#datagrid_form_with} supports the same options as Rails `form_with`.
|
63
|
+
#
|
64
|
+
# ### Advanced Method
|
65
|
+
#
|
66
|
+
# You can use Rails built-in tools to create a form. Additionally, Datagrid provides helpers to generate input/select elements for filters:
|
67
|
+
#
|
68
|
+
# ``` haml
|
69
|
+
# - form_with model: UserGrid.new, method: :get, url: users_path do |f|
|
70
|
+
# %div
|
71
|
+
# = f.datagrid_label :name
|
72
|
+
# = f.datagrid_filter :name # => <input name="grid[name]" type="text"/>
|
73
|
+
# %div
|
74
|
+
# = f.datagrid_label :category_id
|
75
|
+
# = f.datagrid_filter :category_id # => <select name="grid[category_id]">....</select>
|
76
|
+
# ```
|
77
|
+
#
|
78
|
+
# For more flexibility, use Rails default helpers:
|
79
|
+
#
|
80
|
+
# %div
|
81
|
+
# = f.label :name
|
82
|
+
# = f.text_field :name
|
83
|
+
#
|
84
|
+
# See the localization section of {Datagrid::Filters}.
|
85
|
+
#
|
86
|
+
# ## Datagrid Table
|
87
|
+
#
|
88
|
+
# Use the helper to display a report:
|
89
|
+
#
|
90
|
+
# %div== Total #{@grid.assets.total}
|
91
|
+
# = datagrid_table(@report)
|
92
|
+
# = will_paginate @report.assets
|
93
|
+
#
|
94
|
+
# Options:
|
95
|
+
#
|
96
|
+
# - `:html` - Attributes for the `<table>` tag.
|
97
|
+
# - `:order` - Set to `false` to disable ordering controls (default: `true`).
|
98
|
+
# - `:columns` - Specify an array of column names to display.
|
99
|
+
#
|
100
|
+
# ## Pagination
|
101
|
+
#
|
102
|
+
# Datagrid is abstracted from pagination but integrates seamlessly with tools like Kaminari, WillPaginate, or Pagy:
|
103
|
+
#
|
104
|
+
# # Kaminari
|
105
|
+
# @grid = MyGrid.new(params[:grid]) do |scope|
|
106
|
+
# scope.page(params[:page]).per(10)
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# # WillPaginate
|
110
|
+
# @grid = MyGrid.new(params[:grid]) do |scope|
|
111
|
+
# scope.page(params[:page]).per_page(10)
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# # Pagy
|
115
|
+
# @grid = MyGrid.new(params[:grid])
|
116
|
+
# @pagy, @records = pagy(@grid.assets)
|
117
|
+
#
|
118
|
+
# Render the paginated collection:
|
119
|
+
#
|
120
|
+
# # WillPaginate or Kaminari
|
121
|
+
# <%= datagrid_table(@grid, options) %>
|
122
|
+
# # Pagy
|
123
|
+
# <%= datagrid_table(@grid, @records, options) %>
|
124
|
+
#
|
125
|
+
# ## CSV Export
|
126
|
+
#
|
127
|
+
# Add CSV support to your controller:
|
128
|
+
#
|
129
|
+
# class UsersController < ApplicationController
|
130
|
+
# def index
|
131
|
+
# @grid = UsersGrid.new(params[:users_grid])
|
132
|
+
# respond_to do |f|
|
133
|
+
# f.html { @grid.scope { |scope| scope.page(params[:page]) } }
|
134
|
+
# f.csv do
|
135
|
+
# send_data @grid.to_csv, type: "text/csv", disposition: 'inline', filename: "grid-#{Time.now.to_s}.csv"
|
136
|
+
# end
|
137
|
+
# end
|
138
|
+
# end
|
139
|
+
# end
|
140
|
+
#
|
141
|
+
# Add a button in your interface:
|
142
|
+
#
|
143
|
+
# link_to "Get CSV", url_for(format: 'csv', users_grid: params[:users_grid])
|
144
|
+
#
|
145
|
+
# ## AJAX
|
146
|
+
#
|
147
|
+
# Datagrid supports asynchronous data loading. Add this to your controller:
|
148
|
+
#
|
149
|
+
# if request.xhr?
|
150
|
+
# render json: {table: view_context.datagrid_table(@grid)}
|
151
|
+
# end
|
152
|
+
#
|
153
|
+
# Modify the form for AJAX:
|
154
|
+
#
|
155
|
+
# = datagrid_form_with model: @grid, html: {class: 'js-datagrid-form'}
|
156
|
+
# .js-datagrid-table
|
157
|
+
# = datagrid_table @grid
|
158
|
+
# .js-pagination
|
159
|
+
# = paginate @grid.assets
|
160
|
+
# :javascript
|
161
|
+
# $('.js-datagrid-form').submit(function(event) {
|
162
|
+
# event.preventDefault();
|
163
|
+
# $.get($(this).attr("action"), $(this).serialize(), function (data) {
|
164
|
+
# $('.js-datagrid-table').html(data.table);
|
165
|
+
# });
|
166
|
+
# });
|
167
|
+
#
|
168
|
+
# ## Modifying Built-In Partials
|
169
|
+
#
|
170
|
+
# To customize Datagrid views:
|
171
|
+
#
|
172
|
+
# rake datagrid:copy_partials
|
173
|
+
#
|
174
|
+
# This creates files in `app/views/datagrid/`, which you can modify to suit your needs:
|
175
|
+
#
|
176
|
+
# app/views/datagrid/
|
177
|
+
# ├── _enum_checkboxes.html.erb # datagrid_filter for filter(name, :enum, checkboxes: true)
|
178
|
+
# ├── _form.html.erb # datagrid_form_with
|
179
|
+
# ├── _head.html.erb # datagrid_header
|
180
|
+
# ├── _range_filter.html.erb # datagrid_filter for filter(name, type, range: true)
|
181
|
+
# ├── _row.html.erb # datagrid_rows/datagrid_rows
|
182
|
+
# └── _table.html.erb # datagrid_table
|
183
|
+
#
|
184
|
+
# ## Custom Options
|
185
|
+
#
|
186
|
+
# You can add custom options to Datagrid columns and filters and implement their support on the frontend.
|
187
|
+
# For example, you might want to add a `description` option to a column that appears as a tooltip on mouseover.
|
188
|
+
#
|
189
|
+
# column(
|
190
|
+
# :aov, header: 'AOV',
|
191
|
+
# description: 'Average order value: sum of orders subtotal divided by their count'
|
192
|
+
# ) do |category|
|
193
|
+
# category.orders.sum(:subtotal) / category.orders.count
|
194
|
+
# end
|
195
|
+
#
|
196
|
+
# The `:description` option is not built into Datagrid, but you can implement it
|
197
|
+
# by adding the following to partial `app/views/datagrid/_header.html.erb`:
|
198
|
+
#
|
199
|
+
# <% if column.options[:description] %>
|
200
|
+
# <a data-toggle="tooltip" title="<%= column.options[:description] %>">
|
201
|
+
# <i class="icon-question-sign"></i>
|
202
|
+
# </a>
|
203
|
+
# <% end %>
|
204
|
+
#
|
205
|
+
# This modification allows the `:description` tooltip to work with your chosen UI and JavaScript library.
|
206
|
+
# The same technique can be applied to filters by calling `filter.options` in corresponding partials.
|
207
|
+
#
|
208
|
+
# ## Highlight Rows
|
209
|
+
#
|
210
|
+
# To add custom HTML classes to each row for styling, modify the `_row.html.erb` partial:
|
211
|
+
#
|
212
|
+
# ``` diff
|
213
|
+
# -<tr>
|
214
|
+
# +<tr class="<%= grid.respond_to?(:row_class) ? grid.row_class(asset) : "" %>">
|
215
|
+
# <% grid.html_columns(*options[:columns]).each do |column| %>
|
216
|
+
# <td class="<%= datagrid_column_classes(grid, column) %>">
|
217
|
+
# <%= datagrid_value(grid, column, asset) %>
|
218
|
+
# </td>
|
219
|
+
# <% end %>
|
220
|
+
# ```
|
221
|
+
#
|
222
|
+
# This allows you to define a custom `row_class` method in your grid class, like this:
|
223
|
+
#
|
224
|
+
# class IssuesGrid < ApplicationGrid
|
225
|
+
# scope { Issue }
|
226
|
+
#
|
227
|
+
# def row_class(issue)
|
228
|
+
# case issue.status
|
229
|
+
# when "fixed" then "green"
|
230
|
+
# when "rejected" then "red"
|
231
|
+
# else "blue"
|
232
|
+
# end
|
233
|
+
# end
|
234
|
+
# end
|
235
|
+
#
|
236
|
+
# ## Localization
|
237
|
+
#
|
238
|
+
# You can overwrite Datagrid’s custom localization keys at the application level.
|
239
|
+
# See the localization keys here:
|
240
|
+
#
|
241
|
+
# https://github.com/bogdan/datagrid/blob/master/lib/datagrid/locale/en.yml
|
5
242
|
module Helper
|
6
|
-
|
7
243
|
# @param grid [Datagrid] grid object
|
8
244
|
# @param column [Datagrid::Columns::Column, String, Symbol] column name
|
9
245
|
# @param model [Object] an object from grid scope
|
@@ -15,7 +251,9 @@ module Datagrid
|
|
15
251
|
# <% end %>
|
16
252
|
# </ul>
|
17
253
|
def datagrid_value(grid, column, model)
|
18
|
-
|
254
|
+
column = grid.column_by_name(column) if column.is_a?(String) || column.is_a?(Symbol)
|
255
|
+
|
256
|
+
grid.html_value(column, self, model)
|
19
257
|
end
|
20
258
|
|
21
259
|
# @!visibility private
|
@@ -38,12 +276,20 @@ module Datagrid
|
|
38
276
|
# Default: 'datagrid'.
|
39
277
|
# @param grid [Datagrid] grid object
|
40
278
|
# @param assets [Array] objects from grid scope
|
279
|
+
# @param [Hash{Symbol => Object}] options HTML attributes to be passed to `<table>` tag
|
41
280
|
# @return [String] table tag HTML markup
|
42
281
|
# @example
|
43
282
|
# assets = grid.assets.page(params[:page])
|
44
283
|
# datagrid_table(grid, assets, options)
|
45
284
|
def datagrid_table(grid, assets = grid.assets, **options)
|
46
|
-
|
285
|
+
_render_partial(
|
286
|
+
"table", options[:partials],
|
287
|
+
{
|
288
|
+
grid: grid,
|
289
|
+
options: options,
|
290
|
+
assets: assets,
|
291
|
+
},
|
292
|
+
)
|
47
293
|
end
|
48
294
|
|
49
295
|
# Renders HTML table header for given grid instance using columns defined in it
|
@@ -58,11 +304,19 @@ module Datagrid
|
|
58
304
|
# * <tt>:partials</tt> - Path for partials lookup.
|
59
305
|
# Default: 'datagrid'.
|
60
306
|
# @param grid [Datagrid] grid object
|
307
|
+
# @param [Object] opts (deprecated) pass keyword arguments instead
|
308
|
+
# @param [Hash] options
|
61
309
|
# @return [String] HTML table header tag markup
|
62
|
-
def datagrid_header(grid,
|
63
|
-
|
64
|
-
|
310
|
+
def datagrid_header(grid, opts = :__unspecified__, **options)
|
311
|
+
unless opts == :__unspecified__
|
312
|
+
Datagrid::Utils.warn_once("datagrid_header now requires ** operator when passing options.")
|
313
|
+
options.reverse_merge!(opts)
|
314
|
+
end
|
315
|
+
options[:order] = true unless options.key?(:order)
|
65
316
|
|
317
|
+
_render_partial("head", options[:partials],
|
318
|
+
{ grid: grid, options: options },)
|
319
|
+
end
|
66
320
|
|
67
321
|
# Renders HTML table rows using given grid definition using columns defined in it.
|
68
322
|
# Allows to provide a custom layout for each for in place with a block
|
@@ -75,6 +329,7 @@ module Datagrid
|
|
75
329
|
# * <tt>:partials</tt> - Path for partials lookup.
|
76
330
|
# Default: 'datagrid'.
|
77
331
|
#
|
332
|
+
# @return [String]
|
78
333
|
# @example
|
79
334
|
# = datagrid_rows(grid) # Generic table rows Layout
|
80
335
|
#
|
@@ -83,30 +338,73 @@ module Datagrid
|
|
83
338
|
# %td= row.project_name
|
84
339
|
# %td.project-status{class: row.status}= row.status
|
85
340
|
def datagrid_rows(grid, assets = grid.assets, **options, &block)
|
86
|
-
|
341
|
+
safe_join(
|
342
|
+
assets.map do |asset|
|
343
|
+
datagrid_row(grid, asset, **options, &block)
|
344
|
+
end.to_a,
|
345
|
+
)
|
87
346
|
end
|
88
347
|
|
89
|
-
#
|
348
|
+
# @return [String] renders ordering controls for the given column name
|
90
349
|
#
|
91
350
|
# Supported options:
|
92
351
|
#
|
93
352
|
# * <tt>:partials</tt> - Path for partials lookup.
|
94
353
|
# Default: 'datagrid'.
|
95
354
|
def datagrid_order_for(grid, column, options = {})
|
96
|
-
|
355
|
+
Datagrid::Utils.warn_once(<<~MSG)
|
356
|
+
datagrid_order_for is deprecated.
|
357
|
+
Put necessary code inline inside datagrid/head partial.
|
358
|
+
See built-in partial for example.
|
359
|
+
MSG
|
360
|
+
_render_partial("order_for", options[:partials],
|
361
|
+
{ grid: grid, column: column },)
|
362
|
+
end
|
363
|
+
|
364
|
+
# Renders HTML for grid with all filters inputs and labels defined in it
|
365
|
+
#
|
366
|
+
# Supported options:
|
367
|
+
#
|
368
|
+
# * <tt>:partials</tt> - Path for form partial lookup.
|
369
|
+
# Default: 'datagrid' results in using `app/views/datagrid/` partials.
|
370
|
+
# Example: 'datagrid_admin' results in using `app/views/datagrid_admin` partials.
|
371
|
+
# * <tt>:model</tt> - Datagrid object to be rendedred.
|
372
|
+
# * All options supported by Rails <tt>form_with</tt> helper
|
373
|
+
# @param grid [Datagrid] grid object
|
374
|
+
# @param [Hash{Symbol => Object}] options
|
375
|
+
# @return [String] form HTML tag markup
|
376
|
+
def datagrid_form_with(**options)
|
377
|
+
raise ArgumentError, "datagrid_form_with block argument is invalid. Use form_with instead." if block_given?
|
378
|
+
|
379
|
+
grid = options[:model]
|
380
|
+
raise ArgumentError, "Grid has no available filters" if grid&.filters&.empty?
|
381
|
+
|
382
|
+
_render_partial("form", options[:partials], { grid: options[:model], options: options })
|
97
383
|
end
|
98
384
|
|
99
|
-
# Renders HTML for
|
385
|
+
# Renders HTML for grid with all filters inputs and labels defined in it
|
100
386
|
#
|
101
387
|
# Supported options:
|
102
388
|
#
|
103
389
|
# * <tt>:partials</tt> - Path for form partial lookup.
|
104
390
|
# Default: 'datagrid'.
|
105
|
-
# * All options supported by Rails <tt>
|
391
|
+
# * All options supported by Rails <tt>form_with</tt> helper
|
392
|
+
# @deprecated Use {#datagrid_form_with} instead.
|
106
393
|
# @param grid [Datagrid] grid object
|
394
|
+
# @param [Hash] options
|
107
395
|
# @return [String] form HTML tag markup
|
108
396
|
def datagrid_form_for(grid, options = {})
|
109
|
-
|
397
|
+
Datagrid::Utils.warn_once("datagrid_form_for is deprecated if favor of datagrid_form_with.")
|
398
|
+
_render_partial(
|
399
|
+
"form", options[:partials],
|
400
|
+
grid: grid,
|
401
|
+
options: {
|
402
|
+
method: :get,
|
403
|
+
as: grid.param_name,
|
404
|
+
local: true,
|
405
|
+
**options,
|
406
|
+
},
|
407
|
+
)
|
110
408
|
end
|
111
409
|
|
112
410
|
# Provides access to datagrid columns data.
|
@@ -114,6 +412,7 @@ module Datagrid
|
|
114
412
|
# @param grid [Datagrid] grid object
|
115
413
|
# @param asset [Object] object from grid scope
|
116
414
|
# @param block [Proc] block with Datagrid::Helper::HtmlRow as an argument returning a HTML markup as a String
|
415
|
+
# @param [Hash{Symbol => Object}] options
|
117
416
|
# @return [Datagrid::Helper::HtmlRow, String] captured HTML markup if block given otherwise row object
|
118
417
|
# @example
|
119
418
|
# # Suppose that grid has first_name and last_name columns
|
@@ -130,29 +429,105 @@ module Datagrid
|
|
130
429
|
# @example
|
131
430
|
# <%= datagrid_row(grid, user, columns: [:first_name, :last_name, :actions]) %>
|
132
431
|
def datagrid_row(grid, asset, **options, &block)
|
133
|
-
|
432
|
+
Datagrid::Helper::HtmlRow.new(self, grid, asset, options).tap do |row|
|
433
|
+
return capture(row, &block) if block_given?
|
434
|
+
end
|
134
435
|
end
|
135
436
|
|
136
437
|
# Generates an ascending or descending order url for the given column
|
137
438
|
# @param grid [Datagrid] grid object
|
138
439
|
# @param column [Datagrid::Columns::Column, String, Symbol] column name
|
139
|
-
# @param descending [Boolean]
|
440
|
+
# @param descending [Boolean] order direction, descending if true, otherwise ascending.
|
140
441
|
# @return [String] order layout HTML markup
|
141
442
|
def datagrid_order_path(grid, column, descending)
|
142
|
-
|
443
|
+
column = grid.column_by_name(column)
|
444
|
+
query = request&.query_parameters || {}
|
445
|
+
ActionDispatch::Http::URL.path_for(
|
446
|
+
path: request&.path || "/",
|
447
|
+
params: query.merge(grid.query_params(order: column.name, descending: descending)),
|
448
|
+
)
|
143
449
|
end
|
144
450
|
|
451
|
+
# @!visibility private
|
452
|
+
def datagrid_column_classes(grid, column)
|
453
|
+
Datagrid::Utils.warn_once(<<~MSG)
|
454
|
+
datagrid_column_classes is deprecated. Assign necessary classes manually.
|
455
|
+
Correspond to default datagrid/rows partial for example.)
|
456
|
+
MSG
|
457
|
+
column = grid.column_by_name(column)
|
458
|
+
order_class = if grid.ordered_by?(column)
|
459
|
+
["ordered", grid.descending ? "desc" : "asc"]
|
460
|
+
end
|
461
|
+
class_names(column.name, order_class, column.options[:class], column.tag_options[:class])
|
462
|
+
end
|
145
463
|
|
146
464
|
protected
|
147
465
|
|
148
|
-
def
|
149
|
-
|
466
|
+
def _render_partial(partial_name, partials_path, locals = {})
|
467
|
+
render({
|
468
|
+
partial: File.join(partials_path || "datagrid", partial_name),
|
469
|
+
locals: locals,
|
470
|
+
})
|
150
471
|
end
|
151
472
|
|
152
|
-
|
153
|
-
|
154
|
-
|
473
|
+
# Represents a datagrid row that provides access to column values for the given asset
|
474
|
+
# @example
|
475
|
+
# row = datagrid_row(grid, user)
|
476
|
+
# row.class # => Datagrid::Helper::HtmlRow
|
477
|
+
# row.first_name # => "<strong>Bogdan</strong>"
|
478
|
+
# row.grid # => Grid object
|
479
|
+
# row.asset # => User object
|
480
|
+
# row.each do |value|
|
481
|
+
# puts value
|
482
|
+
# end
|
483
|
+
class HtmlRow
|
484
|
+
include Enumerable
|
485
|
+
|
486
|
+
attr_reader :grid, :asset, :options
|
487
|
+
|
488
|
+
# @!visibility private
|
489
|
+
def initialize(renderer, grid, asset, options)
|
490
|
+
@renderer = renderer
|
491
|
+
@grid = grid
|
492
|
+
@asset = asset
|
493
|
+
@options = options
|
494
|
+
end
|
495
|
+
|
496
|
+
# @return [Object] a column value for given column name
|
497
|
+
def get(column)
|
498
|
+
@renderer.datagrid_value(@grid, column, @asset)
|
499
|
+
end
|
500
|
+
|
501
|
+
# Iterates over all column values that are available in the row
|
502
|
+
# param block [Proc] column value iterator
|
503
|
+
def each(&block)
|
504
|
+
(@options[:columns] || @grid.html_columns).each do |column|
|
505
|
+
block.call(get(column))
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
# @return [String] HTML row format
|
510
|
+
def to_s
|
511
|
+
@renderer.send(:_render_partial, "row", options[:partials], {
|
512
|
+
grid: grid,
|
513
|
+
options: options,
|
514
|
+
asset: asset,
|
515
|
+
},)
|
516
|
+
end
|
517
|
+
|
518
|
+
protected
|
519
|
+
|
520
|
+
def method_missing(method, *args, &blk)
|
521
|
+
if (column = @grid.column_by_name(method))
|
522
|
+
get(column)
|
523
|
+
else
|
524
|
+
super
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
def respond_to_missing?(method, include_private = false)
|
529
|
+
!!@grid.column_by_name(method) || super
|
530
|
+
end
|
155
531
|
end
|
156
532
|
end
|
157
533
|
end
|
158
|
-
|
data/lib/datagrid/ordering.rb
CHANGED
@@ -1,42 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "datagrid/columns"
|
2
4
|
|
3
5
|
module Datagrid
|
4
6
|
# Raised when grid order value is incorrect
|
5
7
|
class OrderUnsupported < StandardError
|
6
8
|
end
|
7
|
-
module Ordering
|
8
9
|
|
10
|
+
# Module adds support for ordering by defined columns for Datagrid.
|
11
|
+
module Ordering
|
9
12
|
# @!visibility private
|
10
13
|
def self.included(base)
|
11
|
-
base.extend
|
14
|
+
base.extend ClassMethods
|
12
15
|
base.class_eval do
|
13
16
|
include Datagrid::Columns
|
14
17
|
|
15
18
|
datagrid_attribute :order do |value|
|
16
|
-
if value.present?
|
17
|
-
value.to_sym
|
18
|
-
else
|
19
|
-
nil
|
20
|
-
end
|
21
|
-
|
19
|
+
value.to_sym if value.present?
|
22
20
|
end
|
23
21
|
|
24
22
|
datagrid_attribute :descending do |value|
|
25
23
|
Datagrid::Utils.booleanize(value)
|
26
24
|
end
|
27
|
-
|
28
|
-
|
25
|
+
alias_method :descending?, :descending
|
29
26
|
end
|
30
27
|
end
|
31
28
|
|
32
29
|
# @!visibility private
|
33
30
|
module ClassMethods
|
34
31
|
def order_unsupported(name, reason)
|
35
|
-
raise Datagrid::OrderUnsupported, "Can not sort #{
|
32
|
+
raise Datagrid::OrderUnsupported, "Can not sort #{inspect} by ##{name}: #{reason}"
|
36
33
|
end
|
37
34
|
end
|
38
35
|
|
39
|
-
|
40
36
|
# @!visibility private
|
41
37
|
def assets
|
42
38
|
check_order_valid!
|
@@ -56,42 +52,42 @@ module Datagrid
|
|
56
52
|
end
|
57
53
|
|
58
54
|
# @param column [String, Datagrid::Columns::Column]
|
55
|
+
# @param desc [nil, Boolean] confirm order direction as well if specified
|
59
56
|
# @return [Boolean] true if given grid is ordered by given column.
|
60
|
-
def ordered_by?(column)
|
61
|
-
order_column == column_by_name(column)
|
57
|
+
def ordered_by?(column, desc = nil)
|
58
|
+
order_column == column_by_name(column) &&
|
59
|
+
(desc.nil? || (desc ? descending? : !descending?))
|
62
60
|
end
|
63
61
|
|
64
62
|
private
|
65
63
|
|
66
64
|
def apply_order(assets)
|
67
65
|
return assets unless order
|
66
|
+
|
68
67
|
if order_column.order_by_value?
|
69
68
|
assets = assets.sort_by do |asset|
|
70
69
|
order_column.order_by_value(asset, self)
|
71
70
|
end
|
72
71
|
descending? ? assets.reverse : assets
|
73
|
-
|
74
|
-
if
|
75
|
-
|
76
|
-
apply_asc_order(assets, order_column.order_desc)
|
77
|
-
else
|
78
|
-
apply_desc_order(assets, order_column.order)
|
79
|
-
end
|
72
|
+
elsif descending?
|
73
|
+
if order_column.order_desc
|
74
|
+
apply_asc_order(assets, order_column.order_desc)
|
80
75
|
else
|
81
|
-
|
76
|
+
apply_desc_order(assets, order_column.order)
|
82
77
|
end
|
78
|
+
else
|
79
|
+
apply_asc_order(assets, order_column.order)
|
83
80
|
end
|
84
81
|
end
|
85
82
|
|
86
83
|
def check_order_valid!
|
87
84
|
return unless order
|
85
|
+
|
88
86
|
column = column_by_name(order)
|
89
|
-
unless column
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
self.class.order_unsupported(column.name, "column don't support order" )
|
94
|
-
end
|
87
|
+
self.class.order_unsupported(order, "no column #{order} in #{self.class}") unless column
|
88
|
+
return if column.supports_order?
|
89
|
+
|
90
|
+
self.class.order_unsupported(column.name, "column don't support order")
|
95
91
|
end
|
96
92
|
|
97
93
|
def apply_asc_order(assets, order)
|
@@ -113,7 +109,8 @@ module Datagrid
|
|
113
109
|
def reverse_order(assets)
|
114
110
|
driver.reverse_order(assets)
|
115
111
|
rescue NotImplementedError
|
116
|
-
self.class.order_unsupported(order_column.name,
|
112
|
+
self.class.order_unsupported(order_column.name,
|
113
|
+
"Your ORM do not support reverse order: please specify :order_desc option manually",)
|
117
114
|
end
|
118
115
|
|
119
116
|
def apply_block_order(assets, order)
|
data/lib/datagrid/rspec.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "datagrid"
|
2
4
|
|
3
|
-
#TODO: refactor this experimental shit
|
5
|
+
# TODO: refactor this experimental shit
|
4
6
|
shared_examples_for "Datagrid" do
|
5
7
|
describe "as Datagrid" do
|
6
|
-
|
7
8
|
it "should have at least one entry if assets" do
|
8
9
|
subject.assets.should_not be_empty
|
9
10
|
end
|
10
11
|
|
11
|
-
described_class.columns(:
|
12
|
+
described_class.columns(data: true).each do |column|
|
12
13
|
describe "column ##{column.name}" do
|
13
|
-
|
14
14
|
it "should has value in #data_hash" do
|
15
15
|
subject.data_hash.first.should have_key(column.name)
|
16
16
|
end
|
@@ -25,14 +25,11 @@ shared_examples_for "Datagrid" do
|
|
25
25
|
subject.assets.first.should_not be_nil
|
26
26
|
end
|
27
27
|
end
|
28
|
-
|
29
28
|
end
|
30
29
|
|
31
30
|
described_class.filters.each do |filter|
|
32
31
|
describe "filter ##{filter.name}" do
|
33
|
-
|
34
32
|
let(:filter_value) do
|
35
|
-
|
36
33
|
case Datagrid::Filters::FILTER_TYPES.invert[filter.class]
|
37
34
|
when :default, :string
|
38
35
|
"text"
|
@@ -53,16 +50,15 @@ shared_examples_for "Datagrid" do
|
|
53
50
|
end
|
54
51
|
|
55
52
|
before(:each) do
|
56
|
-
subject.attributes = {filter.name => filter_value}
|
53
|
+
subject.attributes = { filter.name => filter_value }
|
57
54
|
subject.public_send(filter.name).should_not be_nil
|
58
55
|
end
|
59
56
|
|
60
57
|
it "should be supported" do
|
61
58
|
subject.assets.should_not be_nil
|
62
|
-
#TODO: better matcher.
|
59
|
+
# TODO: better matcher.
|
63
60
|
end
|
64
61
|
end
|
65
62
|
end
|
66
|
-
|
67
63
|
end
|
68
64
|
end
|