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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +20 -15
  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 +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 +1 -1
  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 +27 -10
  25. data/lib/rails_admin/adapters/active_record/object_extension.rb +0 -18
  26. data/lib/rails_admin/adapters/active_record.rb +52 -5
  27. data/lib/rails_admin/adapters/active_record.rb.bak +348 -0
  28. data/lib/rails_admin/adapters/mongoid/association.rb +3 -3
  29. data/lib/rails_admin/adapters/mongoid/bson.rb +1 -0
  30. data/lib/rails_admin/adapters/mongoid/object_extension.rb +0 -5
  31. data/lib/rails_admin/adapters/mongoid.rb +8 -3
  32. data/lib/rails_admin/config/actions/index.rb +5 -3
  33. data/lib/rails_admin/config/fields/association.rb +41 -2
  34. data/lib/rails_admin/config/fields/base.rb +5 -5
  35. data/lib/rails_admin/config/fields/collection_association.rb +90 -0
  36. data/lib/rails_admin/config/fields/factories/active_storage.rb +2 -2
  37. data/lib/rails_admin/config/fields/factories/carrierwave.rb +1 -1
  38. data/lib/rails_admin/config/fields/factories/dragonfly.rb +1 -1
  39. data/lib/rails_admin/config/fields/factories/paperclip.rb +1 -1
  40. data/lib/rails_admin/config/fields/factories/shrine.rb +1 -1
  41. data/lib/rails_admin/config/fields/singular_association.rb +59 -0
  42. data/lib/rails_admin/config/fields/types/active_storage.rb +12 -7
  43. data/lib/rails_admin/config/fields/types/all.rb +0 -1
  44. data/lib/rails_admin/config/fields/types/belongs_to_association.rb +17 -20
  45. data/lib/rails_admin/config/fields/types/dragonfly.rb +0 -1
  46. data/lib/rails_admin/config/fields/types/file_upload.rb +7 -1
  47. data/lib/rails_admin/config/fields/types/has_and_belongs_to_many_association.rb +2 -2
  48. data/lib/rails_admin/config/fields/types/has_many_association.rb +2 -24
  49. data/lib/rails_admin/config/fields/types/has_one_association.rb +12 -22
  50. data/lib/rails_admin/config/fields/types/multiple_active_storage.rb +13 -8
  51. data/lib/rails_admin/config/fields/types/multiple_file_upload.rb +7 -1
  52. data/lib/rails_admin/config/fields/types/polymorphic_association.rb +32 -9
  53. data/lib/rails_admin/config.rb +5 -0
  54. data/lib/rails_admin/engine.rb +5 -0
  55. data/lib/rails_admin/extensions/paper_trail/auditing_adapter.rb +1 -1
  56. data/lib/rails_admin/extensions/url_for_extension.rb +15 -0
  57. data/lib/rails_admin/support/composite_keys_serializer.rb +15 -0
  58. data/lib/rails_admin/support/datetime.rb +1 -0
  59. data/lib/rails_admin/version.rb +2 -2
  60. data/package.json +2 -2
  61. data/src/rails_admin/abstract-select.js +30 -0
  62. data/src/rails_admin/base.js +4 -1
  63. data/src/rails_admin/filtering-multiselect.js +2 -4
  64. data/src/rails_admin/filtering-select.js +2 -4
  65. metadata +41 -16
  66. data/lib/rails_admin/adapters/composite_primary_keys/association.rb +0 -45
  67. data/lib/rails_admin/adapters/composite_primary_keys.rb +0 -40
  68. data/lib/rails_admin/config/fields/types/composite_keys_belongs_to_association.rb +0 -31
@@ -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
 
@@ -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".to_sym : 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".to_sym : 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".to_sym : 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
@@ -23,19 +23,16 @@ module RailsAdmin
23
23
  end
24
24
 
25
25
  register_instance_option :image? do
