datagrid 1.8.1 → 2.0.0.pre.alpha
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/CHANGELOG.md +31 -7
- data/{Readme.markdown → README.md} +46 -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 -5
- 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 -8
- data/lib/datagrid/active_model.rb +9 -17
- data/lib/datagrid/base.rb +39 -0
- data/lib/datagrid/column_names_attribute.rb +12 -12
- data/lib/datagrid/columns/column.rb +155 -133
- data/lib/datagrid/columns.rb +495 -282
- data/lib/datagrid/configuration.rb +23 -10
- data/lib/datagrid/core.rb +184 -150
- data/lib/datagrid/deprecated_object.rb +20 -0
- data/lib/datagrid/drivers/abstract_driver.rb +13 -25
- data/lib/datagrid/drivers/active_record.rb +24 -26
- data/lib/datagrid/drivers/array.rb +26 -17
- data/lib/datagrid/drivers/mongo_mapper.rb +15 -14
- data/lib/datagrid/drivers/mongoid.rb +16 -18
- 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 -27
- 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 +234 -106
- 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 +81 -87
- data/lib/datagrid/rspec.rb +8 -12
- data/lib/datagrid/utils.rb +42 -38
- data/lib/datagrid/version.rb +3 -1
- data/lib/datagrid.rb +18 -28
- data/templates/base.rb.erb +33 -7
- data/templates/grid.rb.erb +1 -1
- metadata +18 -19
- 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
@@ -1,14 +1,27 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
def self.configuration
|
4
|
-
@configuration ||= Configuration.new
|
5
|
-
end
|
1
|
+
# frozen_string_literal: true
|
6
2
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
#
|
3
|
+
module Datagrid
|
4
|
+
# ## Configuration
|
5
|
+
#
|
6
|
+
# Datagrid provides several configuration options.
|
7
|
+
#
|
8
|
+
# Here is the API reference and a description of the available options:
|
9
|
+
#
|
10
|
+
# ``` ruby
|
11
|
+
# Datagrid.configure do |config|
|
12
|
+
# # Defines date formats that can be used to parse dates.
|
13
|
+
# # Note: Multiple formats can be specified. The first format is used to format dates as strings,
|
14
|
+
# # while other formats are used only for parsing dates from strings (e.g., if your app supports multiple formats).
|
15
|
+
# config.date_formats = ["%m/%d/%Y", "%Y-%m-%d"]
|
16
|
+
#
|
17
|
+
# # Defines timestamp formats that can be used to parse timestamps.
|
18
|
+
# # Note: Multiple formats can be specified. The first format is used to format timestamps as strings,
|
19
|
+
# # while other formats are used only for parsing timestamps from strings (e.g., if your app supports multiple formats).
|
20
|
+
# config.datetime_formats = ["%m/%d/%Y %h:%M", "%Y-%m-%d %h:%M:%s"]
|
21
|
+
# end
|
22
|
+
# ```
|
23
|
+
#
|
24
|
+
# These options can be set globally in your application to customize Datagrid’s behavior.
|
12
25
|
class Configuration
|
13
26
|
# @return [Array<String>] Date parsing formats
|
14
27
|
attr_accessor :date_formats
|
data/lib/datagrid/core.rb
CHANGED
@@ -1,48 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "datagrid/drivers"
|
2
4
|
require "active_support/core_ext/class/attribute"
|
5
|
+
require "active_model/attribute_assignment"
|
3
6
|
|
4
7
|
module Datagrid
|
8
|
+
# Simple example of using Datagrid scope as the assets source to be queried from the database.
|
9
|
+
#
|
10
|
+
# In most cases, the scope is a model class with some default ORM scopes, like `order` or `includes`:
|
11
|
+
#
|
12
|
+
# The scope is also used to:
|
13
|
+
# - Choose an ORM driver (e.g., Mongoid, ActiveRecord, etc.).
|
14
|
+
# - Association preloading
|
15
|
+
# - Columns Providing default order
|
16
|
+
#
|
17
|
+
# You can set the scope at class level or instance level.
|
18
|
+
# Both having appropriate use cases
|
19
|
+
#
|
20
|
+
# @example Defining a scope in a grid class
|
21
|
+
# class ProjectsGrid
|
22
|
+
# include Datagrid
|
23
|
+
# scope { Project.includes(:category) }
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# @example Setting a scope at the instance level
|
27
|
+
# grid = ProjectsGrid.new(grid_params) do |scope|
|
28
|
+
# scope.where(owner_id: current_user.id)
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# grid.assets # => SELECT * FROM projects WHERE projects.owner_id = ? AND [other filtering conditions]
|
32
|
+
#
|
33
|
+
# @example Retrieving and redefining the scope
|
34
|
+
# grid.scope # => SELECT * FROM projects WHERE projects.user_id = ?
|
35
|
+
# grid.redefined_scope? # => true
|
36
|
+
#
|
37
|
+
# # Reset scope to default class value
|
38
|
+
# grid.reset_scope
|
39
|
+
# grid.assets # => SELECT * FROM projects
|
40
|
+
# grid.redefined_scope? # => false
|
41
|
+
#
|
42
|
+
# # Overwriting the scope (ignoring previously defined)
|
43
|
+
# grid.scope { current_user.projects }
|
44
|
+
# grid.redefined_scope? # => true
|
5
45
|
module Core
|
46
|
+
include ::ActiveModel::AttributeAssignment
|
6
47
|
|
7
48
|
# @!visibility private
|
8
49
|
def self.included(base)
|
9
50
|
base.extend ClassMethods
|
10
51
|
base.class_eval do
|
11
52
|
class_attribute :scope_value
|
12
|
-
|
13
|
-
class_attribute :datagrid_attributes, instance_writer: false
|
14
|
-
self.datagrid_attributes = []
|
15
|
-
|
53
|
+
class_attribute :datagrid_attributes, instance_writer: false, default: []
|
16
54
|
class_attribute :dynamic_block, instance_writer: false
|
17
|
-
class_attribute :forbidden_attributes_protection, instance_writer: false
|
18
|
-
self.forbidden_attributes_protection = false
|
19
|
-
if defined?(::ActiveModel::AttributeAssignment)
|
20
|
-
include ::ActiveModel::AttributeAssignment
|
21
|
-
end
|
55
|
+
class_attribute :forbidden_attributes_protection, instance_writer: false, default: false
|
22
56
|
end
|
23
|
-
base.include InstanceMethods
|
24
57
|
end
|
25
58
|
|
26
59
|
module ClassMethods
|
27
|
-
|
28
60
|
# @!visibility private
|
29
61
|
def datagrid_attribute(name, &block)
|
30
|
-
|
31
|
-
block ||= lambda do |value|
|
32
|
-
value
|
33
|
-
end
|
34
|
-
datagrid_attributes << name
|
35
|
-
define_method name do
|
36
|
-
instance_variable_get("@#{name}")
|
37
|
-
end
|
62
|
+
return if datagrid_attributes.include?(name)
|
38
63
|
|
39
|
-
|
40
|
-
|
41
|
-
|
64
|
+
datagrid_attributes << name
|
65
|
+
define_method name do
|
66
|
+
instance_variable_get("@#{name}")
|
67
|
+
end
|
68
|
+
|
69
|
+
define_method :"#{name}=" do |value|
|
70
|
+
instance_variable_set("@#{name}", block ? instance_exec(value, &block) : value)
|
42
71
|
end
|
43
72
|
end
|
44
73
|
|
45
|
-
# Defines a scope
|
74
|
+
# Defines a relation scope of database models to be filtered
|
46
75
|
# @return [void]
|
47
76
|
# @example
|
48
77
|
# scope { User }
|
@@ -115,163 +144,168 @@ module Datagrid
|
|
115
144
|
end
|
116
145
|
end
|
117
146
|
|
118
|
-
|
119
|
-
|
147
|
+
# @!visibility private
|
120
148
|
def check_scope_defined!(message = nil)
|
121
149
|
message ||= "#{self}.scope is not defined"
|
122
150
|
raise(Datagrid::ConfigurationError, message) unless scope_value
|
123
151
|
end
|
124
152
|
|
153
|
+
protected
|
154
|
+
|
155
|
+
# @!visibility private
|
125
156
|
def inherited(child_class)
|
126
|
-
super
|
127
|
-
child_class.datagrid_attributes =
|
157
|
+
super
|
158
|
+
child_class.datagrid_attributes = datagrid_attributes.clone
|
128
159
|
end
|
129
|
-
|
130
160
|
end
|
131
161
|
|
132
|
-
|
162
|
+
# @param [Hash{String, Symbol => Object}] attributes a hash of attributes to initialize the object
|
163
|
+
# @yield [block] an optional block that is passed to the scope method for further customization
|
164
|
+
# @return [void] Initializes a new instance with optional attributes and an optional block.
|
165
|
+
def initialize(attributes = nil, &block)
|
166
|
+
super()
|
133
167
|
|
134
|
-
|
135
|
-
super()
|
168
|
+
self.attributes = attributes if attributes
|
136
169
|
|
137
|
-
|
138
|
-
|
139
|
-
end
|
170
|
+
instance_eval(&dynamic_block) if dynamic_block
|
171
|
+
return unless block_given?
|
140
172
|
|
141
|
-
|
142
|
-
|
143
|
-
self.scope(&block)
|
144
|
-
end
|
145
|
-
end
|
173
|
+
scope(&block)
|
174
|
+
end
|
146
175
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
176
|
+
# @return [Hash{Symbol => Object}] grid attributes including filter values and ordering values
|
177
|
+
# @example
|
178
|
+
# class UsersGrid < ApplicationGrid
|
179
|
+
# scope { User }
|
180
|
+
# filter(:first_name, :string)
|
181
|
+
# filter(:last_name, :string)
|
182
|
+
# end
|
183
|
+
#
|
184
|
+
# grid = UsersGrid.new(first_name: 'John', last_name: 'Smith')
|
185
|
+
# grid.attributes # => {first_name: 'John', last_name: 'Smith', order: nil, descending: nil}
|
186
|
+
def attributes
|
187
|
+
result = {}
|
188
|
+
datagrid_attributes.each do |name|
|
189
|
+
result[name] = self[name]
|
154
190
|
end
|
191
|
+
result
|
192
|
+
end
|
155
193
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
194
|
+
# @param [String, Symbol] attribute attribute name
|
195
|
+
# @return [Object] Any datagrid attribute value
|
196
|
+
def [](attribute)
|
197
|
+
public_send(attribute)
|
198
|
+
end
|
160
199
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
200
|
+
# Assigns any datagrid attribute
|
201
|
+
# @param attribute [Symbol, String] Datagrid attribute name
|
202
|
+
# @param value [Object] Datagrid attribute value
|
203
|
+
# @return [void]
|
204
|
+
def []=(attribute, value)
|
205
|
+
public_send(:"#{attribute}=", value)
|
206
|
+
end
|
168
207
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
208
|
+
# @return [Object] a scope relation (e.g ActiveRecord::Relation) with all applied filters
|
209
|
+
def assets
|
210
|
+
scope
|
211
|
+
end
|
173
212
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
attributes.each do |name, value|
|
183
|
-
self[name] = value
|
184
|
-
end
|
185
|
-
self
|
186
|
-
end
|
213
|
+
# @return [Hash{Symbol => Object}] serializable query arguments skipping all nil values
|
214
|
+
# @example
|
215
|
+
# grid = ProductsGrid.new(category: 'dresses', available: true)
|
216
|
+
# grid.as_query # => {category: 'dresses', available: true}
|
217
|
+
def as_query
|
218
|
+
attributes = self.attributes.clone
|
219
|
+
attributes.each do |key, value|
|
220
|
+
attributes.delete(key) if value.nil?
|
187
221
|
end
|
222
|
+
attributes
|
223
|
+
end
|
188
224
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
end
|
198
|
-
attributes
|
199
|
-
end
|
225
|
+
# @return [Hash{Symbol => Hash{Symbol => Object}}] query parameters to link this grid from a page
|
226
|
+
# @example
|
227
|
+
# grid = ProductsGrid.new(category: 'dresses', available: true)
|
228
|
+
# Rails.application.routes.url_helpers.products_path(grid.query_params)
|
229
|
+
# # => "/products?products_grid[category]=dresses&products_grid[available]=true"
|
230
|
+
def query_params(attributes = {})
|
231
|
+
{ param_name.to_sym => as_query.merge(attributes) }
|
232
|
+
end
|
200
233
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
234
|
+
# @return [void] redefines scope at instance level
|
235
|
+
# @example
|
236
|
+
# class MyGrid
|
237
|
+
# scope { Article.order('created_at desc') }
|
238
|
+
# end
|
239
|
+
#
|
240
|
+
# grid = MyGrid.new
|
241
|
+
# grid.scope do |scope|
|
242
|
+
# scope.where(author_id: current_user.id)
|
243
|
+
# end
|
244
|
+
# grid.assets
|
245
|
+
# # => SELECT * FROM articles WHERE author_id = ?
|
246
|
+
# # ORDER BY created_at desc
|
247
|
+
def scope(&block)
|
248
|
+
if block_given?
|
249
|
+
current_scope = scope
|
250
|
+
self.scope_value = proc {
|
251
|
+
Datagrid::Utils.apply_args(current_scope, &block)
|
252
|
+
}
|
253
|
+
self
|
254
|
+
else
|
255
|
+
scope = original_scope
|
256
|
+
driver.to_scope(scope)
|
208
257
|
end
|
258
|
+
end
|
209
259
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
#
|
216
|
-
# grid = MyGrid.new
|
217
|
-
# grid.scope do |scope|
|
218
|
-
# scope.where(author_id: current_user.id)
|
219
|
-
# end
|
220
|
-
# grid.assets
|
221
|
-
# # => SELECT * FROM articles WHERE author_id = ?
|
222
|
-
# # ORDER BY created_at desc
|
223
|
-
def scope(&block)
|
224
|
-
if block_given?
|
225
|
-
current_scope = scope
|
226
|
-
self.scope_value = proc {
|
227
|
-
Datagrid::Utils.apply_args(current_scope, &block)
|
228
|
-
}
|
229
|
-
self
|
230
|
-
else
|
231
|
-
scope = original_scope
|
232
|
-
driver.to_scope(scope)
|
233
|
-
end
|
234
|
-
end
|
260
|
+
# @!visibility private
|
261
|
+
def original_scope
|
262
|
+
self.class.check_scope_defined!
|
263
|
+
scope_value.call
|
264
|
+
end
|
235
265
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
end
|
266
|
+
# @return [void] Resets current instance scope to default scope defined in a class
|
267
|
+
def reset_scope
|
268
|
+
self.scope_value = self.class.scope_value
|
269
|
+
end
|
241
270
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
end
|
271
|
+
# @return [Boolean] true if the scope was redefined for this instance of grid object
|
272
|
+
def redefined_scope?
|
273
|
+
self.class.scope_value != scope_value
|
274
|
+
end
|
247
275
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
276
|
+
# @!visibility private
|
277
|
+
def driver
|
278
|
+
self.class.driver
|
279
|
+
end
|
252
280
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
281
|
+
# @return [String] a datagrid attributes and their values in inspection form
|
282
|
+
def inspect
|
283
|
+
attrs = attributes.map do |key, value|
|
284
|
+
"#{key}: #{value.inspect}"
|
285
|
+
end.join(", ")
|
286
|
+
"#<#{self.class} #{attrs}>"
|
287
|
+
end
|
257
288
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
289
|
+
def ==(other)
|
290
|
+
self.class == other.class &&
|
291
|
+
attributes == other.attributes &&
|
292
|
+
scope == other.scope
|
293
|
+
end
|
262
294
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
end.join(", ")
|
268
|
-
"#<#{self.class} #{attrs}>"
|
269
|
-
end
|
295
|
+
# @return [void] Resets loaded assets and column values cache
|
296
|
+
def reset
|
297
|
+
assets.reset
|
298
|
+
end
|
270
299
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
300
|
+
protected
|
301
|
+
|
302
|
+
def sanitize_for_mass_assignment(attributes)
|
303
|
+
if forbidden_attributes_protection
|
304
|
+
super
|
305
|
+
elsif defined?(ActionController::Parameters) && attributes.is_a?(ActionController::Parameters)
|
306
|
+
attributes.to_unsafe_h
|
307
|
+
else
|
308
|
+
attributes
|
275
309
|
end
|
276
310
|
end
|
277
311
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datagrid
|
4
|
+
# @!visibility private
|
5
|
+
class DeprecatedObject < BasicObject
|
6
|
+
def initialize(real_object, &block)
|
7
|
+
@real_object = real_object
|
8
|
+
@block = block
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(method_name, ...)
|
12
|
+
@block.call
|
13
|
+
@real_object.public_send(method_name, ...)
|
14
|
+
end
|
15
|
+
|
16
|
+
def respond_to_missing?(method_name, include_private = false)
|
17
|
+
@real_object.respond_to?(method_name, include_private)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,20 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Datagrid
|
2
4
|
module Drivers
|
3
5
|
# @!visibility private
|
4
6
|
class AbstractDriver
|
7
|
+
TIMESTAMP_CLASSES = [DateTime, Time, ActiveSupport::TimeWithZone].freeze
|
5
8
|
|
6
|
-
|
7
|
-
|
8
|
-
class_attribute :subclasses
|
9
|
+
class_attribute :subclasses, default: []
|
9
10
|
|
10
11
|
def self.inherited(base)
|
11
|
-
super
|
12
|
-
|
13
|
-
self.subclasses << base
|
12
|
+
super
|
13
|
+
subclasses << base
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.guess_driver(scope)
|
17
|
-
|
17
|
+
subclasses.find do |driver_class|
|
18
18
|
driver_class.match?(scope)
|
19
19
|
end || raise(Datagrid::ConfigurationError, "ORM Driver not found for scope: #{scope.inspect}.")
|
20
20
|
end
|
@@ -55,7 +55,7 @@ module Datagrid
|
|
55
55
|
raise NotImplementedError
|
56
56
|
end
|
57
57
|
|
58
|
-
def
|
58
|
+
def scope_has_column?(scope, column_name)
|
59
59
|
raise NotImplementedError
|
60
60
|
end
|
61
61
|
|
@@ -63,7 +63,7 @@ module Datagrid
|
|
63
63
|
raise NotImplementedError
|
64
64
|
end
|
65
65
|
|
66
|
-
def
|
66
|
+
def timestamp_column?(scope, field)
|
67
67
|
normalized_column_type(scope, field) == :timestamp
|
68
68
|
end
|
69
69
|
|
@@ -84,28 +84,15 @@ module Datagrid
|
|
84
84
|
end
|
85
85
|
|
86
86
|
def append_column_queries(assets, columns)
|
87
|
-
if columns.present?
|
88
|
-
|
89
|
-
|
90
|
-
assets
|
91
|
-
end
|
87
|
+
raise NotImplementedError if columns.present?
|
88
|
+
|
89
|
+
assets
|
92
90
|
end
|
93
91
|
|
94
92
|
def default_cache_key(asset)
|
95
93
|
raise NotImplementedError
|
96
94
|
end
|
97
95
|
|
98
|
-
def where_by_timestamp_gotcha(scope, name, value)
|
99
|
-
value = Datagrid::Utils.format_date_as_timestamp(value)
|
100
|
-
if value.first
|
101
|
-
scope = greater_equal(scope, name, value.first)
|
102
|
-
end
|
103
|
-
if value.last
|
104
|
-
scope = less_equal(scope, name, value.last)
|
105
|
-
end
|
106
|
-
scope
|
107
|
-
end
|
108
|
-
|
109
96
|
def default_preload(scope, value)
|
110
97
|
raise NotImplementedError
|
111
98
|
end
|
@@ -115,6 +102,7 @@ module Datagrid
|
|
115
102
|
end
|
116
103
|
|
117
104
|
protected
|
105
|
+
|
118
106
|
def timestamp_class?(klass)
|
119
107
|
TIMESTAMP_CLASSES.include?(klass)
|
120
108
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Datagrid
|
2
4
|
module Drivers
|
3
5
|
# @!visibility private
|
4
6
|
class ActiveRecord < AbstractDriver
|
5
|
-
|
6
7
|
def self.match?(scope)
|
7
8
|
return false unless defined?(::ActiveRecord)
|
9
|
+
|
8
10
|
if scope.is_a?(Class)
|
9
11
|
scope.ancestors.include?(::ActiveRecord::Base)
|
10
12
|
else
|
@@ -14,6 +16,7 @@ module Datagrid
|
|
14
16
|
|
15
17
|
def to_scope(scope)
|
16
18
|
return scope if scope.is_a?(::ActiveRecord::Relation)
|
19
|
+
|
17
20
|
# Model class or Active record association
|
18
21
|
# ActiveRecord association class hides itself under an Array
|
19
22
|
# We can only reveal it by checking if it respond to some specific
|
@@ -29,10 +32,8 @@ module Datagrid
|
|
29
32
|
|
30
33
|
def append_column_queries(assets, columns)
|
31
34
|
if columns.present?
|
32
|
-
if assets.select_values.empty?
|
33
|
-
|
34
|
-
end
|
35
|
-
columns = columns.map {|c| "#{c.query} AS #{c.name}"}
|
35
|
+
assets = assets.select(assets.klass.arel_table[Arel.star]) if assets.select_values.empty?
|
36
|
+
columns = columns.map { |c| "#{c.query} AS #{c.name}" }
|
36
37
|
assets = assets.select(*columns)
|
37
38
|
end
|
38
39
|
assets
|
@@ -61,7 +62,7 @@ module Datagrid
|
|
61
62
|
end
|
62
63
|
|
63
64
|
def default_order(scope, column_name)
|
64
|
-
|
65
|
+
scope_has_column?(scope, column_name) ? prefix_table_name(scope, column_name) : nil
|
65
66
|
end
|
66
67
|
|
67
68
|
def greater_equal(scope, field, value)
|
@@ -72,7 +73,7 @@ module Datagrid
|
|
72
73
|
scope.where(["#{prefix_table_name(scope, field)} <= ?", value])
|
73
74
|
end
|
74
75
|
|
75
|
-
def
|
76
|
+
def scope_has_column?(scope, column_name)
|
76
77
|
scope.column_names.include?(column_name.to_s)
|
77
78
|
rescue ::ActiveRecord::StatementInvalid
|
78
79
|
false
|
@@ -88,15 +89,16 @@ module Datagrid
|
|
88
89
|
end
|
89
90
|
|
90
91
|
def normalized_column_type(scope, field)
|
91
|
-
return nil unless
|
92
|
+
return nil unless scope_has_column?(scope, field)
|
93
|
+
|
92
94
|
builtin_type = scope.columns_hash[field.to_s].type
|
93
95
|
{
|
94
|
-
[
|
95
|
-
[
|
96
|
-
[
|
96
|
+
%i[string text time binary] => :string,
|
97
|
+
%i[integer primary_key] => :integer,
|
98
|
+
%i[float decimal] => :float,
|
97
99
|
[:date] => :date,
|
98
|
-
[
|
99
|
-
[:boolean] => :boolean
|
100
|
+
%i[datetime timestamp] => :timestamp,
|
101
|
+
[:boolean] => :boolean,
|
100
102
|
}.each do |keys, value|
|
101
103
|
return value if keys.include?(builtin_type)
|
102
104
|
end
|
@@ -106,6 +108,7 @@ module Datagrid
|
|
106
108
|
if scope.limit_value
|
107
109
|
raise Datagrid::ConfigurationError, "ActiveRecord can not use batches in combination with SQL limit"
|
108
110
|
end
|
111
|
+
|
109
112
|
options = batch_size ? { batch_size: batch_size } : {}
|
110
113
|
scope.find_each(**options, &block)
|
111
114
|
end
|
@@ -119,28 +122,23 @@ module Datagrid
|
|
119
122
|
end
|
120
123
|
|
121
124
|
def can_preload?(scope, association)
|
122
|
-
!!
|
125
|
+
!!scope.klass.reflect_on_association(association)
|
123
126
|
end
|
124
127
|
|
125
128
|
protected
|
126
129
|
|
127
130
|
def prefix_table_name(scope, field)
|
128
|
-
|
131
|
+
scope_has_column?(scope, field) ? [scope.table_name, field].join(".") : field
|
129
132
|
end
|
130
133
|
|
131
134
|
def contains_predicate
|
132
|
-
defined?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) &&
|
133
|
-
|
134
|
-
|
135
|
+
if defined?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) &&
|
136
|
+
::ActiveRecord::Base.connection.is_a?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
|
137
|
+
"ilike"
|
138
|
+
else
|
139
|
+
"like"
|
140
|
+
end
|
135
141
|
end
|
136
142
|
end
|
137
143
|
end
|
138
144
|
end
|
139
|
-
|
140
|
-
if defined?(ActiveRecord::Base)
|
141
|
-
ActiveRecord::Base.class_eval do
|
142
|
-
def self.datagrid_where_by_timestamp(column, value)
|
143
|
-
Datagrid::Drivers::ActiveRecord.new.where_by_timestamp_gotcha(self, column, value)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|