rails_admin 3.1.4 → 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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +19 -14
  3. data/README.md +2 -2
  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 +5 -1
  7. data/app/helpers/rails_admin/application_helper.rb +4 -0
  8. data/app/helpers/rails_admin/form_builder.rb +2 -2
  9. data/app/helpers/rails_admin/main_helper.rb +1 -1
  10. data/app/views/layouts/rails_admin/_head.html.erb +8 -5
  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/config/initializers/active_record_extensions.rb +0 -23
  19. data/lib/generators/rails_admin/importmap_formatter.rb +1 -1
  20. data/lib/generators/rails_admin/install_generator.rb +13 -1
  21. data/lib/generators/rails_admin/templates/rails_admin.vite.js +2 -0
  22. data/lib/rails_admin/abstract_model.rb +18 -7
  23. data/lib/rails_admin/adapters/active_record/association.rb +25 -8
  24. data/lib/rails_admin/adapters/active_record/object_extension.rb +0 -18
  25. data/lib/rails_admin/adapters/active_record.rb +51 -5
  26. data/lib/rails_admin/adapters/mongoid/association.rb +1 -1
  27. data/lib/rails_admin/adapters/mongoid/object_extension.rb +0 -5
  28. data/lib/rails_admin/adapters/mongoid.rb +6 -3
  29. data/lib/rails_admin/config/actions/index.rb +5 -3
  30. data/lib/rails_admin/config/fields/association.rb +41 -2
  31. data/lib/rails_admin/config/fields/base.rb +4 -4
  32. data/lib/rails_admin/config/fields/collection_association.rb +90 -0
  33. data/lib/rails_admin/config/fields/singular_association.rb +59 -0
  34. data/lib/rails_admin/config/fields/types/active_storage.rb +12 -7
  35. data/lib/rails_admin/config/fields/types/all.rb +0 -1
  36. data/lib/rails_admin/config/fields/types/belongs_to_association.rb +17 -20
  37. data/lib/rails_admin/config/fields/types/dragonfly.rb +0 -1
  38. data/lib/rails_admin/config/fields/types/file_upload.rb +7 -1
  39. data/lib/rails_admin/config/fields/types/has_and_belongs_to_many_association.rb +2 -2
  40. data/lib/rails_admin/config/fields/types/has_many_association.rb +2 -24
  41. data/lib/rails_admin/config/fields/types/has_one_association.rb +12 -22
  42. data/lib/rails_admin/config/fields/types/multiple_active_storage.rb +13 -8
  43. data/lib/rails_admin/config/fields/types/multiple_file_upload.rb +7 -1
  44. data/lib/rails_admin/config/fields/types/polymorphic_association.rb +32 -9
  45. data/lib/rails_admin/config.rb +5 -0
  46. data/lib/rails_admin/engine.rb +5 -0
  47. data/lib/rails_admin/extensions/url_for_extension.rb +15 -0
  48. data/lib/rails_admin/support/composite_keys_serializer.rb +15 -0
  49. data/lib/rails_admin/support/datetime.rb +1 -0
  50. data/lib/rails_admin/version.rb +2 -2
  51. data/package.json +2 -2
  52. data/src/rails_admin/abstract-select.js +30 -0
  53. data/src/rails_admin/base.js +4 -1
  54. data/src/rails_admin/filtering-multiselect.js +2 -4
  55. data/src/rails_admin/filtering-select.js +2 -4
  56. metadata +39 -15
  57. data/lib/rails_admin/adapters/composite_primary_keys/association.rb +0 -45
  58. data/lib/rails_admin/adapters/composite_primary_keys.rb +0 -40
  59. 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.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)
@@ -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
- scope.where(primary_key => options[:bulk_ids])
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)
@@ -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,9 +298,12 @@ module RailsAdmin
255
298
  end
256
299
 
257
300
  def build_statement_for_boolean
258
- return ["(#{@column} IS NULL OR #{@column} = ?)", false] if %w[false f 0].include?(@value)
259
-
260
- ["(#{@column} = ?)", true] if %w[true t 1].include?(@value)
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
261
307
  end