26
- if value
27
- mime_type = Mime::Type.lookup_by_extension(value.filename.extension_without_delimiter)
28
- mime_type.to_s.match?(/^image/)
29
- end
26
+ value && (value.representable? || value.content_type.match?(/^image/))
30
27
  end
31
28
 
32
29
  def resource_url(thumb = false)
33
30
  return nil unless value
34
31
 
35
- if thumb && value.variable?
36
- variant = value.variant(thumb_method)
32
+ if thumb && value.representable?
33
+ representation = value.representation(thumb_method)
37
34
  Rails.application.routes.url_helpers.rails_blob_representation_path(
38
- variant.blob.signed_id, variant.variation.key, variant.blob.filename, only_path: true
35
+ representation.blob.signed_id, representation.variation.key, representation.blob.filename, only_path: true
39
36
  )
40
37
  else
41
38
  Rails.application.routes.url_helpers.rails_blob_path(value, only_path: true)
@@ -48,7 +45,7 @@ module RailsAdmin
48
45
  end
49
46
 
50
47
  register_instance_option :keep_method do
51
- method_name if ::ActiveStorage.replace_on_assign_to_many
48
+ method_name if ::ActiveStorage.gem_version >= Gem::Version.new('7.1') || ::ActiveStorage.replace_on_assign_to_many
52
49
  end
53
50
 
54
51
  register_instance_option :delete_method do
@@ -70,6 +67,14 @@ module RailsAdmin
70
67
  direct? && {data: {direct_upload_url: bindings[:view].main_app.rails_direct_uploads_url}} || {},
71
68
  )
72
69
  end
70
+
71
+ register_instance_option :searchable do
72
+ false
73
+ end
74
+
75
+ register_instance_option :sortable do
76
+ false
77
+ end
73
78
  end
74
79
  end
75
80
  end
@@ -47,13 +47,19 @@ module RailsAdmin
47
47
  end
48
48
 
49
49
  register_instance_option :image? do
50
- mime_type = Mime::Type.lookup_by_extension(resource_url.to_s.split('.').last)
50
+ mime_type = Mime::Type.lookup_by_extension(extension)
51
51
  mime_type.to_s.match?(/^image/)
52
52
  end
53
53
 
54
54
  def resource_url(_thumb = false)
55
55
  raise 'not implemented'
56
56
  end
57
+
58
+ def extension
59
+ URI.parse(resource_url).path.split('.').last
60
+ rescue URI::InvalidURIError
61
+ nil
62
+ end
57
63
  end
58
64
 
59
65
  def initialize(*args)
@@ -51,26 +51,29 @@ module RailsAdmin
51
51
  false
52
52
  end
53
53
 
54
- def associated_collection(type)
55
- return [] if type.blank?
54
+ def associated_model_config
55
+ @associated_model_config ||= association.klass.collect { |type| RailsAdmin.config(type) }.reject(&:excluded?)
56
+ end
56
57
 
57
- config = RailsAdmin.config(type)
58
- config.abstract_model.all.collect do |object|
59
- [object.send(config.object_label_method), object.id]
58
+ def collection(_scope = nil)
59
+ if value
60
+ [[formatted_value, selected_id]]
61
+ else
62
+ [[]]
60
63
  end
61
64
  end
62
65
 
63
- def associated_model_config
64
- @associated_model_config ||= association.klass.collect { |type| RailsAdmin.config(type) }.reject(&:excluded?)
66
+ def type_column
67
+ association.foreign_type.to_s
65
68
  end
66
69
 
67
- def polymorphic_type_collection
70
+ def type_collection
68
71
  associated_model_config.collect do |config|
69
72
  [config.label, config.abstract_model.model.name]
70
73
  end
71
74
  end
72
75
 
73
- def polymorphic_type_urls
76
+ def type_urls
74
77
  types = associated_model_config.collect do |config|
75
78
  [config.abstract_model.model.name, config.abstract_model.to_param]
76
79
  end
@@ -82,6 +85,26 @@ module RailsAdmin
82
85
  bindings[:object].send(association.name)
