rails_admin 3.1.1 → 3.3.1

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +23 -17
  3. data/README.md +6 -4
  4. data/app/assets/javascripts/rails_admin/application.js.erb +3 -2
  5. data/app/assets/stylesheets/rails_admin/application.scss.erb +1 -1
  6. data/app/controllers/rails_admin/main_controller.rb +16 -1
  7. data/app/helpers/rails_admin/application_helper.rb +4 -0
  8. data/app/helpers/rails_admin/form_builder.rb +6 -5
  9. data/app/helpers/rails_admin/main_helper.rb +1 -1
  10. data/app/views/layouts/rails_admin/_head.html.erb +10 -7
  11. data/app/views/rails_admin/main/_form_boolean.html.erb +2 -2
  12. data/app/views/rails_admin/main/_form_filtering_multiselect.html.erb +5 -35
  13. data/app/views/rails_admin/main/_form_filtering_select.html.erb +6 -18
  14. data/app/views/rails_admin/main/_form_nested_many.html.erb +1 -1
  15. data/app/views/rails_admin/main/_form_nested_one.html.erb +1 -1
  16. data/app/views/rails_admin/main/_form_polymorphic_association.html.erb +12 -21
  17. data/app/views/rails_admin/main/delete.html.erb +1 -1
  18. data/app/views/rails_admin/main/index.html.erb +2 -2
  19. data/config/initializers/active_record_extensions.rb +0 -23
  20. data/lib/generators/rails_admin/importmap_formatter.rb +3 -2
  21. data/lib/generators/rails_admin/install_generator.rb +13 -1
  22. data/lib/generators/rails_admin/templates/rails_admin.vite.js +2 -0
  23. data/lib/rails_admin/abstract_model.rb +18 -7
  24. data/lib/rails_admin/adapters/active_record/association.rb +28 -11
  25. data/lib/rails_admin/adapters/active_record/object_extension.rb +0 -18
  26. data/lib/rails_admin/adapters/active_record.rb +56 -5
  27. data/lib/rails_admin/adapters/mongoid/association.rb +4 -4
  28. data/lib/rails_admin/adapters/mongoid/bson.rb +1 -0
  29. data/lib/rails_admin/adapters/mongoid/object_extension.rb +0 -5
  30. data/lib/rails_admin/adapters/mongoid.rb +13 -3
  31. data/lib/rails_admin/config/actions/export.rb +2 -1
  32. data/lib/rails_admin/config/actions/index.rb +7 -5
  33. data/lib/rails_admin/config/actions/show.rb +1 -1
  34. data/lib/rails_admin/config/configurable.rb +1 -1
  35. data/lib/rails_admin/config/fields/association.rb +41 -2
  36. data/lib/rails_admin/config/fields/base.rb +5 -5
  37. data/lib/rails_admin/config/fields/collection_association.rb +90 -0
  38. data/lib/rails_admin/config/fields/factories/active_storage.rb +2 -2
  39. data/lib/rails_admin/config/fields/factories/carrierwave.rb +1 -1
  40. data/lib/rails_admin/config/fields/factories/dragonfly.rb +1 -1
  41. data/lib/rails_admin/config/fields/factories/paperclip.rb +1 -1
  42. data/lib/rails_admin/config/fields/factories/shrine.rb +1 -1
  43. data/lib/rails_admin/config/fields/singular_association.rb +59 -0
  44. data/lib/rails_admin/config/fields/types/active_storage.rb +12 -7
  45. data/lib/rails_admin/config/fields/types/all.rb +0 -1
  46. data/lib/rails_admin/config/fields/types/belongs_to_association.rb +17 -20
  47. data/lib/rails_admin/config/fields/types/dragonfly.rb +0 -1
  48. data/lib/rails_admin/config/fields/types/file_upload.rb +7 -1
  49. data/lib/rails_admin/config/fields/types/has_and_belongs_to_many_association.rb +2 -2
  50. data/lib/rails_admin/config/fields/types/has_many_association.rb +2 -24
  51. data/lib/rails_admin/config/fields/types/has_one_association.rb +12 -22
  52. data/lib/rails_admin/config/fields/types/multiple_active_storage.rb +13 -8
  53. data/lib/rails_admin/config/fields/types/multiple_file_upload.rb +7 -1
  54. data/lib/rails_admin/config/fields/types/polymorphic_association.rb +32 -9
  55. data/lib/rails_admin/config/model.rb +3 -1
  56. data/lib/rails_admin/config.rb +9 -5
  57. data/lib/rails_admin/engine.rb +5 -0
  58. data/lib/rails_admin/extensions/paper_trail/auditing_adapter.rb +1 -1
  59. data/lib/rails_admin/extensions/pundit/authorization_adapter.rb +2 -1
  60. data/lib/rails_admin/extensions/url_for_extension.rb +15 -0
  61. data/lib/rails_admin/support/composite_keys_serializer.rb +15 -0
  62. data/lib/rails_admin/support/csv_converter.rb +0 -1
  63. data/lib/rails_admin/support/datetime.rb +1 -0
  64. data/lib/rails_admin/support/serialization_schema.rb +72 -0
  65. data/lib/rails_admin/version.rb +1 -1
  66. data/lib/rails_admin.rb +1 -0
  67. data/package.json +2 -2
  68. data/src/rails_admin/abstract-select.js +30 -0
  69. data/src/rails_admin/base.js +4 -1
  70. data/src/rails_admin/filtering-multiselect.js +2 -4
  71. data/src/rails_admin/filtering-select.js +2 -4
  72. data/src/rails_admin/ui.js +7 -1
  73. metadata +41 -18
  74. data/lib/rails_admin/adapters/composite_primary_keys/association.rb +0 -45
  75. data/lib/rails_admin/adapters/composite_primary_keys.rb +0 -40
  76. data/lib/rails_admin/config/fields/types/composite_keys_belongs_to_association.rb +0 -31
