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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -0
  3. data/CHANGELOG.md +11 -1
  4. data/{Readme.markdown → README.md} +44 -29
  5. data/app/assets/stylesheets/datagrid.css +145 -0
  6. data/app/views/datagrid/_enum_checkboxes.html.erb +5 -3
  7. data/app/views/datagrid/_form.html.erb +4 -4
  8. data/app/views/datagrid/_head.html.erb +26 -3
  9. data/app/views/datagrid/_range_filter.html.erb +5 -3
  10. data/app/views/datagrid/_row.html.erb +12 -1
  11. data/app/views/datagrid/_table.html.erb +4 -4
  12. data/datagrid.gemspec +8 -7
  13. data/lib/datagrid/active_model.rb +9 -17
  14. data/lib/datagrid/base.rb +39 -0
  15. data/lib/datagrid/column_names_attribute.rb +9 -11
  16. data/lib/datagrid/columns/column.rb +155 -133
  17. data/lib/datagrid/columns.rb +325 -115
  18. data/lib/datagrid/configuration.rb +33 -4
  19. data/lib/datagrid/core.rb +89 -54
  20. data/lib/datagrid/deprecated_object.rb +20 -0
  21. data/lib/datagrid/drivers/abstract_driver.rb +12 -23
  22. data/lib/datagrid/drivers/active_record.rb +24 -26
  23. data/lib/datagrid/drivers/array.rb +22 -14
  24. data/lib/datagrid/drivers/mongo_mapper.rb +15 -14
  25. data/lib/datagrid/drivers/mongoid.rb +15 -17
  26. data/lib/datagrid/drivers/sequel.rb +14 -19
  27. data/lib/datagrid/drivers.rb +2 -1
  28. data/lib/datagrid/engine.rb +11 -3
  29. data/lib/datagrid/filters/base_filter.rb +166 -143
  30. data/lib/datagrid/filters/boolean_filter.rb +19 -5
  31. data/lib/datagrid/filters/date_filter.rb +33 -35
  32. data/lib/datagrid/filters/date_time_filter.rb +24 -16
  33. data/lib/datagrid/filters/default_filter.rb +9 -3
  34. data/lib/datagrid/filters/dynamic_filter.rb +151 -105
  35. data/lib/datagrid/filters/enum_filter.rb +43 -19
  36. data/lib/datagrid/filters/extended_boolean_filter.rb +39 -31
  37. data/lib/datagrid/filters/float_filter.rb +15 -5
  38. data/lib/datagrid/filters/integer_filter.rb +21 -10
  39. data/lib/datagrid/filters/ranged_filter.rb +66 -45
  40. data/lib/datagrid/filters/select_options.rb +58 -49
  41. data/lib/datagrid/filters/string_filter.rb +9 -4
  42. data/lib/datagrid/filters.rb +204 -79
  43. data/lib/datagrid/form_builder.rb +116 -128
  44. data/lib/datagrid/generators/scaffold.rb +184 -0
  45. data/lib/datagrid/generators/views.rb +20 -0
  46. data/lib/datagrid/helper.rb +436 -69
  47. data/lib/datagrid/ordering.rb +26 -29
  48. data/lib/datagrid/rspec.rb +6 -10
  49. data/lib/datagrid/utils.rb +37 -30
  50. data/lib/datagrid/version.rb +3 -1
  51. data/lib/datagrid.rb +8 -28
  52. data/templates/base.rb.erb +6 -4
  53. data/templates/grid.rb.erb +1 -1
  54. metadata +17 -17
  55. data/app/assets/stylesheets/datagrid.sass +0 -134
  56. data/lib/datagrid/filters/composite_filters.rb +0 -49
  57. data/lib/datagrid/renderer.rb +0 -157
  58. data/lib/datagrid/scaffold.rb +0 -129
  59. data/lib/tasks/datagrid_tasks.rake +0 -15
  60. data/templates/controller.rb.erb +0 -6
  61. data/templates/index.html.erb +0 -5
@@ -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
- require "datagrid/columns/column"
8
-
9
- # @!method default_column_options=
10
- # @param value [Hash] default options passed to #column method call
11
- # @return [Hash] default options passed to #column method call
12
- # @example
13
- # # Disable default order
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
- # @return [Hash]
20
- # @see #default_column_options=
21
-
22
- # @!method batch_size=
23
- # @param value [Integer] Specify a default batch size when generating CSV or just data. Default: 1000
24
- # @return [Integer] Specify a default batch size when generating CSV or just data.
25
- # @example
26
- # self.batch_size = 500
27
- # # Disable batches
28
- # self.batch_size = nil
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
- # @return [Integer]
33
- # @see #batch_size=
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 ClassMethods
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
- # content_tag(:strong, value)
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 } # a shortcut
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.is_a?(Class) ? presenter.new(model) : 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(child_class)
157
- child_class.columns_array = self.columns_array.clone
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 if you want to limit data only to specified columns
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 if you want to limit data only to specified columns
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 values are column values for the given asset
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
- self.data_columns.each do |column|
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 if you want to limit data only to specified columns
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
- self.row_for(asset, *column_names)
431
+ row_for(asset, *column_names)
237
432
  end
238
433
  end
239
434
 
240
- # @param column_names [Array<String>] list of column names if you want to limit data only to specified columns
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
- self.rows(*column_names).unshift(self.header(*column_names))
439
+ rows(*column_names).unshift(header(*column_names))
244
440
  end
245
441
 
246
- # Return Array of Hashes where keys are column names and values are column values
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: self.header(*column_names),
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 if you want to limit data only to specified columns
304
- # @return columns that can be represented in plain data(non-html) way
305
- def data_columns(*column_names, **options)
306
- self.columns(*column_names, **options, data: true)
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, **options)
312
- self.columns(*column_names, **options, html: true)
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 definition by name
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
- # content_tag(:strong, value)
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 block_given?
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
- # class MyGrid
349
- # scope { User }
350
- # column(:id)
351
- # column(:name)
352
- # column(:number_of_purchases) do |user|
353
- # user.purchases.count
354
- # end
355
- # end
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
- # row = MyGrid.new.data_row(User.last)
358
- # row.id # => user.id
359
- # row.number_of_purchases # => user.purchases.count
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 that are possible to be displayed for the current grid object
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 = column_by_name(column_name)
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
- raise(Datagrid::CacheKeyError, "Datagrid Cache key is #{key.inspect} for #{asset.inspect}.")
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, "#{self} is setup to use cache. But there was appropriate cache key found for #{asset.inspect}. Please set cached option to block with asset as argument and cache key as returning value to resolve the issue."
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 && batch_size > 0
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 < 0
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 > 0
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, *args, &blk)
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