262
308
 
263
309
  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
@@ -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
@@ -251,9 +251,12 @@ module RailsAdmin
251
251
  end
252
252
 
253
253
  def build_statement_for_boolean
254
- return {@column => false} if %w[false f 0].include?(@value)
255
-
256
- {@column => true} if %w[true t 1].include?(@value)
254
+ case @value
255
+ when 'false', 'f', '0'
256
+ {@column => false}
257
+ when 'true', 't', '1'
258
+ {@column => true}
259
+ end
257
260
  end
258
261
 
259
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
- 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
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.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
 
@@ -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
@@ -0,0 +1,59 @@
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 SingularAssociation < Association
9
+ register_instance_option :filter_operators do
10
+ %w[_discard like not_like is starts_with ends_with] + (required? ? [] : %w[_separator _present _blank])
11
+ end
12
+
13
+ register_instance_option :formatted_value do
14
+ (o = value) && o.send(associated_model_config.object_label_method)
15
+ end
16
+
17
+ register_instance_option :partial do
18
+ nested_form ? :form_nested_one : :form_filtering_select
19
+ end
20
+
21
+ def collection(scope = nil)
22
+ if associated_collection_cache_all || scope
23
+ super
24
+ else
25
+ [[formatted_value, selected_id]]
26
+ end
27
+ end
28
+
29
+ def multiple?
30
+ false
31
+ end
32
+
33
+ def selected_id
34
+ raise NoMethodError # abstract
35
+ end
36
+
37
+ def parse_input(params)
38
+ return unless nested_form && params[method_name].try(:[], :id).present?
39
+
40
+ ids = associated_model_config.abstract_model.parse_id(params[method_name][:id])
41
+ ids = ids.to_composite_keys.to_s if ids.respond_to?(:to_composite_keys)
42
+ params[method_name][:id] = ids
43
+ end
44
+
45
+ def form_value
46
+ form_default_value.nil? ? selected_id : form_default_value
47
+ end
48
+
49
+ def widget_options
50
+ {
51
+ xhr: !associated_collection_cache_all,
52
+ 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),
53
+ scopeBy: dynamic_scope_relationships,
54
+ }
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -18,10 +18,7 @@ module RailsAdmin
18
18
  end
19
19
 
20
20
  register_instance_option :image? do
21
- if value
22
- mime_type = Mime::Type.lookup_by_extension(value.filename.extension_without_delimiter)
23
- mime_type.to_s.match?(/^image/)
24
- end
21
+ value && (value.representable? || value.content_type.match?(/^image/))
25
22
  end
26
23
 
27
24
  register_instance_option :eager_load do
@@ -40,14 +37,22 @@ module RailsAdmin
40
37
  )
41
38
  end
42
39
 
40
+ register_instance_option :searchable do
41
+ false
42
+ end
43
+
44
+ register_instance_option :sortable do
45
+ false
46
+ end
47
+
43
48
  def resource_url(thumb = false)
44
49
  return nil unless value
45
50
 
46
- if thumb && value.variable?
51
+ if thumb && value.representable?
47
52
  thumb = thumb_method if thumb == true
48
- variant = value.variant(thumb)
53
+ representation = value.representation(thumb)
49
54
  Rails.application.routes.url_helpers.rails_blob_representation_path(
50
- variant.blob.signed_id, variant.variation.key, variant.blob.filename, only_path: true
55
+ representation.blob.signed_id, representation.variation.key, representation.blob.filename, only_path: true
51
56
  )
52
57
  else
53
58
  Rails.application.routes.url_helpers.rails_blob_path(value, only_path: true)
@@ -6,7 +6,6 @@ require 'rails_admin/config/fields/types/active_storage'
6
6
  require 'rails_admin/config/fields/types/belongs_to_association'
7
7
  require 'rails_admin/config/fields/types/boolean'
8
8
  require 'rails_admin/config/fields/types/bson_object_id'