83
86
  end
84
87
 
88
+ def widget_options_for_types
89
+ type_collection.inject({}) do |options, model|
90
+ options.merge(
91
+ model.second.downcase.gsub('::', '-') => {
92
+ xhr: true,
93
+ remote_source: bindings[:view].index_path(model.second.underscore, source_object_id: bindings[:object].id, source_abstract_model: abstract_model.to_param, current_action: bindings[:view].current_action, compact: true),
94
+ float_left: false,
95
+ },
96
+ )
97
+ end
98
+ end
99
+
100
+ def widget_options
101
+ widget_options_for_types[selected_type.try(:downcase)] || {float_left: false}
102
+ end
103
+
104
+ def selected_type
105
+ bindings[:object].send(type_column)
106
+ end
107
+
85
108
  def parse_input(params)
86
109
  if (type_value = params[association.foreign_type.to_sym]).present?
87
110
  config = associated_model_config.find { |c| type_value == c.abstract_model.model.name }
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'rails_admin/config/lazy_model'
4
4
  require 'rails_admin/config/sections/list'
5
+ require 'rails_admin/support/composite_keys_serializer'
5
6
  require 'active_support/core_ext/module/attribute_accessors'
6
7
 
7
8
  module RailsAdmin
@@ -84,6 +85,9 @@ module RailsAdmin
84
85
  # Set where RailsAdmin fetches JS/CSS from, defaults to :sprockets
85
86
  attr_writer :asset_source
86
87
 
88
+ # For customization of composite keys representation
89
+ attr_accessor :composite_keys_serializer
90
+
87
91
  # Setup authentication to be run as a before filter
88
92
  # This is run inside the controller instance so you can setup any authentication you need to
89
93
  #
@@ -329,6 +333,7 @@ module RailsAdmin
329
333
  @navigation_static_links = {}
330
334
  @navigation_static_label = nil
331
335
  @asset_source = nil
336
+ @composite_keys_serializer = RailsAdmin::Support::CompositeKeysSerializer
332
337
  @parent_controller = '::ActionController::Base'
333
338
  @forgery_protection_settings = {with: :exception}
334
339
  RailsAdmin::Config::Actions.reset
@@ -4,6 +4,7 @@ require 'kaminari'
4
4
  require 'nested_form'
5
5
  require 'rails'
6
6
  require 'rails_admin'
7
+ require 'rails_admin/extensions/url_for_extension'
7
8
  require 'rails_admin/version'
8
9
  require 'turbo-rails'
9
10
 
@@ -15,6 +16,10 @@ module RailsAdmin
15
16
 
16
17
  config.action_dispatch.rescue_responses['RailsAdmin::ActionNotAllowed'] = :forbidden
17
18
 
19
+ initializer 'RailsAdmin load UrlForExtension' do
20
+ RailsAdmin::Engine.routes.singleton_class.prepend(RailsAdmin::Extensions::UrlForExtension)
21
+ end
22
+
18
23
  initializer 'RailsAdmin reload config in development' do |app|
19
24
  config.initializer_path = app.root.join('config/initializers/rails_admin.rb')
20
25
 
@@ -146,7 +146,7 @@ module RailsAdmin
146
146
  versions = object.nil? ? versions_for_model(model) : object.public_send(model.model.versions_association_name)
147
147
  versions = versions.where('event LIKE ?', "%#{query}%") if query.present?
148
148
  versions = versions.order(sort)
149
- versions = all ? versions : versions.send(Kaminari.config.page_method_name, current_page).per(per_page)
149
+ versions = versions.send(Kaminari.config.page_method_name, current_page).per(per_page) unless all
150
150
  paginated_proxies = Kaminari.paginate_array([], total_count: versions.try(:total_count) || versions.count)
151
151
  paginated_proxies = paginated_proxies.send(
152
152
  paginated_proxies.respond_to?(Kaminari.config.page_method_name) ? Kaminari.config.page_method_name : :page,