datagrid 1.8.2 → 2.0.0
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/.yardopts +2 -0
- data/CHANGELOG.md +11 -1
- data/{Readme.markdown → README.md} +44 -29
- 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 +8 -7
- 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 +325 -115
- data/lib/datagrid/configuration.rb +33 -4
- 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 -143
- 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 -31
- data/lib/datagrid/filters/float_filter.rb +15 -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 +204 -79
- data/lib/datagrid/form_builder.rb +116 -128
- data/lib/datagrid/generators/scaffold.rb +184 -0
- data/lib/datagrid/generators/views.rb +20 -0
- data/lib/datagrid/helper.rb +436 -69
- 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 +8 -28
- data/templates/base.rb.erb +6 -4
- data/templates/grid.rb.erb +1 -1
- metadata +17 -17
- 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/columns.rb
CHANGED
@@ -1,40 +1,234 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "datagrid/utils"
|
2
4
|
require "active_support/core_ext/class/attribute"
|
5
|
+
require "datagrid/columns/column"
|
3
6
|
|
4
7
|
module Datagrid
|
5
|
-
|
8
|
+
# Defines a column to be used for displaying data in a Datagrid.
|
9
|
+
#
|
10
|
+
# class UserGrid < ApplicationGrid
|
11
|
+
# scope do
|
12
|
+
# User.order("users.created_at desc").joins(:group)
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# column(:name)
|
16
|
+
# column(:group, order: "groups.name") do
|
17
|
+
# self.group.name
|
18
|
+
# end
|
19
|
+
# column(:active, header: "Activated") do |user|
|
20
|
+
# !user.disabled
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# Each column is used to generate data for the grid.
|
25
|
+
#
|
26
|
+
# To create a grid displaying all users:
|
27
|
+
#
|
28
|
+
# grid = UserGrid.new
|
29
|
+
# grid.header # => ["Group", "Name", "Disabled"]
|
30
|
+
# grid.rows # => [
|
31
|
+
# # ["Steve", "Spammers", true],
|
32
|
+
# # ["John", "Spoilers", true],
|
33
|
+
# # ["Berry", "Good people", false]
|
34
|
+
# # ]
|
35
|
+
# grid.data # => Header & Rows
|
36
|
+
# grid.data_hash # => [
|
37
|
+
# # { name: "Steve", group: "Spammers", active: true },
|
38
|
+
# # { name: "John", group: "Spoilers", active: true },
|
39
|
+
# # { name: "Berry", group: "Good people", active: false },
|
40
|
+
# # ]
|
41
|
+
# }
|
42
|
+
#
|
43
|
+
# ## Column Value
|
44
|
+
#
|
45
|
+
# The value of a column can be defined by passing a block to `Datagrid.column`.
|
46
|
+
#
|
47
|
+
# ### Basic Column Value
|
48
|
+
#
|
49
|
+
# If no block is provided, the column value is generated automatically by sending the column name method to the model.
|
50
|
+
#
|
51
|
+
# column(:name) # => asset.name
|
52
|
+
#
|
53
|
+
# Using <tt>instance_eval</tt>:
|
54
|
+
#
|
55
|
+
# column(:completed) { completed? }
|
56
|
+
#
|
57
|
+
# Using the asset as an argument:
|
58
|
+
#
|
59
|
+
# column(:completed) { |asset| asset.completed? }
|
60
|
+
#
|
61
|
+
# ### Advanced Column Value
|
62
|
+
#
|
63
|
+
# You can also pass the Datagrid object itself to define more complex column values.
|
64
|
+
#
|
65
|
+
# Using filters with columns:
|
66
|
+
#
|
67
|
+
# filter(:category) do |value|
|
68
|
+
# where("category LIKE '%#{value}%'")
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# column(:exactly_matches_category) do |asset, grid|
|
72
|
+
# asset.category == grid.category
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# Combining columns:
|
76
|
+
#
|
77
|
+
# column(:total_sales) do |merchant|
|
78
|
+
# merchant.purchases.sum(:subtotal)
|
79
|
+
# end
|
80
|
+
# column(:number_of_sales) do |merchant|
|
81
|
+
# merchant.purchases.count
|
82
|
+
# end
|
83
|
+
# column(:average_order_value) do |_, _, row|
|
84
|
+
# row.total_sales / row.number_of_sales
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
# ## Using Database Expressions
|
88
|
+
#
|
89
|
+
# Columns can use database expressions to directly manipulate data in the database.
|
90
|
+
#
|
91
|
+
# column(:count_of_users, 'count(user_id)')
|
92
|
+
# column(:uppercase_name, 'upper(name)')
|
93
|
+
#
|
94
|
+
# ## HTML Columns
|
95
|
+
#
|
96
|
+
# Columns can have different formats for HTML and non-HTML representations.
|
97
|
+
#
|
98
|
+
# column(:name) do |asset|
|
99
|
+
# format(asset.name) do |value|
|
100
|
+
# content_tag(:strong, value)
|
101
|
+
# end
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# ## Column Value Cache
|
105
|
+
#
|
106
|
+
# Enables grid-level caching for column values.
|
107
|
+
#
|
108
|
+
# self.cached = true
|
109
|
+
#
|
110
|
+
# ## Ordering
|
111
|
+
#
|
112
|
+
# Columns can specify SQL ordering expressions using the `:order` and `:order_desc` options.
|
113
|
+
#
|
114
|
+
# Basic ordering:
|
115
|
+
#
|
116
|
+
# column(:group_name, order: "groups.name") { self.group.name }
|
117
|
+
#
|
118
|
+
# In example above descending order is automatically inherited from ascending order specified.
|
119
|
+
# When such default is not enough, specify both options together:
|
120
|
+
#
|
121
|
+
# column(
|
122
|
+
# :priority,
|
123
|
+
# # models with null priority are always on bottom
|
124
|
+
# # no matter if sorting ascending or descending
|
125
|
+
# order: "priority is not null desc, priority",
|
126
|
+
# order_desc: "priority is not null desc, priority desc"
|
127
|
+
# )
|
128
|
+
#
|
129
|
+
# Disable order like this:
|
130
|
+
#
|
131
|
+
# column(:title, order: false)
|
132
|
+
#
|
133
|
+
# Order by joined table
|
134
|
+
# Allows to join specified table only when order is enabled
|
135
|
+
# for performance:
|
136
|
+
#
|
137
|
+
# column(:profile_updated_at, order: proc { |scope|
|
138
|
+
# scope.joins(:profile).order("profiles.updated_at")
|
139
|
+
# }) do |model|
|
140
|
+
# model.profile.updated_at.to_date
|
141
|
+
# end
|
142
|
+
#
|
143
|
+
# Order by a calculated value
|
144
|
+
#
|
145
|
+
# column(
|
146
|
+
# :duration_request,
|
147
|
+
# order: "(requests.finished_at - requests.accepted_at)"
|
148
|
+
# ) do |model|
|
149
|
+
# Time.at(model.finished_at - model.accepted_at).strftime("%H:%M:%S")
|
150
|
+
# end
|
151
|
+
#
|
152
|
+
# ## Default Column Options
|
153
|
+
#
|
154
|
+
# Default options for all columns in a grid can be set using `default_column_options`.
|
155
|
+
#
|
156
|
+
# self.default_column_options = { order: false }
|
157
|
+
#
|
158
|
+
# ## Columns Visibility
|
159
|
+
#
|
160
|
+
# Columns can be dynamically shown or hidden based on the grid's `column_names` accessor.
|
161
|
+
#
|
162
|
+
# grid.column_names = [:id, :name]
|
163
|
+
#
|
164
|
+
# ## Dynamic Columns
|
165
|
+
#
|
166
|
+
# Columns can be defined dynamically on a grid instance or based on data.
|
167
|
+
#
|
168
|
+
# Adding a dynamic column:
|
169
|
+
#
|
170
|
+
# grid.column(:extra_data) do |model|
|
171
|
+
# model.extra_data
|
172
|
+
# end
|
173
|
+
#
|
174
|
+
# ## Localization
|
175
|
+
#
|
176
|
+
# Column headers can be localized using the `:header` option or through i18n files.
|
177
|
+
#
|
178
|
+
# column(:active, header: Proc.new { I18n.t("activated") })
|
179
|
+
#
|
180
|
+
# ## Preloading Associations
|
181
|
+
#
|
182
|
+
# Preload database associations for better performance.
|
183
|
+
#
|
184
|
+
# Automatic association preloading:
|
185
|
+
#
|
186
|
+
# column(:group) do |user|
|
187
|
+
# user.group.name
|
188
|
+
# end
|
189
|
+
#
|
190
|
+
# Custom preloading:
|
191
|
+
#
|
192
|
+
# column(:account_name, preload: { |s| s.includes(:account) })
|
193
|
+
#
|
194
|
+
# ## Decorator
|
195
|
+
#
|
196
|
+
# A decorator or presenter class can be used around each object in the `scope`.
|
197
|
+
#
|
198
|
+
# decorate { UserPresenter }
|
199
|
+
# column(:created_at) do |presenter|
|
200
|
+
# presenter.user.created_at
|
201
|
+
# end
|
6
202
|
module Columns
|
7
|
-
|
8
|
-
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# self.default_column_options = { order: false }
|
15
|
-
# # Makes entire report HTML
|
16
|
-
# self.default_column_options = { html: true }
|
203
|
+
# @!method default_column_options=(value)
|
204
|
+
# @param [Hash] value default options passed to {#column} method call
|
205
|
+
# @return [Hash] default options passed to {#column} method call
|
206
|
+
# @example Disable default order
|
207
|
+
# self.default_column_options = { order: false }
|
208
|
+
# @example Makes entire report HTML
|
209
|
+
# self.default_column_options = { html: true }
|
17
210
|
|
18
211
|
# @!method default_column_options
|
19
|
-
#
|
20
|
-
#
|
21
|
-
|
22
|
-
# @!method batch_size=
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
212
|
+
# @return [Hash] default options passed to {#column} method call
|
213
|
+
# @see #default_column_options=
|
214
|
+
|
215
|
+
# @!method batch_size=(value)
|
216
|
+
# Specify a default batch size when generating CSV or just data.
|
217
|
+
# @param [Integer] value a batch size when generating CSV or just data. Default: 1000
|
218
|
+
# @return [void]
|
219
|
+
# @example Set batch size to 500
|
220
|
+
# self.batch_size = 500
|
221
|
+
# @example Disable batches
|
222
|
+
# self.batch_size = nil
|
30
223
|
|
31
224
|
# @!method batch_size
|
32
|
-
#
|
33
|
-
#
|
225
|
+
# @return [Integer]
|
226
|
+
# @see #batch_size=
|
34
227
|
|
35
228
|
# @visibility private
|
229
|
+
# @param [Object] base
|
36
230
|
def self.included(base)
|
37
|
-
base.extend
|
231
|
+
base.extend ClassMethods
|
38
232
|
base.class_eval do
|
39
233
|
include Datagrid::Core
|
40
234
|
|
@@ -47,7 +241,6 @@ module Datagrid
|
|
47
241
|
end
|
48
242
|
|
49
243
|
module ClassMethods
|
50
|
-
|
51
244
|
# @param data [Boolean] if true returns only columns with data representation. Default: false.
|
52
245
|
# @param html [Boolean] if true returns only columns with html columns. Default: false.
|
53
246
|
# @param column_names [Array<String>] list of column names if you want to limit data only to specified columns
|
@@ -58,35 +251,31 @@ module Datagrid
|
|
58
251
|
filter_columns(columns_array, *column_names, data: data, html: html)
|
59
252
|
end
|
60
253
|
|
61
|
-
# Defines new datagrid column
|
62
|
-
#
|
254
|
+
# Defines a new datagrid column
|
63
255
|
# @param name [Symbol] column name
|
64
256
|
# @param query [String, nil] a string representing the query to select this column (supports only ActiveRecord)
|
65
|
-
# @param options [Hash<Symbol, Object>] hash of options
|
66
257
|
# @param block [Block] proc to calculate a column value
|
258
|
+
# @option options [Boolean, String] html Determines if the column should be present
|
259
|
+
# in the HTML table and how it is formatted.
|
260
|
+
# @option options [String, Array<Symbol>] order Determines if the column can be sortable and
|
261
|
+
# specifies the ORM ordering method.
|
262
|
+
# Example: `"created_at, id"` for ActiveRecord, `[:created_at, :id]` for Mongoid.
|
263
|
+
# @option options [String] order_desc Specifies a descending order for the column
|
264
|
+
# (used when `:order` cannot be easily reversed by the ORM).
|
265
|
+
# @option options [Boolean, Proc] order_by_value Enables Ruby-level ordering for the column.
|
266
|
+
# Warning: Sorting large datasets in Ruby is not recommended.
|
267
|
+
# If `true`, Datagrid orders by the column value.
|
268
|
+
# If a block is provided, Datagrid orders by the block's return value.
|
269
|
+
# @option options [Boolean] mandatory If `true`, the column will never be hidden by the `#column_names` selection.
|
270
|
+
# @option options [Symbol] before Places the column before the specified column when determining order.
|
271
|
+
# @option options [Symbol] after Places the column after the specified column when determining order.
|
272
|
+
# @option options [Boolean, Proc] if conditions when a column is available.
|
273
|
+
# @option options [Boolean, Proc] unless conditions when a column is not available.
|
274
|
+
# @option options [Symbol, Array<Symbol>] preload Specifies associations
|
275
|
+
# to preload for the column within the scope.
|
276
|
+
# @option options [Hash] tag_options Specifies HTML attributes for the `<td>` or `<th>` of the column.
|
277
|
+
# Example: `{ class: "content-align-right", "data-group": "statistics" }`.
|
67
278
|
# @return [Datagrid::Columns::Column]
|
68
|
-
#
|
69
|
-
# Available options:
|
70
|
-
#
|
71
|
-
# * <tt>html</tt> - determines if current column should be present in html table and how is it formatted
|
72
|
-
# * <tt>order</tt> - determines if this column could be sortable and how.
|
73
|
-
# The value of order is explicitly passed to ORM ordering method.
|
74
|
-
# Ex: <tt>"created_at, id"</tt> for ActiveRecord, <tt>[:created_at, :id]</tt> for Mongoid
|
75
|
-
# * <tt>order_desc</tt> - determines a descending order for given column
|
76
|
-
# (only in case when <tt>:order</tt> can not be easily reversed by ORM)
|
77
|
-
# * <tt>order_by_value</tt> - used in case it is easier to perform ordering at ruby level not on database level.
|
78
|
-
# Warning: using ruby to order large datasets is very unrecommended.
|
79
|
-
# If set to true - datagrid will use column value to order by this column
|
80
|
-
# If block is given - datagrid will use value returned from block
|
81
|
-
# * <tt>mandatory</tt> - if true, column will never be hidden with #column_names selection
|
82
|
-
# * <tt>url</tt> - a proc with one argument, pass this option to easily convert the value into an URL
|
83
|
-
# * <tt>before</tt> - determines the position of this column, by adding it before the column passed here
|
84
|
-
# * <tt>after</tt> - determines the position of this column, by adding it after the column passed here
|
85
|
-
# * <tt>if</tt> - the column is shown if the reult of calling this argument is true
|
86
|
-
# * <tt>unless</tt> - the column is shown unless the reult of calling this argument is true
|
87
|
-
# * <tt>preload</tt> - spefies which associations of the scope should be preloaded for this column
|
88
|
-
#
|
89
|
-
# @see https://github.com/bogdan/datagrid/wiki/Columns
|
90
279
|
def column(name, query = nil, **options, &block)
|
91
280
|
define_column(columns_array, name, query, **options, &block)
|
92
281
|
end
|
@@ -115,7 +304,7 @@ module Datagrid
|
|
115
304
|
# @example
|
116
305
|
# column(:name) do |model|
|
117
306
|
# format(model.name) do |value|
|
118
|
-
#
|
307
|
+
# tag.strong(value)
|
119
308
|
# end
|
120
309
|
# end
|
121
310
|
def format(value, &block)
|
@@ -136,33 +325,35 @@ module Datagrid
|
|
136
325
|
# Defines a model decorator that will be used to define a column value.
|
137
326
|
# All column blocks will be given a decorated version of the model.
|
138
327
|
# @return [void]
|
139
|
-
# @example
|
328
|
+
# @example Wrapping a model with presenter
|
140
329
|
# decorate { |user| UserPresenter.new(user) }
|
141
|
-
#
|
142
|
-
# decorate { UserPresenter }
|
330
|
+
# @example A shortcut for doing the same
|
331
|
+
# decorate { UserPresenter }
|
143
332
|
def decorate(model = nil, &block)
|
144
333
|
if !model && !block
|
145
334
|
raise ArgumentError, "decorate needs either a block to define decoration or a model to decorate"
|
146
335
|
end
|
147
336
|
return self.decorator = block unless model
|
148
337
|
return model unless decorator
|
338
|
+
|
149
339
|
presenter = ::Datagrid::Utils.apply_args(model, &decorator)
|
150
|
-
presenter = presenter.
|
340
|
+
presenter = presenter.new(model) if presenter.is_a?(Class)
|
151
341
|
block_given? ? yield(presenter) : presenter
|
152
342
|
end
|
153
343
|
|
154
344
|
# @!visibility private
|
155
345
|
def inherited(child_class)
|
156
|
-
super
|
157
|
-
child_class.columns_array =
|
346
|
+
super
|
347
|
+
child_class.columns_array = columns_array.clone
|
158
348
|
end
|
159
349
|
|
160
350
|
# @!visibility private
|
161
351
|
def filter_columns(columns_array, *names, data: false, html: false)
|
162
352
|
names.compact!
|
163
|
-
if names.size >= 1 && names.all? {|n| n.is_a?(Datagrid::Columns::Column) && n.grid_class == self.class}
|
353
|
+
if names.size >= 1 && names.all? { |n| n.is_a?(Datagrid::Columns::Column) && n.grid_class == self.class }
|
164
354
|
return names
|
165
355
|
end
|
356
|
+
|
166
357
|
names.map!(&:to_sym)
|
167
358
|
columns_array.select do |column|
|
168
359
|
(!data || column.data?) &&
|
@@ -186,32 +377,34 @@ module Datagrid
|
|
186
377
|
end
|
187
378
|
|
188
379
|
# @!visibility private
|
189
|
-
def find_column_by_name(columns,name)
|
380
|
+
def find_column_by_name(columns, name)
|
190
381
|
return name if name.is_a?(Datagrid::Columns::Column)
|
382
|
+
|
191
383
|
columns.find do |col|
|
192
384
|
col.name.to_sym == name.to_sym
|
193
385
|
end
|
194
386
|
end
|
195
|
-
|
196
387
|
end
|
197
388
|
|
198
389
|
# @!visibility private
|
199
390
|
def assets
|
200
391
|
append_column_preload(
|
201
392
|
driver.append_column_queries(
|
202
|
-
super, columns.select(&:query)
|
203
|
-
)
|
393
|
+
super, columns.select(&:query),
|
394
|
+
),
|
204
395
|
)
|
205
396
|
end
|
206
397
|
|
207
|
-
# @param column_names [Array<String>] list of column names
|
398
|
+
# @param column_names [Array<String, Symbol>] list of column names
|
399
|
+
# if you want to limit data only to specified columns
|
208
400
|
# @return [Array<String>] human readable column names. See also "Localization" section
|
209
401
|
def header(*column_names)
|
210
402
|
data_columns(*column_names).map(&:header)
|
211
403
|
end
|
212
404
|
|
213
405
|
# @param asset [Object] asset from datagrid scope
|
214
|
-
# @param column_names [Array<String>] list of column names
|
406
|
+
# @param column_names [Array<String, Symbol>] list of column names
|
407
|
+
# if you want to limit data only to specified columns
|
215
408
|
# @return [Array<Object>] column values for given asset
|
216
409
|
def row_for(asset, *column_names)
|
217
410
|
data_columns(*column_names).map do |column|
|
@@ -220,31 +413,33 @@ module Datagrid
|
|
220
413
|
end
|
221
414
|
|
222
415
|
# @param asset [Object] asset from datagrid scope
|
223
|
-
# @return [Hash] A mapping where keys are column names and
|
416
|
+
# @return [Hash] A mapping where keys are column names and
|
417
|
+
# values are column values for the given asset
|
224
418
|
def hash_for(asset)
|
225
419
|
result = {}
|
226
|
-
|
420
|
+
data_columns.each do |column|
|
227
421
|
result[column.name] = data_value(column, asset)
|
228
422
|
end
|
229
423
|
result
|
230
424
|
end
|
231
425
|
|
232
|
-
# @param column_names [Array<String>] list of column names
|
426
|
+
# @param column_names [Array<String,Symbol>] list of column names
|
427
|
+
# if you want to limit data only to specified columns
|
233
428
|
# @return [Array<Array<Object>>] with data for each row in datagrid assets without header
|
234
429
|
def rows(*column_names)
|
235
430
|
map_with_batches do |asset|
|
236
|
-
|
431
|
+
row_for(asset, *column_names)
|
237
432
|
end
|
238
433
|
end
|
239
434
|
|
240
|
-
# @param column_names [Array<String>] list of column names
|
435
|
+
# @param column_names [Array<String, Symbol>] list of column names
|
436
|
+
# if you want to limit data only to specified columns.
|
241
437
|
# @return [Array<Array<Object>>] data for each row in datagrid assets with header.
|
242
438
|
def data(*column_names)
|
243
|
-
|
439
|
+
rows(*column_names).unshift(header(*column_names))
|
244
440
|
end
|
245
441
|
|
246
|
-
#
|
247
|
-
# for each row in filtered datagrid relation.
|
442
|
+
# @return [Array<Hash{Symbol => Object}>] an array of hashes representing the rows in the filtered datagrid relation
|
248
443
|
#
|
249
444
|
# @example
|
250
445
|
# class MyGrid
|
@@ -263,7 +458,7 @@ module Datagrid
|
|
263
458
|
end
|
264
459
|
end
|
265
460
|
|
266
|
-
# @param column_names [Array<String>]
|
461
|
+
# @param column_names [Array<String,Symbol>]
|
267
462
|
# @param options [Hash] CSV generation options
|
268
463
|
# @return [String] a CSV representation of the data in the grid
|
269
464
|
#
|
@@ -274,9 +469,9 @@ module Datagrid
|
|
274
469
|
def to_csv(*column_names, **options)
|
275
470
|
require "csv"
|
276
471
|
CSV.generate(
|
277
|
-
headers:
|
472
|
+
headers: header(*column_names),
|
278
473
|
write_headers: true,
|
279
|
-
**options
|
474
|
+
**options,
|
280
475
|
) do |csv|
|
281
476
|
each_with_batches do |asset|
|
282
477
|
csv << row_for(asset, *column_names)
|
@@ -284,8 +479,9 @@ module Datagrid
|
|
284
479
|
end
|
285
480
|
end
|
286
481
|
|
287
|
-
|
288
482
|
# @param column_names [Array<Symbol, String>]
|
483
|
+
# @param [Boolean] data return only data columns
|
484
|
+
# @param [Boolean] html return only HTML columns
|
289
485
|
# @return [Array<Datagrid::Columns::Column>] all columns selected in grid instance
|
290
486
|
# @example
|
291
487
|
# MyGrid.new.columns # => all defined columns
|
@@ -294,25 +490,28 @@ module Datagrid
|
|
294
490
|
# grid.columns(:id, :category) # => id and category column
|
295
491
|
def columns(*column_names, data: false, html: false)
|
296
492
|
self.class.filter_columns(
|
297
|
-
columns_array, *column_names, data: data, html: html
|
493
|
+
columns_array, *column_names, data: data, html: html,
|
298
494
|
).select do |column|
|
299
495
|
column.enabled?(self)
|
300
496
|
end
|
301
497
|
end
|
302
498
|
|
303
|
-
# @param column_names [Array<String, Symbol>] list of column names
|
304
|
-
#
|
305
|
-
|
306
|
-
|
499
|
+
# @param column_names [Array<String, Symbol>] list of column names
|
500
|
+
# if you want to limit data only to specified columns
|
501
|
+
# @param [Boolean] html return only HTML columns
|
502
|
+
# @return [Array<Datagrid::Columns::Column>] columns that can be represented in plain data(non-html) way
|
503
|
+
def data_columns(*column_names, html: false)
|
504
|
+
columns(*column_names, html: html, data: true)
|
307
505
|
end
|
308
506
|
|
309
507
|
# @param column_names [Array<String>] list of column names if you want to limit data only to specified columns
|
508
|
+
# @param [Boolean] data return only data columns
|
310
509
|
# @return all columns that can be represented in HTML table
|
311
|
-
def html_columns(*column_names,
|
312
|
-
|
510
|
+
def html_columns(*column_names, data: false)
|
511
|
+
columns(*column_names, data: data, html: true)
|
313
512
|
end
|
314
513
|
|
315
|
-
# Finds a column
|
514
|
+
# Finds a column by name
|
316
515
|
# @param name [String, Symbol] column name to be found
|
317
516
|
# @return [Datagrid::Columns::Column, nil]
|
318
517
|
def column_by_name(name)
|
@@ -320,22 +519,21 @@ module Datagrid
|
|
320
519
|
end
|
321
520
|
|
322
521
|
# Gives ability to have a different formatting for CSV and HTML column value.
|
323
|
-
#
|
324
|
-
# @example
|
522
|
+
# @example Formating using Rails view helpers
|
325
523
|
# column(:name) do |model|
|
326
524
|
# format(model.name) do |value|
|
327
|
-
#
|
525
|
+
# tag.strong(value)
|
328
526
|
# end
|
329
527
|
# end
|
330
|
-
#
|
528
|
+
# @example Formatting using a separated view template
|
331
529
|
# column(:company) do |model|
|
332
530
|
# format(model.company.name) do
|
333
|
-
# render partial: "company_with_logo", locals: {company: model.company }
|
531
|
+
# render partial: "companies/company_with_logo", locals: {company: model.company }
|
334
532
|
# end
|
335
533
|
# end
|
336
534
|
# @return [Datagrid::Columns::Column::ResponseFormat] Format object
|
337
535
|
def format(value, &block)
|
338
|
-
if
|
536
|
+
if block
|
339
537
|
self.class.format(value, &block)
|
340
538
|
else
|
341
539
|
# don't override Object#format method
|
@@ -343,26 +541,26 @@ module Datagrid
|
|
343
541
|
end
|
344
542
|
end
|
345
543
|
|
544
|
+
# @param [Object] asset one of the assets from grid scope
|
346
545
|
# @return [Datagrid::Columns::DataRow] an object representing a grid row.
|
347
546
|
# @example
|
348
|
-
#
|
349
|
-
#
|
350
|
-
#
|
351
|
-
#
|
352
|
-
#
|
353
|
-
#
|
354
|
-
#
|
355
|
-
#
|
547
|
+
# class MyGrid
|
548
|
+
# scope { User }
|
549
|
+
# column(:id)
|
550
|
+
# column(:name)
|
551
|
+
# column(:number_of_purchases) do |user|
|
552
|
+
# user.purchases.count
|
553
|
+
# end
|
554
|
+
# end
|
356
555
|
#
|
357
|
-
#
|
358
|
-
#
|
359
|
-
#
|
556
|
+
# row = MyGrid.new.data_row(User.last)
|
557
|
+
# row.id # => user.id
|
558
|
+
# row.number_of_purchases # => user.purchases.count
|
360
559
|
def data_row(asset)
|
361
560
|
::Datagrid::Columns::DataRow.new(self, asset)
|
362
561
|
end
|
363
562
|
|
364
563
|
# Defines a column at instance level
|
365
|
-
#
|
366
564
|
# @see Datagrid::Columns::ClassMethods#column
|
367
565
|
def column(name, query = nil, **options, &block)
|
368
566
|
self.class.define_column(columns_array, name, query, **options, &block)
|
@@ -374,8 +572,8 @@ module Datagrid
|
|
374
572
|
super
|
375
573
|
end
|
376
574
|
|
377
|
-
# @return [Array<Datagrid::Columns::Column>] all columns
|
378
|
-
#
|
575
|
+
# @return [Array<Datagrid::Columns::Column>] all columns
|
576
|
+
# that are possible to be displayed for the current grid object
|
379
577
|
# @example
|
380
578
|
# class MyGrid
|
381
579
|
# filter(:search) {|scope, value| scope.full_text_search(value)}
|
@@ -397,19 +595,25 @@ module Datagrid
|
|
397
595
|
end
|
398
596
|
end
|
399
597
|
|
598
|
+
# @param [String,Symbol] column_name column name
|
599
|
+
# @param [Object] asset one of the assets from grid scope
|
400
600
|
# @return [Object] a cell data value for given column name and asset
|
401
601
|
def data_value(column_name, asset)
|
402
602
|
column = column_by_name(column_name)
|
403
603
|
cache(column, asset, :data_value) do
|
404
604
|
raise "no data value for #{column.name} column" unless column.data?
|
605
|
+
|
405
606
|
result = generic_value(column, asset)
|
406
607
|
result.is_a?(Datagrid::Columns::Column::ResponseFormat) ? result.call_data : result
|
407
608
|
end
|
408
609
|
end
|
409
610
|
|
611
|
+
# @param [String,Symbol] column_name column name
|
612
|
+
# @param [Object] asset one of the assets from grid scope
|
613
|
+
# @param [ActionView::Base] context view context object
|
410
614
|
# @return [Object] a cell HTML value for given column name and asset and view context
|
411
615
|
def html_value(column_name, context, asset)
|
412
|
-
column
|
616
|
+
column = column_by_name(column_name)
|
413
617
|
cache(column, asset, :html_value) do
|
414
618
|
if column.html? && column.html_block
|
415
619
|
value_from_html_block(context, asset, column)
|
@@ -420,6 +624,7 @@ module Datagrid
|
|
420
624
|
end
|
421
625
|
end
|
422
626
|
|
627
|
+
# @param [Object] model one of the assets from grid scope
|
423
628
|
# @return [Object] a decorated version of given model if decorator is specified or the model otherwise.
|
424
629
|
def decorate(model)
|
425
630
|
self.class.decorate(model)
|
@@ -462,9 +667,8 @@ module Datagrid
|
|
462
667
|
return yield
|
463
668
|
end
|
464
669
|
key = cache_key(asset)
|
465
|
-
unless key
|
466
|
-
|
467
|
-
end
|
670
|
+
raise(Datagrid::CacheKeyError, "Datagrid Cache key is #{key.inspect} for #{asset.inspect}.") unless key
|
671
|
+
|
468
672
|
@cache[column.name] ||= {}
|
469
673
|
@cache[column.name][key] ||= {}
|
470
674
|
@cache[column.name][key][type] ||= yield
|
@@ -477,10 +681,12 @@ module Datagrid
|
|
477
681
|
driver.default_cache_key(asset)
|
478
682
|
end
|
479
683
|
rescue NotImplementedError
|
480
|
-
raise Datagrid::ConfigurationError,
|
684
|
+
raise Datagrid::ConfigurationError,
|
685
|
+
<<~MSG
|
686
|
+
#{self} is setup to use cache. But there was appropriate cache key found for #{asset.inspect}.
|
687
|
+
MSG
|
481
688
|
end
|
482
689
|
|
483
|
-
|
484
690
|
def map_with_batches(&block)
|
485
691
|
result = []
|
486
692
|
each_with_batches do |asset|
|
@@ -490,7 +696,7 @@ module Datagrid
|
|
490
696
|
end
|
491
697
|
|
492
698
|
def each_with_batches(&block)
|
493
|
-
if batch_size
|
699
|
+
if batch_size&.positive?
|
494
700
|
driver.batch_each(assets, batch_size, &block)
|
495
701
|
else
|
496
702
|
assets.each(&block)
|
@@ -500,7 +706,7 @@ module Datagrid
|
|
500
706
|
def value_from_html_block(context, asset, column)
|
501
707
|
args = []
|
502
708
|
remaining_arity = column.html_block.arity
|
503
|
-
remaining_arity = 1 if remaining_arity
|
709
|
+
remaining_arity = 1 if remaining_arity.negative?
|
504
710
|
|
505
711
|
asset = decorate(asset)
|
506
712
|
|
@@ -509,7 +715,7 @@ module Datagrid
|
|
509
715
|
remaining_arity -= 1
|
510
716
|
end
|
511
717
|
|
512
|
-
args << asset if remaining_arity
|
718
|
+
args << asset if remaining_arity.positive?
|
513
719
|
args << self if remaining_arity > 1
|
514
720
|
|
515
721
|
context.instance_exec(*args, &column.html_block)
|
@@ -523,9 +729,13 @@ module Datagrid
|
|
523
729
|
@model = model
|
524
730
|
end
|
525
731
|
|
526
|
-
def method_missing(meth, *
|
732
|
+
def method_missing(meth, *_args)
|
527
733
|
@grid.data_value(meth, @model)
|
528
734
|
end
|
735
|
+
|
736
|
+
def respond_to_missing?(meth, include_private = false)
|
737
|
+
!!@grid.column_by_name(meth) || super
|
738
|
+
end
|
529
739
|
end
|
530
740
|
end
|
531
741
|
end
|