@@ -33,7 +33,7 @@ module RailsAdmin
33
33
 
34
34
  def klass
35
35
  if options[:polymorphic]
36
- polymorphic_parents(:active_record, model.name.to_s, name) || []
36
+ polymorphic_parents(:active_record, association.active_record.name.to_s, name) || []
37
37
  else
38
38
  association.klass
39
39
  end
@@ -42,16 +42,29 @@ module RailsAdmin
42
42
  def primary_key
43
43
  return nil if polymorphic?
44
44
 
45
- case type
46
- when :has_one
47
- association.klass.primary_key
45
+ value =
46
+ case type
47
+ when :has_one
48
+ association.klass.primary_key
49
+ else
50
+ association.association_primary_key
51
+ end
52
+
53
+ if value.is_a? Array
54
+ :id
48
55
  else
49
- association.association_primary_key
50
- end.try(:to_sym)
56
+ value.to_sym
57
+ end
51
58
  end
52
59
 
53
60
  def foreign_key
54
- association.foreign_key.to_sym
61
+ if association.options[:query_constraints].present?
62
+ association.options[:query_constraints].map(&:to_sym)
63
+ elsif association.foreign_key.is_a?(Array)
64
+ association.foreign_key.map(&:to_sym)
65
+ else
66
+ association.foreign_key.to_sym
67
+ end
55
68
  end
56
69
 
57
70
  def foreign_key_nullable?
@@ -71,11 +84,15 @@ module RailsAdmin
71
84
  def key_accessor
72
85
  case type
73
86
  when :has_many, :has_and_belongs_to_many
74
- "#{name.to_s.singularize}_ids".to_sym
87
+ :"#{name.to_s.singularize}_ids"
75
88
  when :has_one
76
- "#{name}_id".to_sym
89
+ :"#{name}_id"
77
90
  else
78
- foreign_key
91
+ if foreign_key.is_a?(Array)
92
+ :"#{name}_id"
93
+ else
94
+ foreign_key
95
+ end
79
96
  end
80
97
  end
81
98
 
@@ -88,7 +105,7 @@ module RailsAdmin
88
105
  end
89
106
 
90
107
  def inverse_of
91
- options[:inverse_of].try :to_sym
108
+ options[:inverse_of].try(:to_sym) || as
92
109
  end
93
110
 
94
111
  def read_only?
@@ -4,24 +4,6 @@ module RailsAdmin
4
4
  module Adapters
5
5
  module ActiveRecord
6
6
  module ObjectExtension