9
- require 'rails_admin/config/fields/types/composite_keys_belongs_to_association'
10
9
  require 'rails_admin/config/fields/types/date'
11
10
  require 'rails_admin/config/fields/types/datetime'
12
11
  require 'rails_admin/config/fields/types/decimal'
@@ -1,22 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rails_admin/config/fields/association'
3
+ require 'rails_admin/config/fields/singular_association'
4
4
 
5
5
  module RailsAdmin
6
6
  module Config
7
7
  module Fields
8
8
  module Types
9
- class BelongsToAssociation < RailsAdmin::Config::Fields::Association
9
+ class BelongsToAssociation < RailsAdmin::Config::Fields::SingularAssociation
10
10
  RailsAdmin::Config::Fields::Types.register(self)
11
11
 
12
- register_instance_option :filter_operators do
13
- %w[_discard like not_like is starts_with ends_with] + (required? ? [] : %w[_separator _present _blank])
14
- end
15
-
16
- register_instance_option :formatted_value do
17
- (o = value) && o.send(associated_model_config.object_label_method)
18
- end
19
-
20
12
  register_instance_option :sortable do
21
13
  @sortable ||= abstract_model.adapter_supports_joins? && associated_model_config.abstract_model.properties.collect(&:name).include?(associated_model_config.object_label_method) ? associated_model_config.object_label_method : {abstract_model.table_name => method_name}
22
14
  end
@@ -25,24 +17,29 @@ module RailsAdmin
25
17
  @searchable ||= associated_model_config.abstract_model.properties.collect(&:name).include?(associated_model_config.object_label_method) ? [associated_model_config.object_label_method, {abstract_model.model => method_name}] : {abstract_model.model => method_name}
26
18
  end
27
19
 
28
- register_instance_option :partial do
29
- nested_form ? :form_nested_one : :form_filtering_select
30
- end
31
-
32
20
  register_instance_option :eager_load do
33
21
  true
34
22
  end
35
23
 
36
- def selected_id
37
- bindings[:object].safe_send(association.key_accessor)
24
+ register_instance_option :allowed_methods do
25
+ nested_form ? [method_name] : Array(association.foreign_key)
38
26
  end
39
27
 
40
- def method_name
41
- nested_form ? :"#{name}_attributes" : super
28
+ def selected_id
29
+ if association.foreign_key.is_a?(Array)
30
+ format_key(association.foreign_key.map { |attribute| bindings[:object].safe_send(attribute) })
31
+ else
32
+ bindings[:object].safe_send(association.key_accessor)
33
+ end
42
34
  end
43
35
 
44
- def multiple?
45
- false
36
+ def parse_input(params)
37
+ return super if nested_form
38
+ return unless params[method_name].present? && association.foreign_key.is_a?(Array)
39
+
40
+ association.foreign_key.zip(RailsAdmin.config.composite_keys_serializer.deserialize(params.delete(method_name))).each do |key, value|
41
+ params[key] = value
42
+ end
46
43
  end
47
44
  end
48
45
  end
@@ -12,7 +12,6 @@ module RailsAdmin
12
12
  RailsAdmin::Config::Fields::Types.register(self)
13
13
 
14
14
  register_instance_option :image? do
15
- false unless value
16
15
  if abstract_model.model.new.respond_to?("#{name}_name")
17
16
  mime_type = Mime::Type.lookup_by_extension(bindings[:object].send("#{name}_name").to_s.split('.').last)
18
17
  mime_type.to_s.match?(/^image/)
@@ -52,7 +52,7 @@ module RailsAdmin
52
52
  end
53
53
 
54
54
  register_instance_option :image? do
55
- mime_type = Mime::Type.lookup_by_extension(resource_url.to_s.split('.').last)
55
+ mime_type = Mime::Type.lookup_by_extension(extension)
56
56
  mime_type.to_s.match?(/^image/)
57
57
  end
58
58
 
@@ -66,6 +66,12 @@ module RailsAdmin
66
66
  }
67
67
  end
68
68
 
