rails_admin 3.1.2 → 3.3.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/Gemfile +20 -15
- data/README.md +2 -2
- data/app/assets/javascripts/rails_admin/application.js.erb +3 -2
- data/app/assets/stylesheets/rails_admin/application.scss.erb +1 -1
- data/app/controllers/rails_admin/main_controller.rb +5 -1
- data/app/helpers/rails_admin/application_helper.rb +4 -0
- data/app/helpers/rails_admin/form_builder.rb +2 -2
- data/app/helpers/rails_admin/main_helper.rb +1 -1
- data/app/views/layouts/rails_admin/_head.html.erb +10 -7
- data/app/views/rails_admin/main/_form_boolean.html.erb +2 -2
- data/app/views/rails_admin/main/_form_filtering_multiselect.html.erb +5 -35
- data/app/views/rails_admin/main/_form_filtering_select.html.erb +6 -18
- data/app/views/rails_admin/main/_form_nested_many.html.erb +1 -1
- data/app/views/rails_admin/main/_form_nested_one.html.erb +1 -1
- data/app/views/rails_admin/main/_form_polymorphic_association.html.erb +12 -21
- data/app/views/rails_admin/main/delete.html.erb +1 -1
- data/app/views/rails_admin/main/index.html.erb +2 -2
- data/config/initializers/active_record_extensions.rb +0 -23
- data/lib/generators/rails_admin/importmap_formatter.rb +1 -1
- data/lib/generators/rails_admin/install_generator.rb +13 -1
- data/lib/generators/rails_admin/templates/rails_admin.vite.js +2 -0
- data/lib/rails_admin/abstract_model.rb +18 -7
- data/lib/rails_admin/adapters/active_record/association.rb +27 -10
- data/lib/rails_admin/adapters/active_record/object_extension.rb +0 -18
- data/lib/rails_admin/adapters/active_record.rb +52 -5
- data/lib/rails_admin/adapters/active_record.rb.bak +348 -0
- data/lib/rails_admin/adapters/mongoid/association.rb +3 -3
- data/lib/rails_admin/adapters/mongoid/bson.rb +1 -0
- data/lib/rails_admin/adapters/mongoid/object_extension.rb +0 -5
- data/lib/rails_admin/adapters/mongoid.rb +8 -3
- data/lib/rails_admin/config/actions/index.rb +5 -3
- data/lib/rails_admin/config/fields/association.rb +41 -2
- data/lib/rails_admin/config/fields/base.rb +5 -5
- data/lib/rails_admin/config/fields/collection_association.rb +90 -0
- data/lib/rails_admin/config/fields/factories/active_storage.rb +2 -2
- data/lib/rails_admin/config/fields/factories/carrierwave.rb +1 -1
- data/lib/rails_admin/config/fields/factories/dragonfly.rb +1 -1
- data/lib/rails_admin/config/fields/factories/paperclip.rb +1 -1
- data/lib/rails_admin/config/fields/factories/shrine.rb +1 -1
- data/lib/rails_admin/config/fields/singular_association.rb +59 -0
- data/lib/rails_admin/config/fields/types/active_storage.rb +12 -7
- data/lib/rails_admin/config/fields/types/all.rb +0 -1
- data/lib/rails_admin/config/fields/types/belongs_to_association.rb +17 -20
- data/lib/rails_admin/config/fields/types/dragonfly.rb +0 -1
- data/lib/rails_admin/config/fields/types/file_upload.rb +7 -1
- data/lib/rails_admin/config/fields/types/has_and_belongs_to_many_association.rb +2 -2
- data/lib/rails_admin/config/fields/types/has_many_association.rb +2 -24
- data/lib/rails_admin/config/fields/types/has_one_association.rb +12 -22
- data/lib/rails_admin/config/fields/types/multiple_active_storage.rb +13 -8
- data/lib/rails_admin/config/fields/types/multiple_file_upload.rb +7 -1
- data/lib/rails_admin/config/fields/types/polymorphic_association.rb +32 -9
- data/lib/rails_admin/config.rb +5 -0
- data/lib/rails_admin/engine.rb +5 -0
- data/lib/rails_admin/extensions/paper_trail/auditing_adapter.rb +1 -1
- data/lib/rails_admin/extensions/url_for_extension.rb +15 -0
- data/lib/rails_admin/support/composite_keys_serializer.rb +15 -0
- data/lib/rails_admin/support/datetime.rb +1 -0
- data/lib/rails_admin/version.rb +2 -2
- data/package.json +2 -2
- data/src/rails_admin/abstract-select.js +30 -0
- data/src/rails_admin/base.js +4 -1
- data/src/rails_admin/filtering-multiselect.js +2 -4
- data/src/rails_admin/filtering-select.js +2 -4
- metadata +41 -16
- data/lib/rails_admin/adapters/composite_primary_keys/association.rb +0 -45
- data/lib/rails_admin/adapters/composite_primary_keys.rb +0 -40
- data/lib/rails_admin/config/fields/types/composite_keys_belongs_to_association.rb +0 -31
@@ -15,7 +15,7 @@ module RailsAdmin
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def get(id, scope = scoped)
|
18
|
-
object = scope
|
18
|
+
object = primary_key_scope(scope, id).first
|
19
19
|
return unless object
|
20
20
|
|
21
21
|
object.extend(ObjectExtension)
|
@@ -72,6 +72,14 @@ module RailsAdmin
|
|
72
72
|
|
73
73
|
delegate :primary_key, :table_name, to: :model, prefix: false
|
74
74
|
|
75
|
+
def quoted_table_name
|
76
|
+
model.quoted_table_name
|
77
|
+
end
|
78
|
+
|
79
|
+
def quote_column_name(name)
|
80
|
+
model.connection.quote_column_name(name)
|
81
|
+
end
|
82
|
+
|
75
83
|
def encoding
|
76
84
|
adapter =
|
77
85
|
if ::ActiveRecord::Base.respond_to?(:connection_db_config)
|
@@ -107,10 +115,42 @@ module RailsAdmin
|
|
107
115
|
true
|
108
116
|
end
|
109
117
|
|
118
|
+
def format_id(id)
|
119
|
+
if primary_key.is_a? Array
|
120
|
+
RailsAdmin.config.composite_keys_serializer.serialize(id)
|
121
|
+
else
|
122
|
+
id
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def parse_id(id)
|
127
|
+
if primary_key.is_a?(Array)
|
128
|
+
ids = RailsAdmin.config.composite_keys_serializer.deserialize(id)
|
129
|
+
primary_key.each_with_index do |key, i|
|
130
|
+
ids[i] = model.type_for_attribute(key).cast(ids[i])
|
131
|
+
end
|
132
|
+
ids
|
133
|
+
else
|
134
|
+
id
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
110
138
|
private
|
111
139
|
|
140
|
+
def primary_key_scope(scope, id)
|
141
|
+
if primary_key.is_a? Array
|
142
|
+
scope.where(primary_key.zip(parse_id(id)).to_h)
|
143
|
+
else
|
144
|
+
scope.where(primary_key => id)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
112
148
|
def bulk_scope(scope, options)
|
113
|
-
|
149
|
+
if primary_key.is_a? Array
|
150
|
+
options[:bulk_ids].map { |id| primary_key_scope(scope, id) }.reduce(&:or)
|
151
|
+
else
|
152
|
+
scope.where(primary_key => options[:bulk_ids])
|
153
|
+
end
|
114
154
|
end
|
115
155
|
|
116
156
|
def sort_scope(scope, options)
|
@@ -172,7 +212,7 @@ module RailsAdmin
|
|
172
212
|
# "0055" is the filter index, no use here. o is the operator, v the value
|
173
213
|
def filter_scope(scope, filters, fields = config.list.fields.select(&:filterable?))
|
174
214
|
filters.each_pair do |field_name, filters_dump|
|
175
|
-
filters_dump.
|
215
|
+
filters_dump.each_value do |filter_dump|
|
176
216
|
wb = WhereBuilder.new(scope)
|
177
217
|
field = fields.detect { |f| f.name.to_s == field_name }
|
178
218
|
value = parse_field_value(field, filter_dump[:v])
|
@@ -201,6 +241,8 @@ module RailsAdmin
|
|
201
241
|
case @type
|
202
242
|
when :boolean
|
203
243
|
boolean_unary_operators
|
244
|
+
when :uuid
|
245
|
+
uuid_unary_operators
|
204
246
|
when :integer, :decimal, :float
|
205
247
|
numeric_unary_operators
|
206
248
|
else
|
@@ -230,6 +272,7 @@ module RailsAdmin
|
|
230
272
|
)
|
231
273
|
end
|
232
274
|
alias_method :numeric_unary_operators, :boolean_unary_operators
|
275
|
+
alias_method :uuid_unary_operators, :boolean_unary_operators
|
233
276
|
|
234
277
|
def range_filter(min, max)
|
235
278
|
if min && max && min == max
|
@@ -255,8 +298,12 @@ module RailsAdmin
|
|
255
298
|
end
|
256
299
|
|
257
300
|
def build_statement_for_boolean
|
258
|
-
|
259
|
-
|
301
|
+
case @value
|
302
|
+
when 'false', 'f', '0'
|
303
|
+
["(#{@column} IS NULL OR #{@column} = ?)", false]
|
304
|
+
when 'true', 't', '1'
|
305
|
+
["(#{@column} = ?)", true]
|
306
|
+
end
|
260
307
|
end
|
261
308
|
|
262
309
|
def column_for_value(value)
|
@@ -0,0 +1,348 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_record'
|
4
|
+
require 'rails_admin/adapters/active_record/association'
|
5
|
+
require 'rails_admin/adapters/active_record/model_extension'
|
6
|
+
require 'rails_admin/adapters/active_record/property'
|
7
|
+
|
8
|
+
module RailsAdmin
|
9
|
+
module Adapters
|
10
|
+
module ActiveRecord
|
11
|
+
DISABLED_COLUMN_TYPES = %i[tsvector blob binary spatial hstore geometry].freeze
|
12
|
+
|
13
|
+
def model_with_extension
|
14
|
+
@model_with_extension ||=
|
15
|
+
begin
|
16
|
+
klass = Class.new(model) do
|
17
|
+
include ModelExtension
|
18
|
+
end
|
19
|
+
klass.instance_eval <<-RUBY, __FILE__, __LINE__+1
|
20
|
+
def name
|
21
|
+
"#{@model_name.to_s}"
|
22
|
+
end
|
23
|
+
RUBY
|
24
|
+
klass
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def new(params = {})
|
29
|
+
model_with_extension.new(params)
|
30
|
+
end
|
31
|
+
|
32
|
+
def get(id, scope = nil)
|
33
|
+
scope = model_with_extension.merge(scope || scoped)
|
34
|
+
scope.where(primary_key => id).first
|
35
|
+
end
|
36
|
+
|
37
|
+
def scoped
|
38
|
+
model_with_extension.all
|
39
|
+
end
|
40
|
+
|
41
|
+
def first(options = {}, scope = nil)
|
42
|
+
all(options, scope).first
|
43
|
+
end
|
44
|
+
|
45
|
+
def all(options = {}, scope = nil)
|
46
|
+
scope = model_with_extension.merge(scope || scoped)
|
47
|
+
scope = scope.includes(options[:include]) if options[:include]
|
48
|
+
scope = scope.limit(options[:limit]) if options[:limit]
|
49
|
+
scope = bulk_scope(scope, options) if options[:bulk_ids]
|
50
|
+
scope = query_scope(scope, options[:query]) if options[:query]
|
51
|
+
scope = filter_scope(scope, options[:filters]) if options[:filters]
|
52
|
+
scope = scope.send(Kaminari.config.page_method_name, options[:page]).per(options[:per]) if options[:page] && options[:per]
|
53
|
+
scope = sort_scope(scope, options) if options[:sort]
|
54
|
+
scope
|
55
|
+
end
|
56
|
+
|
57
|
+
def count(options = {}, scope = nil)
|
58
|
+
all(options.merge(limit: false, page: false), scope).count(:all)
|
59
|
+
end
|
60
|
+
|
61
|
+
def destroy(objects)
|
62
|
+
Array.wrap(objects).each(&:destroy)
|
63
|
+
end
|
64
|
+
|
65
|
+
def associations
|
66
|
+
model.reflect_on_all_associations.collect do |association|
|
67
|
+
Association.new(association, model)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def properties
|
72
|
+
columns = model.columns.reject do |c|
|
73
|
+
c.type.blank? ||
|
74
|
+
DISABLED_COLUMN_TYPES.include?(c.type.to_sym) ||
|
75
|
+
c.try(:array)
|
76
|
+
end
|
77
|
+
columns.collect do |property|
|
78
|
+
Property.new(property, model)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def base_class
|
83
|
+
model.base_class
|
84
|
+
end
|
85
|
+
|
86
|
+
delegate :primary_key, :table_name, to: :model, prefix: false
|
87
|
+
|
88
|
+
def quoted_table_name
|
89
|
+
model.quoted_table_name
|
90
|
+
end
|
91
|
+
|
92
|
+
def quote_column_name(name)
|
93
|
+
model.connection.quote_column_name(name)
|
94
|
+
end
|
95
|
+
|
96
|
+
def encoding
|
97
|
+
adapter =
|
98
|
+
if ::ActiveRecord::Base.respond_to?(:connection_db_config)
|
99
|
+
::ActiveRecord::Base.connection_db_config.configuration_hash[:adapter]
|
100
|
+
else
|
101
|
+
::ActiveRecord::Base.connection_config[:adapter]
|
102
|
+
end
|
103
|
+
case adapter
|
104
|
+
when 'postgresql'
|
105
|
+
::ActiveRecord::Base.connection.select_one("SELECT ''::text AS str;").values.first.encoding
|
106
|
+
when 'mysql2'
|
107
|
+
if RUBY_ENGINE == 'jruby'
|
108
|
+
::ActiveRecord::Base.connection.select_one("SELECT '' AS str;").values.first.encoding
|
109
|
+
else
|
110
|
+
::ActiveRecord::Base.connection.raw_connection.encoding
|
111
|
+
end
|
112
|
+
when 'oracle_enhanced'
|
113
|
+
::ActiveRecord::Base.connection.select_one('SELECT dummy FROM DUAL').values.first.encoding
|
114
|
+
else
|
115
|
+
::ActiveRecord::Base.connection.select_one("SELECT '' AS str;").values.first.encoding
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def embedded?
|
120
|
+
false
|
121
|
+
end
|
122
|
+
|
123
|
+
def cyclic?
|
124
|
+
false
|
125
|
+
end
|
126
|
+
|
127
|
+
def adapter_supports_joins?
|
128
|
+
true
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def bulk_scope(scope, options)
|
134
|
+
scope.where(primary_key => options[:bulk_ids])
|
135
|
+
end
|
136
|
+
|
137
|
+
def sort_scope(scope, options)
|
138
|
+
direction = options[:sort_reverse] ? :asc : :desc
|
139
|
+
case options[:sort]
|
140
|
+
when String, Symbol
|
141
|
+
scope.reorder("#{options[:sort]} #{direction}")
|
142
|
+
when Array
|
143
|
+
scope.reorder(options[:sort].zip(Array.new(options[:sort].size) { direction }).to_h)
|
144
|
+
when Hash
|
145
|
+
scope.reorder(options[:sort].map { |table_name, column| "#{table_name}.#{column}" }.
|
146
|
+
zip(Array.new(options[:sort].size) { direction }).to_h)
|
147
|
+
else
|
148
|
+
raise ArgumentError.new("Unsupported sort value: #{options[:sort]}")
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
class WhereBuilder
|
153
|
+
def initialize(scope)
|
154
|
+
@statements = []
|
155
|
+
@values = []
|
156
|
+
@tables = []
|
157
|
+
@scope = scope
|
158
|
+
end
|
159
|
+
|
160
|
+
def add(field, value, operator)
|
161
|
+
field.searchable_columns.flatten.each do |column_infos|
|
162
|
+
statement, value1, value2 = StatementBuilder.new(column_infos[:column], column_infos[:type], value, operator, @scope.connection.adapter_name).to_statement
|
163
|
+
@statements << statement if statement.present?
|
164
|
+
@values << value1 unless value1.nil?
|
165
|
+
@values << value2 unless value2.nil?
|
166
|
+
table, column = column_infos[:column].split('.')
|
167
|
+
@tables.push(table) if column
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def build
|
172
|
+
scope = @scope.where(@statements.join(' OR '), *@values)
|
173
|
+
scope = scope.references(*@tables.uniq) if @tables.any?
|
174
|
+
scope
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def query_scope(scope, query, fields = config.list.fields.select(&:queryable?))
|
179
|
+
if config.list.search_by
|
180
|
+
scope.send(config.list.search_by, query)
|
181
|
+
else
|
182
|
+
wb = WhereBuilder.new(scope)
|
183
|
+
fields.each do |field|
|
184
|
+
value = parse_field_value(field, query)
|
185
|
+
wb.add(field, value, field.search_operator)
|
186
|
+
end
|
187
|
+
# OR all query statements
|
188
|
+
wb.build
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# filters example => {"string_field"=>{"0055"=>{"o"=>"like", "v"=>"test_value"}}, ...}
|
193
|
+
# "0055" is the filter index, no use here. o is the operator, v the value
|
194
|
+
def filter_scope(scope, filters, fields = config.list.fields.select(&:filterable?))
|
195
|
+
filters.each_pair do |field_name, filters_dump|
|
196
|
+
filters_dump.each_value do |filter_dump|
|
197
|
+
wb = WhereBuilder.new(scope)
|
198
|
+
field = fields.detect { |f| f.name.to_s == field_name }
|
199
|
+
value = parse_field_value(field, filter_dump[:v])
|
200
|
+
|
201
|
+
wb.add(field, value, (filter_dump[:o] || RailsAdmin::Config.default_search_operator))
|
202
|
+
# AND current filter statements to other filter statements
|
203
|
+
scope = wb.build
|
204
|
+
end
|
205
|
+
end
|
206
|
+
scope
|
207
|
+
end
|
208
|
+
|
209
|
+
def build_statement(column, type, value, operator)
|
210
|
+
StatementBuilder.new(column, type, value, operator, model.connection.adapter_name).to_statement
|
211
|
+
end
|
212
|
+
|
213
|
+
class StatementBuilder < RailsAdmin::AbstractModel::StatementBuilder
|
214
|
+
def initialize(column, type, value, operator, adapter_name)
|
215
|
+
super column, type, value, operator
|
216
|
+
@adapter_name = adapter_name
|
217
|
+
end
|
218
|
+
|
219
|
+
protected
|
220
|
+
|
221
|
+
def unary_operators
|
222
|
+
case @type
|
223
|
+
when :boolean
|
224
|
+
boolean_unary_operators
|
225
|
+
when :uuid
|
226
|
+
uuid_unary_operators
|
227
|
+
when :integer, :decimal, :float
|
228
|
+
numeric_unary_operators
|
229
|
+
else
|
230
|
+
generic_unary_operators
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
private
|
235
|
+
|
236
|
+
def generic_unary_operators
|
237
|
+
{
|
238
|
+
'_blank' => ["(#{@column} IS NULL OR #{@column} = '')"],
|
239
|
+
'_present' => ["(#{@column} IS NOT NULL AND #{@column} != '')"],
|
240
|
+
'_null' => ["(#{@column} IS NULL)"],
|
241
|
+
'_not_null' => ["(#{@column} IS NOT NULL)"],
|
242
|
+
'_empty' => ["(#{@column} = '')"],
|
243
|
+
'_not_empty' => ["(#{@column} != '')"],
|
244
|
+
}
|
245
|
+
end
|
246
|
+
|
247
|
+
def boolean_unary_operators
|
248
|
+
generic_unary_operators.merge(
|
249
|
+
'_blank' => ["(#{@column} IS NULL)"],
|
250
|
+
'_empty' => ["(#{@column} IS NULL)"],
|
251
|
+
'_present' => ["(#{@column} IS NOT NULL)"],
|
252
|
+
'_not_empty' => ["(#{@column} IS NOT NULL)"],
|
253
|
+
)
|
254
|
+
end
|
255
|
+
alias_method :numeric_unary_operators, :boolean_unary_operators
|
256
|
+
alias_method :uuid_unary_operators, :boolean_unary_operators
|
257
|
+
|
258
|
+
def range_filter(min, max)
|
259
|
+
if min && max && min == max
|
260
|
+
["(#{@column} = ?)", min]
|
261
|
+
elsif min && max
|
262
|
+
["(#{@column} BETWEEN ? AND ?)", min, max]
|
263
|
+
elsif min
|
264
|
+
["(#{@column} >= ?)", min]
|
265
|
+
elsif max
|
266
|
+
["(#{@column} <= ?)", max]
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def build_statement_for_type
|
271
|
+
case @type
|
272
|
+
when :boolean then build_statement_for_boolean
|
273
|
+
when :integer, :decimal, :float then build_statement_for_integer_decimal_or_float
|
274
|
+
when :string, :text, :citext then build_statement_for_string_or_text
|
275
|
+
when :enum then build_statement_for_enum
|
276
|
+
when :belongs_to_association then build_statement_for_belongs_to_association
|
277
|
+
when :uuid then build_statement_for_uuid
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def build_statement_for_boolean
|
282
|
+
case @value
|
283
|
+
when 'false', 'f', '0'
|
284
|
+
["(#{@column} IS NULL OR #{@column} = ?)", false]
|
285
|
+
when 'true', 't', '1'
|
286
|
+
["(#{@column} = ?)", true]
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def column_for_value(value)
|
291
|
+
["(#{@column} = ?)", value]
|
292
|
+
end
|
293
|
+
|
294
|
+
def build_statement_for_belongs_to_association
|
295
|
+
return if @value.blank?
|
296
|
+
|
297
|
+
["(#{@column} = ?)", @value.to_i] if @value.to_i.to_s == @value
|
298
|
+
end
|
299
|
+
|
300
|
+
def build_statement_for_string_or_text
|
301
|
+
return if @value.blank?
|
302
|
+
|
303
|
+
return ["(#{@column} = ?)", @value] if ['is', '='].include?(@operator)
|
304
|
+
|
305
|
+
@value = @value.mb_chars.downcase unless %w[postgresql postgis].include? ar_adapter
|
306
|
+
|
307
|
+
@value =
|
308
|
+
case @operator
|
309
|
+
when 'default', 'like', 'not_like'
|
310
|
+
"%#{@value}%"
|
311
|
+
when 'starts_with'
|
312
|
+
"#{@value}%"
|
313
|
+
when 'ends_with'
|
314
|
+
"%#{@value}"
|
315
|
+
else
|
316
|
+
return
|
317
|
+
end
|
318
|
+
|
319
|
+
if %w[postgresql postgis].include? ar_adapter
|
320
|
+
if @operator == 'not_like'
|
321
|
+
["(#{@column} NOT ILIKE ?)", @value]
|
322
|
+
else
|
323
|
+
["(#{@column} ILIKE ?)", @value]
|
324
|
+
end
|
325
|
+
elsif @operator == 'not_like'
|
326
|
+
["(LOWER(#{@column}) NOT LIKE ?)", @value]
|
327
|
+
else
|
328
|
+
["(LOWER(#{@column}) LIKE ?)", @value]
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
def build_statement_for_enum
|
333
|
+
return if @value.blank?
|
334
|
+
|
335
|
+
["(#{@column} IN (?))", Array.wrap(@value)]
|
336
|
+
end
|
337
|
+
|
338
|
+
def build_statement_for_uuid
|
339
|
+
column_for_value(@value) if /\A[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}\z/.match?(@value.to_s)
|
340
|
+
end
|
341
|
+
|
342
|
+
def ar_adapter
|
343
|
+
@adapter_name.downcase
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
@@ -46,7 +46,7 @@ module RailsAdmin
|
|
46
46
|
|
47
47
|
def klass
|
48
48
|
if polymorphic? && %i[referenced_in belongs_to].include?(macro)
|
49
|
-
polymorphic_parents(:mongoid,
|
49
|
+
polymorphic_parents(:mongoid, association.inverse_class_name, name) || []
|
50
50
|
else
|
51
51
|
association.klass
|
52
52
|
end
|
@@ -92,9 +92,9 @@ module RailsAdmin
|
|
92
92
|
def key_accessor
|
93
93
|
case macro.to_sym
|
94
94
|
when :has_many
|
95
|
-
"#{name.to_s.singularize}_ids"
|
95
|
+
:"#{name.to_s.singularize}_ids"
|
96
96
|
when :has_one
|
97
|
-
"#{name}_id"
|
97
|
+
:"#{name}_id"
|
98
98
|
when :embedded_in, :embeds_one, :embeds_many
|
99
99
|
nil
|
100
100
|
else
|
@@ -31,6 +31,7 @@ module RailsAdmin
|
|
31
31
|
Mongoid::Errors::InvalidFind
|
32
32
|
Moped::Errors::InvalidObjectId
|
33
33
|
BSON::InvalidObjectId
|
34
|
+
BSON::Error::InvalidObjectId
|
34
35
|
].exclude?(e.class.to_s)
|
35
36
|
end
|
36
37
|
|
@@ -152,7 +153,7 @@ module RailsAdmin
|
|
152
153
|
statements = []
|
153
154
|
|
154
155
|
filters.each_pair do |field_name, filters_dump|
|
155
|
-
filters_dump.
|
156
|
+
filters_dump.each_value do |filter_dump|
|
156
157
|
field = fields.detect { |f| f.name.to_s == field_name }
|
157
158
|
next unless field
|
158
159
|
|
@@ -250,8 +251,12 @@ module RailsAdmin
|
|
250
251
|
end
|
251
252
|
|
252
253
|
def build_statement_for_boolean
|
253
|
-
|
254
|
-
|
254
|
+
case @value
|
255
|
+
when 'false', 'f', '0'
|
256
|
+
{@column => false}
|
257
|
+
when 'true', 't', '1'
|
258
|
+
{@column => true}
|
259
|
+
end
|
255
260
|
end
|
256
261
|
|
257
262
|
def column_for_value(value)
|
@@ -50,9 +50,11 @@ module RailsAdmin
|
|
50
50
|
format.json do
|
51
51
|
output =
|
52
52
|
if params[:compact]
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
if @association
|
54
|
+
@association.collection(@objects).collect { |(label, id)| {id: id, label: label} }
|
55
|
+
else
|
56
|
+
@objects.collect { |object| {id: object.id.to_s, label: object.send(@model_config.object_label_method).to_s} }
|
57
|
+
end
|
56
58
|
else
|
57
59
|
@objects.to_json(@schema)
|
58
60
|
end
|
@@ -13,7 +13,7 @@ module RailsAdmin
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def method_name
|
16
|
-
association.key_accessor
|
16
|
+
nested_form ? :"#{name}_attributes" : association.key_accessor
|
17
17
|
end
|
18
18
|
|
19
19
|
register_instance_option :pretty_value do
|
@@ -56,7 +56,30 @@ module RailsAdmin
|
|
56
56
|
# preload entire associated collection (per associated_collection_scope) on load
|
57
57
|
# Be sure to set limit in associated_collection_scope if set is large
|
58
58
|
register_instance_option :associated_collection_cache_all do
|
59
|
-
@associated_collection_cache_all ||= (associated_model_config.abstract_model.count < associated_model_limit)
|
59
|
+
@associated_collection_cache_all ||= dynamically_scope_by.blank? && (associated_model_config.abstract_model.count < associated_model_limit)
|
60
|
+
end
|
61
|
+
|
62
|
+
# client-side dynamic scoping
|
63
|
+
register_instance_option :dynamically_scope_by do
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
|
67
|
+
# parses #dynamically_scope_by and returns a Hash in the form of
|
68
|
+
# {[form field name in this model]: [field name in the associated model]}
|
69
|
+
def dynamic_scope_relationships
|
70
|
+
@dynamic_scope_relationships ||=
|
71
|
+
Array.wrap(dynamically_scope_by).flat_map do |field|
|
72
|
+
field.is_a?(Hash) ? field.to_a : [[field, field]]
|
73
|
+
end.map do |field_name, target_name| # rubocop:disable Style/MultilineBlockChain
|
74
|
+
field = section.fields.detect { |f| f.name == field_name }
|
75
|
+
raise "Field '#{field_name}' was given for #dynamically_scope_by but not found in '#{abstract_model.model_name}'" unless field
|
76
|
+
|
77
|
+
target_field = associated_model_config.list.fields.detect { |f| f.name == target_name }
|
78
|
+
raise "Field '#{field_name}' was given for #dynamically_scope_by but not found in '#{associated_model_config.abstract_model.model_name}'" unless target_field
|
79
|
+
raise "Field '#{field_name}' in '#{associated_model_config.abstract_model.model_name}' can't be used for dynamic scoping because it's not filterable" unless target_field.filterable
|
80
|
+
|
81
|
+
[field.method_name, target_name]
|
82
|
+
end.to_h
|
60
83
|
end
|
61
84
|
|
62
85
|
# determines whether association's elements can be removed
|
@@ -111,6 +134,12 @@ module RailsAdmin
|
|
111
134
|
bindings[:object].send(association.name)
|
112
135
|
end
|
113
136
|
|
137
|
+
# Returns collection of all selectable records
|
138
|
+
def collection(scope = nil)
|
139
|
+
(scope || bindings[:controller].list_entries(associated_model_config, :index, associated_collection_scope, false)).
|
140
|
+
map { |o| [o.send(associated_object_label_method), format_key(o.send(associated_primary_key)).to_s] }
|
141
|
+
end
|
142
|
+
|
114
143
|
# has many?
|
115
144
|
def multiple?
|
116
145
|
true
|
@@ -123,6 +152,16 @@ module RailsAdmin
|
|
123
152
|
def associated_model_limit
|
124
153
|
RailsAdmin.config.default_associated_collection_limit
|
125
154
|
end
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
def format_key(key)
|
159
|
+
if key.is_a?(Array)
|
160
|
+
RailsAdmin.config.composite_keys_serializer.serialize(key)
|
161
|
+
else
|
162
|
+
key
|
163
|
+
end
|
164
|
+
end
|
126
165
|
end
|
127
166
|
end
|
128
167
|
end
|
@@ -62,15 +62,15 @@ module RailsAdmin
|
|
62
62
|
|
63
63
|
def sort_column
|
64
64
|
if sortable == true
|
65
|
-
"#{abstract_model.
|
65
|
+
"#{abstract_model.quoted_table_name}.#{abstract_model.quote_column_name(name)}"
|
66
66
|
elsif (sortable.is_a?(String) || sortable.is_a?(Symbol)) && sortable.to_s.include?('.') # just provide sortable, don't do anything smart
|
67
67
|
sortable
|
68
68
|
elsif sortable.is_a?(Hash) # just join sortable hash, don't do anything smart
|
69
69
|
"#{sortable.keys.first}.#{sortable.values.first}"
|
70
|
-
elsif association # use column on target table
|
71
|
-
"#{associated_model_config.abstract_model.
|
70
|
+
elsif association? # use column on target table
|
71
|
+
"#{associated_model_config.abstract_model.quoted_table_name}.#{abstract_model.quote_column_name(sortable)}"
|
72
72
|
else # use described column in the field conf.
|
73
|
-
"#{abstract_model.
|
73
|
+
"#{abstract_model.quoted_table_name}.#{abstract_model.quote_column_name(sortable)}"
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
@@ -357,7 +357,7 @@ module RailsAdmin
|
|
357
357
|
|
358
358
|
def generic_field_help
|
359
359
|
model = abstract_model.model_name.underscore
|
360
|
-
model_lookup = "admin.help.#{model}.#{name}"
|
360
|
+
model_lookup = :"admin.help.#{model}.#{name}"
|
361
361
|
translated = I18n.translate(model_lookup, help: generic_help, default: [generic_help])
|
362
362
|
(translated.is_a?(Hash) ? translated.to_a.first[1] : translated).html_safe
|
363
363
|
end
|