7
- def self.extended(object)
8
- object.class.reflect_on_all_associations.each do |association|
9
- association = Association.new(association, object.class)
10
- case association.type
11
- when :has_one
12
- object.instance_eval <<-RUBY, __FILE__, __LINE__ + 1
13
- def #{association.name}_id
14
- self.#{association.name}&.id
15
- end
16
-
17
- def #{association.name}_id=(item_id)
18
- self.#{association.name} = (#{association.klass}.find(item_id) rescue nil)
19
- end
20
- RUBY
21
- end
22
- end
23
- end
24
-
25
7
  def assign_attributes(attributes)
26
8
  super if attributes
27
9
  end
@@ -15,7 +15,7 @@ module RailsAdmin
15
15
  end
16
16
 
17
17
  def get(id, scope = scoped)
18
- object = scope.where(primary_key => id).first
18
+ object = primary_key_scope(scope, id).first
19
19
  return unless object
20
20
 
21
21
  object.extend(ObjectExtension)
@@ -25,6 +25,10 @@ module RailsAdmin
25
25
  model.all
26
26
  end
27
27
 
28
+ def none
29
+ model.none
30
+ end
31
+
28
32
  def first(options = {}, scope = nil)
29
33
  all(options, scope).first
30
34
  end
@@ -72,6 +76,14 @@ module RailsAdmin
72
76
 
73
77
  delegate :primary_key, :table_name, to: :model, prefix: false
74
78
 
79
+ def quoted_table_name
80
+ model.quoted_table_name
81
+ end
82
+
83
+ def quote_column_name(name)
84
+ model.connection.quote_column_name(name)
85
+ end
86
+
75
87
  def encoding
76
88
  adapter =
77
89
  if ::ActiveRecord::Base.respond_to?(:connection_db_config)
@@ -107,10 +119,42 @@ module RailsAdmin
107
119
  true
108
120
  end
109
121
 
122
+ def format_id(id)
123
+ if primary_key.is_a? Array
124
+ RailsAdmin.config.composite_keys_serializer.serialize(id)
125
+ else
126
+ id
127
+ end
128
+ end
129
+
130
+ def parse_id(id)
131
+ if primary_key.is_a?(Array)
132
+ ids = RailsAdmin.config.composite_keys_serializer.deserialize(id)
133
+ primary_key.each_with_index do |key, i|
134
+ ids[i] = model.type_for_attribute(key).cast(ids[i])
135
+ end
136
+ ids
137
+ else
138
+ id
139
+ end
140
+ end
141
+
110
142
  private
111
143
 
144
+ def primary_key_scope(scope, id)
145
+ if primary_key.is_a? Array
146
+ scope.where(primary_key.zip(parse_id(id)).to_h)
147
+ else
148
+ scope.where(primary_key => id)
149
+ end
150
+ end
151
+
112
152
  def bulk_scope(scope, options)
113
- scope.where(primary_key => options[:bulk_ids])
153
+ if primary_key.is_a? Array
154
+ options[:bulk_ids].map { |id| primary_key_scope(scope, id) }.reduce(&:or)
155
+ else
156
+ scope.where(primary_key => options[:bulk_ids])
157
+ end
114
158
  end
115
159
 
116
160
  def sort_scope(scope, options)
@@ -172,7 +216,7 @@ module RailsAdmin
172
216
  # "0055" is the filter index, no use here. o is the operator, v the value
173
217
  def filter_scope(scope, filters, fields = config.list.fields.select(&:filterable?))
174
218
  filters.each_pair do |field_name, filters_dump|
175
- filters_dump.each do |_, filter_dump|
219
+ filters_dump.each_value do |filter_dump|
176
220
  wb = WhereBuilder.new(scope)
177
221
  field = fields.detect { |f| f.name.to_s == field_name }
178
222
  value = parse_field_value(field, filter_dump[:v])
@@ -201,6 +245,8 @@ module RailsAdmin
201
245
  case @type
202
246
  when :boolean
203
247
  boolean_unary_operators
248
+ when :uuid
249
+ uuid_unary_operators
204
250
  when :integer, :decimal, :float
205
251
  numeric_unary_operators
206
252
  else
@@ -230,6 +276,7 @@ module RailsAdmin
230
276
  )
231
277
  end
232
278
  alias_method :numeric_unary_operators, :boolean_unary_operators
279
+ alias_method :uuid_unary_operators, :boolean_unary_operators
233
280
 
234
281
  def range_filter(min, max)
235
282
  if min && max && min == max
@@ -255,8 +302,12 @@ module RailsAdmin
255
302
  end