69
+ def extension
70
+ URI.parse(resource_url).path.split('.').last
71
+ rescue URI::InvalidURIError
72
+ nil
73
+ end
74
+
69
75
  # virtual class
70
76
  def resource_url
71
77
  raise 'not implemented'
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rails_admin/config/fields/types/has_many_association'
3
+ require 'rails_admin/config/fields/collection_association'
4
4
 
5
5
  module RailsAdmin
6
6
  module Config
7
7
  module Fields
8
8
  module Types
9
- class HasAndBelongsToManyAssociation < RailsAdmin::Config::Fields::Types::HasManyAssociation
9
+ class HasAndBelongsToManyAssociation < RailsAdmin::Config::Fields::CollectionAssociation
10
10
  # Register field type for the type loader
11
11
  RailsAdmin::Config::Fields::Types.register(self)
12
12
  end
@@ -1,36 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rails_admin/config/fields/association'
3
+ require 'rails_admin/config/fields/collection_association'
4
4
 
5
5
  module RailsAdmin
6
6
  module Config
7
7
  module Fields
8
8
  module Types
9
- class HasManyAssociation < RailsAdmin::Config::Fields::Association
9
+ class HasManyAssociation < RailsAdmin::Config::Fields::CollectionAssociation
10
10
  # Register field type for the type loader
11
11
  RailsAdmin::Config::Fields::Types.register(self)
12
-
13
- register_instance_option :partial do
14
- nested_form ? :form_nested_many : :form_filtering_multiselect
15
- end
16
-
17
- # orderable associated objects
18
- register_instance_option :orderable do
19
- false
20
- end
21
-
22
- def method_name
23
- nested_form ? :"#{name}_attributes" : super
24
- end
25
-
26
- # Reader for validation errors of the bound object
27
- def errors
28
- bindings[:object].errors[name]
29
- end
30
-
31
- def associated_prepopulate_params
32
- {associated_model_config.abstract_model.param_key => {association.foreign_key => bindings[:object].try(:id)}}
33
- end
34
12
  end
35
13
  end
36
14
  end
@@ -1,42 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rails_admin/config/fields/association'
3
+ require 'rails_admin/config/fields/singular_association'
4
4
 
5
5
  module RailsAdmin
6
6
  module Config
7
7
  module Fields
8
8
  module Types
9
- class HasOneAssociation < RailsAdmin::Config::Fields::Association
9
+ class HasOneAssociation < RailsAdmin::Config::Fields::SingularAssociation
10
10
  # Register field type for the type loader
11
11
  RailsAdmin::Config::Fields::Types.register(self)
12
12
 
13
- register_instance_option :filter_operators do
14
- %w[_discard like not_like is starts_with ends_with] + (required? ? [] : %w[_separator _present _blank])
13
+ register_instance_option :allowed_methods do
14
+ nested_form ? [method_name] : [name]
15
15
  end
16
16
 
17
- register_instance_option :partial do
18
- nested_form ? :form_nested_one : :form_filtering_select
19
- end
20
-
21
- # Accessor for field's formatted value
22
- register_instance_option :formatted_value do
23
- (o = value) && o.send(associated_model_config.object_label_method)
24
- end
25
-
26
- def selected_id
27
- value.try(:id).try(:to_s)
17
+ def associated_prepopulate_params
18
+ {associated_model_config.abstract_model.param_key => {association.foreign_key => bindings[:object].try(:id)}}
28
19
  end
29
20
 
30
- def method_name
31
- nested_form ? :"#{name}_attributes" : super
32
- end
21
+ def parse_input(params)
22
+ return super if nested_form
33
23
 
34
- def multiple?
35
- false
24
+ id = params.delete(method_name)
25
+ params[name] = associated_model_config.abstract_model.get(id) if id
36
26
  end
37
27
 
38
- def associated_prepopulate_params
39
- {associated_model_config.abstract_model.param_key => {association.foreign_key => bindings[:object].try(:id)}}
28
+ def selected_id
29
+ format_key(value.try(:id)).try(:to_s)
40
30
  end
41
31
  end
42
32
  end