256
303
 
257
304
  def build_statement_for_boolean
258
- return ["(#{@column} IS NULL OR #{@column} = ?)", false] if %w[false f 0].include?(@value)
259
- return ["(#{@column} = ?)", true] if %w[true t 1].include?(@value)
305
+ case @value
306
+ when 'false', 'f', '0'
307
+ ["(#{@column} IS NULL OR #{@column} = ?)", false]
308
+ when 'true', 't', '1'
309
+ ["(#{@column} = ?)", true]
310
+ end
260
311
  end
261
312
 
262
313
  def column_for_value(value)
@@ -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, model.name, name) || []
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".to_sym
95
+ :"#{name.to_s.singularize}_ids"
96
96
  when :has_one
97
- "#{name}_id".to_sym
97
+ :"#{name}_id"
98
98
  when :embedded_in, :embeds_one, :embeds_many
99
99
  nil
100
100
  else
@@ -111,7 +111,7 @@ module RailsAdmin
111
111
  end
112
112
 
113
113
  def inverse_of
114
- association.inverse_of.try :to_sym
114
+ association.inverse_of.try(:to_sym) || as
115
115
  end
116
116
 
117
117
  def read_only?
@@ -21,6 +21,7 @@ module RailsAdmin
21
21
  Moped::Errors::InvalidObjectId
22
22
  BSON::ObjectId::Invalid
23
23
  BSON::InvalidObjectId
24
+ BSON::Error::InvalidObjectId
24
25
  ].exclude?(e.class.to_s)
25
26
  end
26
27
  end
@@ -20,11 +20,6 @@ module RailsAdmin
20
20
  send(name)&.save
21
21
  end
22
22
  end
23
- object.instance_eval <<-RUBY, __FILE__, __LINE__ + 1
24
- def #{name}_id=(item_id)
25
- self.#{name} = (#{association.klass}.find(item_id) rescue nil)
26
- end
27
- RUBY
28
23
  end
29
24
  end
30
25
  end
@@ -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
 
@@ -38,6 +39,11 @@ module RailsAdmin
38
39
  model.scoped
39
40
  end
40
41
 
42
+ # Criteria#none is lost when merged into another criteria
43
+ def none
44
+ scoped.in(_id: [])
45
+ end
46
+
41
47
  def first(options = {}, scope = nil)
42
48
  all(options, scope).first
43
49
  end
@@ -152,7 +158,7 @@ module RailsAdmin
152
158
  statements = []
153
159
 
154
160
  filters.each_pair do |field_name, filters_dump|
155
- filters_dump.each do |_, filter_dump|
161
+ filters_dump.each_value do |filter_dump|
156
162
  field = fields.detect { |f| f.name.to_s == field_name }
157
163
  next unless field
158
164
 
@@ -250,8 +256,12 @@ module RailsAdmin
250
256
  end
251
257
 
252
258
  def build_statement_for_boolean
253
- return {@column => false} if %w[false f 0].include?(@value)
254
- return {@column => true} if %w[true t 1].include?(@value)
259
+ case @value
260
+ when 'false', 'f', '0'
261
+ {@column => false}
262
+ when 'true', 't', '1'
263
+ {@column => true}
264
+ end
255
265
  end
256
266
 
257
267
  def column_for_value(value)
@@ -19,7 +19,8 @@ module RailsAdmin
19
19
  format = params[:json] && :json || params[:csv] && :csv || params[:xml] && :xml
20
20
  if format
21
21
  request.format = format
22
- @schema = HashHelper.symbolize(params[:schema].slice(:except, :include, :methods, :only).permit!.to_h) if params[:schema] # to_json and to_xml expect symbols for keys AND values.
22
+ serialization_schema # built while @action is still export
23
+ @schema = serialization_schema.sanitize(params[:schema].slice(:include, :methods, :only).permit!.to_h) if params[:schema]
23
24
  @objects = list_entries(@model_config, :export)
24
25
  index
25
26
  else
@@ -50,11 +50,13 @@ module RailsAdmin
50
50
  format.json do
51
51
  output =
52
52
  if params[:compact]
53
- primary_key_method = @association ? @association.associated_primary_key : @model_config.abstract_model.primary_key
54
- label_method = @model_config.object_label_method
55
- @objects.collect { |o| {id: o.send(primary_key_method).to_s, label: o.send(label_method).to_s} }
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
- @objects.to_json(@schema)
59
+ @objects.to_json(@schema || serialization_schema.default)
58
60
  end
59
61
 
60
62
  if params[:send_data]
@@ -65,7 +67,7 @@ module RailsAdmin
65
67
  end
66
68
 
67
69
  format.xml do
68
- output = @objects.to_xml(@schema)
70
+ output = @objects.to_xml(@schema || serialization_schema.default)
69
71
  if params[:send_data]
70
72
  send_data output, filename: "#{params[:model_name]}_#{DateTime.now.strftime('%Y-%m-%d_%Hh%Mm%S')}.xml"
71
73
  else
@@ -21,7 +21,7 @@ module RailsAdmin
21
21
  register_instance_option :controller do
22
22
  proc do
23
23
  respond_to do |format|
24
- format.json { render json: @object }
24
+ format.json { render json: @object.to_json(serialization_schema.default) }
25
25
  format.html { render @action.template_name }
26
26
  end
27
27
  end
@@ -9,7 +9,7 @@ module RailsAdmin
9
9
  base.send :extend, ClassMethods
10
10
  end
11
11
 
12
- def has_option?(name) # rubocop:disable Naming/PredicateName
12
+ def has_option?(name) # rubocop:disable Naming/PredicatePrefix
13
13
  options = self.class.instance_variable_get('@config_options')
14
14
  options&.key?(name)
15
15
  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.table_name}.#{name}"
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.table_name}.#{sortable}"
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.table_name}.#{sortable}"
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}".to_sym
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
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_admin/config/fields/association'
4
+
5
+ module RailsAdmin
6
+ module Config
7
+ module Fields
8
+ class CollectionAssociation < Association
9
+ # orderable associated objects
10
+ register_instance_option :orderable do
11
+ false
12
+ end
13
+
14
+ register_instance_option :partial do
15
+ nested_form ? :form_nested_many : :form_filtering_multiselect
16
+ end
17
+
18
+ def collection(scope = nil)
19
+ if scope
20
+ super
21
+ elsif associated_collection_cache_all
22
+ selected = selected_ids
23
+ i = 0
24
+ super.sort_by { |a| [selected.index(a[1]) || selected.size, i += 1] }
25
+ else
26
+ value.map { |o| [o.send(associated_object_label_method), format_key(o.send(associated_primary_key))] }
27
+ end
28
+ end
29
+
30
+ def associated_prepopulate_params
31
+ {associated_model_config.abstract_model.param_key => {association.foreign_key => bindings[:object].try(:id)}}
32
+ end
33
+
34
+ def multiple?
35
+ true
36
+ end
37
+
38
+ def selected_ids
39
+ value.map { |s| format_key(s.send(associated_primary_key)).to_s }
40
+ end
41
+
42
+ def parse_input(params)
43
+ return unless associated_model_config.abstract_model.primary_key.is_a?(Array)
44
+
45
+ if nested_form
46
+ params[method_name].each_value do |value|
47
+ value[:id] = associated_model_config.abstract_model.parse_id(value[:id])
48
+ end
49
+ elsif params[method_name].is_a?(Array)
50
+ params[method_name] = params[method_name].map { |key| associated_model_config.abstract_model.parse_id(key) if key.present? }.compact
51
+ if params[method_name].empty?
52
+ # Workaround for Arel::Visitors::UnsupportedVisitError in #ids_writer, until https://github.com/rails/rails/pull/51116 is in place
53
+ params.delete(method_name)
54
+ params[name] = []
55
+ end
56
+ end
57
+ end
58
+
59
+ def form_default_value
60
+ (default_value if bindings[:object].new_record? && value.empty?)
61
+ end
62
+
63
+ def form_value
64
+ form_default_value.nil? ? selected_ids : form_default_value
65
+ end
66
+
67
+ def widget_options
68
+ {
69
+ xhr: !associated_collection_cache_all,
70
+ 'edit-url': (inline_edit && bindings[:view].authorized?(:edit, associated_model_config.abstract_model) ? bindings[:view].edit_path(model_name: associated_model_config.abstract_model.to_param, id: '__ID__') : ''),
71
+ remote_source: bindings[:view].index_path(associated_model_config.abstract_model, source_object_id: abstract_model.format_id(bindings[:object].id), source_abstract_model: abstract_model.to_param, associated_collection: name, current_action: bindings[:view].current_action, compact: true),
72
+ scopeBy: dynamic_scope_relationships,
73
+ sortable: !!orderable,
74
+ removable: !!removable,
75
+ cacheAll: !!associated_collection_cache_all,
76
+ regional: {
77
+ add: ::I18n.t('admin.misc.add_new'),
78
+ chooseAll: ::I18n.t('admin.misc.chose_all'),
79
+ clearAll: ::I18n.t('admin.misc.clear_all'),
80
+ down: ::I18n.t('admin.misc.down'),
81
+ remove: ::I18n.t('admin.misc.remove'),
82
+ search: ::I18n.t('admin.misc.search'),
83
+ up: ::I18n.t('admin.misc.up'),
84
+ },
85
+ }
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -13,9 +13,9 @@ RailsAdmin::Config::Fields.register_factory do |parent, properties, fields|
13
13
  fields << field
14
14
  associations =
15
15
  if properties.type == :has_many
16
- ["#{name}_attachments".to_sym, "#{name}_blobs".to_sym]
16
+ [:"#{name}_attachments", :"#{name}_blobs"]
17
17
  else
18
- ["#{name}_attachment".to_sym, "#{name}_blob".to_sym]
18
+ [:"#{name}_attachment", :"#{name}_blob"]
19
19
  end
20
20
  children_fields = associations.map do |child_name|
21
21
  child_association = parent.abstract_model.associations.detect { |p| p.name.to_sym == child_name }
@@ -7,7 +7,7 @@ require 'rails_admin/config/fields/types/file_upload'
7
7
  RailsAdmin::Config::Fields.register_factory do |parent, properties, fields|
8
8
  model = parent.abstract_model.model
9
9
  if defined?(::CarrierWave) && model.is_a?(CarrierWave::Mount) && model.uploaders.include?(attachment_name = properties.name.to_s.chomp('_file_name').to_sym)
10
- columns = [model.uploader_options[attachment_name][:mount_on] || attachment_name, "#{attachment_name}_content_type".to_sym, "#{attachment_name}_file_size".to_sym]
10
+ columns = [model.uploader_options[attachment_name][:mount_on] || attachment_name, :"#{attachment_name}_content_type", :"#{attachment_name}_file_size"]
11
11
  field = RailsAdmin::Config::Fields::Types.load(
12
12
  %i[serialized json].include?(properties.type) ? :multiple_carrierwave : :carrierwave,
13
13
  ).new(parent, attachment_name, properties)
@@ -10,7 +10,7 @@ RailsAdmin::Config::Fields.register_factory do |parent, properties, fields|
10
10
  field = RailsAdmin::Config::Fields::Types.load(:dragonfly).new(parent, attachment_name, properties)
11
11
  children_fields = []
12
12
  extensions.each do |ext|
13
- children_column_name = "#{attachment_name}_#{ext}".to_sym
13
+ children_column_name = :"#{attachment_name}_#{ext}"
14
14
  child_properties = parent.abstract_model.properties.detect { |p| p.name.to_s == children_column_name.to_s }
15
15
  next unless child_properties
16
16
 
@@ -11,7 +11,7 @@ RailsAdmin::Config::Fields.register_factory do |parent, properties, fields|
11
11
  field = RailsAdmin::Config::Fields::Types.load(:paperclip).new(parent, attachment_name, properties)
12
12
  children_fields = []
13
13
  extensions.each do |ext|
14
- children_column_name = "#{attachment_name}_#{ext}".to_sym
14
+ children_column_name = :"#{attachment_name}_#{ext}"
15
15
  child_properties = parent.abstract_model.properties.detect { |p| p.name.to_s == children_column_name.to_s }
16
16
  next unless child_properties
17
17
 
@@ -16,7 +16,7 @@ RailsAdmin::Config::Fields.register_factory do |parent, properties, fields|
16
16
  field = RailsAdmin::Config::Fields::Types.load(:shrine).new(parent, attachment_name, properties)
17
17
  fields << field
18
18
 
19
- data_field_name = "#{attachment_name}_data".to_sym
19
+ data_field_name = :"#{attachment_name}_data"
20
20
  child_properties = parent.abstract_model.properties.detect { |p| p.name == data_field_name }
21
21
  next true unless child_properties
22
22