rails_admin 3.2.0.beta → 3.2.0.rc

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -9
  3. data/app/helpers/rails_admin/application_helper.rb +4 -0
  4. data/app/helpers/rails_admin/form_builder.rb +2 -2
  5. data/app/views/rails_admin/main/_form_filtering_multiselect.html.erb +5 -36
  6. data/app/views/rails_admin/main/_form_filtering_select.html.erb +6 -19
  7. data/app/views/rails_admin/main/_form_polymorphic_association.html.erb +12 -21
  8. data/config/initializers/active_record_extensions.rb +0 -23
  9. data/lib/rails_admin/abstract_model.rb +10 -7
  10. data/lib/rails_admin/adapters/active_record/association.rb +24 -7
  11. data/lib/rails_admin/adapters/active_record.rb +34 -2
  12. data/lib/rails_admin/config/actions/index.rb +5 -3
  13. data/lib/rails_admin/config/fields/association.rb +17 -1
  14. data/lib/rails_admin/config/fields/collection_association.rb +90 -0
  15. data/lib/rails_admin/config/fields/singular_association.rb +59 -0
  16. data/lib/rails_admin/config/fields/types/active_storage.rb +4 -7
  17. data/lib/rails_admin/config/fields/types/all.rb +0 -1
  18. data/lib/rails_admin/config/fields/types/belongs_to_association.rb +17 -20
  19. data/lib/rails_admin/config/fields/types/dragonfly.rb +0 -1
  20. data/lib/rails_admin/config/fields/types/file_upload.rb +7 -1
  21. data/lib/rails_admin/config/fields/types/has_and_belongs_to_many_association.rb +2 -2
  22. data/lib/rails_admin/config/fields/types/has_many_association.rb +2 -24
  23. data/lib/rails_admin/config/fields/types/has_one_association.rb +7 -28
  24. data/lib/rails_admin/config/fields/types/multiple_active_storage.rb +4 -7
  25. data/lib/rails_admin/config/fields/types/multiple_file_upload.rb +7 -1
  26. data/lib/rails_admin/config/fields/types/polymorphic_association.rb +32 -9
  27. data/lib/rails_admin/config.rb +5 -0
  28. data/lib/rails_admin/engine.rb +5 -0
  29. data/lib/rails_admin/extensions/url_for_extension.rb +15 -0
  30. data/lib/rails_admin/support/composite_keys_serializer.rb +15 -0
  31. data/lib/rails_admin/version.rb +1 -1
  32. data/package.json +2 -2
  33. data/src/rails_admin/widgets.js +1 -0
  34. metadata +6 -5
  35. data/lib/rails_admin/adapters/composite_primary_keys/association.rb +0 -45
  36. data/lib/rails_admin/adapters/composite_primary_keys.rb +0 -40
  37. data/lib/rails_admin/config/fields/types/composite_keys_belongs_to_association.rb +0 -31
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ad121e2654221b302244d3792229d2f7ef62e95ff35777196f8f885b945c475
4
- data.tar.gz: 5f800d3097c56282ed4ec366bf55a2cb839f5bfa481567c16dcdf81684b02ad1
3
+ metadata.gz: bb8262bbe1aab2c4d873c5a470ca69ff43800ba59d0f319d20039be36e34a640
4
+ data.tar.gz: 7dea99571de5a200d22096ee47881d0b114033eb8643d95af58041115cf66088
5
5
  SHA512:
6
- metadata.gz: e8f2148f02b5b35e9f7f2ae6a8d635b2f10e9fa99370baa0f78d7bc0805880c6d02097b43b928f0abdcf123294910713e68ab87bc0be2c708d436b3eb425ce3c
7
- data.tar.gz: d708dc50f78bbe22015721d49213d6991048022d4c6773f6168412ad025cb1a8125579bd9daabb20921f2c12bf8c8a846cdd914aa8fefdb8967e0ec8ec028008
6
+ metadata.gz: ddb8cabf32fa1023033bc51d8eb2ffa287a8981f7bd10dd862237f1aa5e1a8b84c8b2df64558e1a9bca6c306af42239e952988959fb26b2d68228f9bf828f62d
7
+ data.tar.gz: ee7a91df4279aa73f889c83c52bcd6e5bb64ae217e2e03aa92bcef08f084f0bef8ce1736405fe390d4c9e5651349cff9e5cc789b0cd7899dce4d911b4125ff05
data/Gemfile CHANGED
@@ -21,7 +21,6 @@ group :test do
21
21
  gem 'carrierwave', ['>= 2.0.0.rc', '< 3']
22
22
  gem 'cuprite', '!= 0.15.1'
23
23
  gem 'database_cleaner-active_record', '>= 2.0', require: false
24
- gem 'database_cleaner-mongoid', '>= 2.0', require: false
25
24
  gem 'dragonfly', '~> 1.0'
26
25
  gem 'factory_bot', '>= 4.2', '!= 6.4.5'
27
26
  gem 'generator_spec', '>= 0.8'
@@ -54,12 +53,4 @@ group :active_record do
54
53
  end
55
54
  end
56
55
 
57
- group :mongoid do
58
- gem 'cancancan-mongoid'
59
- gem 'carrierwave-mongoid', '>= 0.6.3', require: 'carrierwave/mongoid'
60
- gem 'kaminari-mongoid'
61
- gem 'mongoid-paperclip', '>= 0.0.8', require: 'mongoid_paperclip'
62
- gem 'shrine-mongoid', '~> 1.0'
63
- end
64
-
65
56
  gemspec
@@ -7,6 +7,10 @@ module RailsAdmin
7
7
  action(action_name, abstract_model, object).try(:authorized?)
8
8
  end
9
9
 
10
+ def current_action
11
+ params[:action].in?(%w[create new]) ? 'create' : 'update'
12
+ end
13
+
10
14
  def current_action?(action, abstract_model = @abstract_model, object = @object)
11
15
  @action.custom_key == action.custom_key &&
12
16
  abstract_model.try(:to_param) == @abstract_model.try(:to_param) &&
@@ -108,8 +108,8 @@ module RailsAdmin
108
108
  end
109
109
 
110
110
  def hidden_field(method, options = {})
111
- if method == :id
112
- super method, {value: object.id.to_s}
111
+ if method == :id && object.id.is_a?(Array)
112
+ super method, {value: RailsAdmin.config.composite_keys_serializer.serialize(object.id)}
113
113
  else
114
114
  super
115
115
  end
@@ -1,45 +1,14 @@
1
1
  <%
2
2
  config = field.associated_model_config
3
- source_abstract_model = RailsAdmin.config(form.object.class).abstract_model
4
-
5
- selected = form.object.send(field.name)
6
- selected_ids = selected.map{|s| s.send(field.associated_primary_key).to_s}
7
-
8
- current_action = params[:action].in?(['create', 'new']) ? 'create' : 'update'
9
-
10
- xhr = !field.associated_collection_cache_all
11
-
12
- collection = if xhr
13
- selected.map { |o| [o.send(field.associated_object_label_method), o.send(field.associated_primary_key)] }
14
- else
15
- i = 0
16
- controller.list_entries(config, :index, field.associated_collection_scope, false).map { |o| [o.send(field.associated_object_label_method), o.send(field.associated_primary_key).to_s] }.sort_by {|a| [selected_ids.index(a[1]) || selected_ids.size, i+=1] }
17
- end
18
-
19
- js_data = {
20
- xhr: xhr,
21
- :'edit-url' => (field.inline_edit && authorized?(:edit, config.abstract_model) ? edit_path(model_name: config.abstract_model.to_param, id: '__ID__') : ''),
22
- remote_source: index_path(config.abstract_model, source_object_id: form.object.id, source_abstract_model: source_abstract_model.to_param, associated_collection: field.name, current_action: current_action, compact: true),
23
- scopeBy: field.dynamic_scope_relationships,
24
- sortable: !!field.orderable,
25
- removable: !!field.removable,
26
- cacheAll: !!field.associated_collection_cache_all,
27
- regional: {
28
- add: t('admin.misc.add_new'),
29
- chooseAll: t('admin.misc.chose_all'),
30
- clearAll: t('admin.misc.clear_all'),
31
- down: t('admin.misc.down'),
32
- remove: t('admin.misc.remove'),
33
- search: t('admin.misc.search'),
34
- up: t('admin.misc.up')
35
- }
36
- }
37
3
  %>
4
+
38
5
  <div class="row">
39
6
  <div class="col-auto">
40
7
  <input name="<%= form.dom_name(field) %>" type="hidden" />
41
- <% selected_ids = (hdv = field.form_default_value).nil? ? selected_ids : hdv %>
42
- <%= form.select field.method_name, collection, { selected: selected_ids, object: form.object }, field.html_attributes.reverse_merge({data: { filteringmultiselect: true, options: js_data.to_json }, multiple: true}) %>
8
+ <%=
9
+ form.select field.method_name, field.collection, { selected: field.form_value, object: form.object },
10
+ field.html_attributes.reverse_merge({data: { filteringmultiselect: true, options: field.widget_options.to_json }, multiple: true})
11
+ %>
43
12
  </div>
44
13
  <% if authorized?(:new, config.abstract_model) && field.inline_add %>
45
14
  <div class="col-sm-4 modal-actions">
@@ -1,34 +1,21 @@
1
1
  <%
2
2
  config = field.associated_model_config
3
- source_abstract_model = RailsAdmin.config(form.object.class).abstract_model
4
-
5
- current_action = params[:action].in?(['create', 'new']) ? 'create' : 'update'
6
-
7
- edit_url = authorized?(:edit, config.abstract_model) ? edit_path(model_name: config.abstract_model.to_param, modal: true, id: '__ID__') : ''
8
-
9
- xhr = !field.associated_collection_cache_all
10
-
11
- collection = xhr ? [[field.formatted_value, field.selected_id]] : controller.list_entries(config, :index, field.associated_collection_scope, false).map { |o| [o.send(field.associated_object_label_method), o.send(field.associated_primary_key).to_s] }
12
-
13
- js_data = {
14
- xhr: xhr,
15
- remote_source: index_path(config.abstract_model.to_param, source_object_id: form.object.id, source_abstract_model: source_abstract_model.to_param, associated_collection: field.name, current_action: current_action, compact: true),
16
- scopeBy: field.dynamic_scope_relationships
17
- }
18
3
  %>
19
4
 
20
5
  <div class="row">
21
6
  <div class="col-sm-4">
22
- <% selected_id = (hdv = field.form_default_value).nil? ? field.selected_id : hdv %>
23
- <%= form.select field.method_name, collection, { selected: selected_id, include_blank: true }, field.html_attributes.reverse_merge({ data: { filteringselect: true, options: js_data.to_json }, placeholder: t('admin.misc.search') }) %>
7
+ <%=
8
+ form.select field.method_name, field.collection, { selected: field.form_value, include_blank: true },
9
+ field.html_attributes.reverse_merge({ data: { filteringselect: true, options: field.widget_options.to_json }, placeholder: t('admin.misc.search') })
10
+ %>
24
11
  </div>
25
12
  <div class="col-sm-8 mt-2 mt-md-0 modal-actions">
26
13
  <% if authorized?(:new, config.abstract_model) && field.inline_add %>
27
14
  <% path_hash = { model_name: config.abstract_model.to_param, modal: true }.merge!(field.associated_prepopulate_params) %>
28
15
  <%= link_to "<i class=\"fas fa-plus\"></i> ".html_safe + wording_for(:link, :new, config.abstract_model), '#', data: { link: new_path(path_hash) }, class: "btn btn-info create" %>
29
16
  <% end %>
30
- <% if edit_url.present? && field.inline_edit %>
31
- <%= link_to "<i class=\"fas fa-pencil-alt\"></i> ".html_safe + wording_for(:link, :edit, config.abstract_model), '#', data: { link: edit_url }, class: "btn btn-info update ms-2#{' disabled' if field.value.nil?}" %>
17
+ <% if authorized?(:edit, config.abstract_model) && field.inline_edit %>
18
+ <%= link_to "<i class=\"fas fa-pencil-alt\"></i> ".html_safe + wording_for(:link, :edit, config.abstract_model), '#', data: { link: edit_path(model_name: config.abstract_model.to_param, modal: true, id: '__ID__') }, class: "btn btn-info update ms-2#{' disabled' if field.value.nil?}" %>
32
19
  <% end %>
33
20
  </div>
34
21
  </div>
@@ -1,32 +1,23 @@
1
1
  <%
2
- type_collection = field.polymorphic_type_collection
3
- type_column = field.association.foreign_type.to_s
4
- selected_type = field.bindings[:object].send(type_column)
5
- selected = field.bindings[:object].send(field.association.name)
6
- collection = selected ? [[field.formatted_value, selected.id]] : [[]]
7
- column_type_dom_id = form.dom_id(field).sub(field.method_name.to_s, type_column)
8
- current_action = params[:action].in?(['create', 'new']) ? 'create' : 'update'
9
-
10
- default_options = { float_left: false }
11
-
12
- js_data = type_collection.inject({}) do |options, model|
13
- source_abstract_model = RailsAdmin.config(form.object.class).abstract_model
14
- options.merge(model.second.downcase.gsub('::', '-') => {
15
- xhr: true,
16
- remote_source: index_path(model.second.underscore, source_object_id: form.object.id, source_abstract_model: source_abstract_model.to_param, current_action: current_action, compact: true),
17
- float_left: false
18
- })
19
- end
2
+ column_type_dom_id = form.dom_id(field).sub(field.method_name.to_s, field.type_column)
20
3
  %>
21
4
 
22
5
  <div class="row">
23
6
  <div class="col-sm-3">
24
- <% js_data.each do |model, value| %>
7
+ <% field.widget_options_for_types.each do |model, value| %>
25
8
  <div data-options="<%= value.to_json %>" id="<%= model %>-js-options"></div>
26
9
  <% end %>
27
- <%= form.select type_column, type_collection, {include_blank: true, selected: selected_type}, class: "form-select", id: column_type_dom_id, data: { polymorphic: true, urls: field.polymorphic_type_urls.to_json }, style: "float: left; margin-right: 10px;" %>
10
+ <%=
11
+ form.select field.type_column, field.type_collection, {include_blank: true, selected: field.selected_type},
12
+ class: "form-select", id: column_type_dom_id, data: { polymorphic: true, urls: field.type_urls.to_json },
13
+ style: "float: left; margin-right: 10px;"
14
+ %>
28
15
  </div>
29
16
  <div class="col-sm-4">
30
- <%= form.select field.method_name, collection, {include_blank: true, selected: selected.try(:id)}, class: "form-control", data: { filteringselect: true, options: js_data[selected_type.try(:downcase)] || default_options }, placeholder: 'Search' %>
17
+ <%=
18
+ form.select field.method_name, field.collection, {include_blank: true, selected: field.selected_id},
19
+ class: "form-control", data: { filteringselect: true, options: field.widget_options },
20
+ placeholder: 'Search'
21
+ %>
31
22
  </div>
32
23
  </div>
@@ -20,27 +20,4 @@ ActiveSupport.on_load(:active_record) do
20
20
  end
21
21
  end
22
22
  end
23
-
24
- if defined?(CompositePrimaryKeys)
25
- # Apply patch until the fix is released:
26
- # https://github.com/composite-primary-keys/composite_primary_keys/pull/572
27
- CompositePrimaryKeys::CompositeKeys.class_eval do
28
- alias_method :to_param, :to_s
29
- end
30
-
31
- CompositePrimaryKeys::CollectionAssociation.prepend(Module.new do
32
- def ids_writer(ids)
33
- if reflection.association_primary_key.is_a? Array
34
- ids = CompositePrimaryKeys.normalize(Array(ids).reject(&:blank?), reflection.association_primary_key.size)
35
- reflection.association_primary_key.each_with_index do |primary_key, i|
36
- pk_type = klass.type_for_attribute(primary_key)
37
- ids.each do |id|
38
- id[i] = pk_type.cast(id[i]) if id.is_a? Array
39
- end
40
- end
41
- end
42
- super ids
43
- end
44
- end)
45
- end
46
23
  end
@@ -105,17 +105,20 @@ module RailsAdmin
105
105
  end
106
106
  end
107
107
 
108
+ def format_id(id)
109
+ id
110
+ end
111
+
112
+ def parse_id(id)
113
+ id
114
+ end
115
+
108
116
  private
109
117
 
110
118
  def initialize_active_record
111
119
  @adapter = :active_record
112
- if defined?(::CompositePrimaryKeys)
113
- require 'rails_admin/adapters/composite_primary_keys'
114
- extend Adapters::CompositePrimaryKeys
115
- else
116
- require 'rails_admin/adapters/active_record'
117
- extend Adapters::ActiveRecord
118
- end
120
+ require 'rails_admin/adapters/active_record'
121
+ extend Adapters::ActiveRecord
119
122
  end
120
123
 
121
124
  def initialize_mongoid
@@ -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?
@@ -75,7 +88,11 @@ module RailsAdmin
75
88
  when :has_one
76
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
 
@@ -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)
@@ -115,10 +115,42 @@ module RailsAdmin
115
115
  true
116
116
  end
117
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
+
118
138
  private
119
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
+
120
148
  def bulk_scope(scope, options)
121
- 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
122
154
  end
123
155
 
124
156
  def sort_scope(scope, options)
@@ -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
@@ -134,6 +134,12 @@ module RailsAdmin
134
134
  bindings[:object].send(association.name)
135
135
  end
136
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
+
137
143
  # has many?
138
144
  def multiple?
139
145
  true
@@ -146,6 +152,16 @@ module RailsAdmin
146
152
  def associated_model_limit
147
153
  RailsAdmin.config.default_associated_collection_limit
148
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
149
165
  end
150
166
  end
151
167
  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
@@ -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
@@ -51,11 +48,11 @@ module RailsAdmin
51
48
  def resource_url(thumb = false)
52
49
  return nil unless value
53
50
 
54
- if thumb && value.variable?
51
+ if thumb && value.representable?
55
52
  thumb = thumb_method if thumb == true
56
- variant = value.variant(thumb)
53
+ representation = value.representation(thumb)
57
54
  Rails.application.routes.url_helpers.rails_blob_representation_path(
58
- 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
59
56
  )
60
57
  else
61
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,54 +1,33 @@
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])
15
- end
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
13
  register_instance_option :allowed_methods do
27
14
  nested_form ? [method_name] : [name]
28
15
  end
29
16
 
30
- def selected_id
31
- value.try(:id).try(:to_s)
32
- end
33
-
34
- def method_name
35
- nested_form ? :"#{name}_attributes" : super
36
- end
37
-
38
- def multiple?
39
- false
40
- end
41
-
42
17
  def associated_prepopulate_params
43
18
  {associated_model_config.abstract_model.param_key => {association.foreign_key => bindings[:object].try(:id)}}
44
19
  end
45
20
 
46
21
  def parse_input(params)
47
- return if nested_form
22
+ return super if nested_form
48
23
 
49
24
  id = params.delete(method_name)
50
25
  params[name] = associated_model_config.abstract_model.get(id) if id
51
26
  end
27
+
28
+ def selected_id
29
+ format_key(value.try(:id)).try(:to_s)
30
+ end
52
31
  end
53
32
  end
54
33
  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)
@@ -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
 
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAdmin
4
+ module Extensions
5
+ module UrlForExtension
6
+ def url_for(options, *args)
7
+ case options[:id]
8
+ when Array
9
+ options[:id] = RailsAdmin.config.composite_keys_serializer.serialize(options[:id])
10
+ end
11
+ super options, *args
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAdmin
4
+ module Support
5
+ module CompositeKeysSerializer
6
+ def self.serialize(keys)
7
+ keys.map { |key| key&.to_s&.gsub('_', '__') }.join('_')
8
+ end
9
+
10
+ def self.deserialize(string)
11
+ string.split('_').map { |key| key&.gsub('__', '_') }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -5,7 +5,7 @@ module RailsAdmin
5
5
  MAJOR = 3
6
6
  MINOR = 2
7
7
  PATCH = 0
8
- PRE = 'beta'
8
+ PRE = 'rc'
9
9
 
10
10
  class << self
11
11
  # @return [String]
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rails_admin",
3
- "version": "3.2.0-beta",
3
+ "version": "3.2.0-rc",
4
4
  "description": "RailsAdmin is a Rails engine that provides an easy-to-use interface for managing your data.",
5
5
  "homepage": "https://github.com/railsadminteam/rails_admin",
6
6
  "license": "MIT",
@@ -22,7 +22,7 @@
22
22
  "bootstrap": "^5.1.3",
23
23
  "flatpickr": "^4.6.9",
24
24
  "jquery": "^3.6.0",
25
- "jquery-ui": "^1.12.1"
25
+ "jquery-ui": "^1.12.1 <1.14.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "prettier": "^2.4.1"
@@ -258,6 +258,7 @@ import I18n from "./i18n";
258
258
  "options"
259
259
  );
260
260
  object_select.data("options", selected_data);
261
+ object_select.val("");
261
262
  object_select.filteringSelect("destroy");
262
263
  object_select.filteringSelect(selected_data);
263
264
  });
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_admin
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0.beta
4
+ version: 3.2.0.rc
5
5
  platform: ruby
6
6
  authors:
7
7
  - Erik Michaels-Ober
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2024-07-13 00:00:00.000000000 Z
15
+ date: 2024-08-25 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: activemodel-serializers-xml
@@ -220,8 +220,6 @@ files:
220
220
  - lib/rails_admin/adapters/active_record/association.rb
221
221
  - lib/rails_admin/adapters/active_record/object_extension.rb
222
222
  - lib/rails_admin/adapters/active_record/property.rb
223
- - lib/rails_admin/adapters/composite_primary_keys.rb
224
- - lib/rails_admin/adapters/composite_primary_keys/association.rb
225
223
  - lib/rails_admin/adapters/mongoid.rb
226
224
  - lib/rails_admin/adapters/mongoid/association.rb
227
225
  - lib/rails_admin/adapters/mongoid/bson.rb
@@ -247,6 +245,7 @@ files:
247
245
  - lib/rails_admin/config/fields.rb
248
246
  - lib/rails_admin/config/fields/association.rb
249
247
  - lib/rails_admin/config/fields/base.rb
248
+ - lib/rails_admin/config/fields/collection_association.rb
250
249
  - lib/rails_admin/config/fields/factories/action_text.rb
251
250
  - lib/rails_admin/config/fields/factories/active_storage.rb
252
251
  - lib/rails_admin/config/fields/factories/association.rb
@@ -258,6 +257,7 @@ files:
258
257
  - lib/rails_admin/config/fields/factories/password.rb
259
258
  - lib/rails_admin/config/fields/factories/shrine.rb
260
259
  - lib/rails_admin/config/fields/group.rb
260
+ - lib/rails_admin/config/fields/singular_association.rb
261
261
  - lib/rails_admin/config/fields/types.rb
262
262
  - lib/rails_admin/config/fields/types/action_text.rb
263
263
  - lib/rails_admin/config/fields/types/active_record_enum.rb
@@ -271,7 +271,6 @@ files:
271
271
  - lib/rails_admin/config/fields/types/ck_editor.rb
272
272
  - lib/rails_admin/config/fields/types/code_mirror.rb
273
273
  - lib/rails_admin/config/fields/types/color.rb
274
- - lib/rails_admin/config/fields/types/composite_keys_belongs_to_association.rb
275
274
  - lib/rails_admin/config/fields/types/date.rb
276
275
  - lib/rails_admin/config/fields/types/datetime.rb
277
276
  - lib/rails_admin/config/fields/types/decimal.rb
@@ -333,6 +332,8 @@ files:
333
332
  - lib/rails_admin/extensions/paper_trail/auditing_adapter.rb
334
333
  - lib/rails_admin/extensions/pundit.rb
335
334
  - lib/rails_admin/extensions/pundit/authorization_adapter.rb
335
+ - lib/rails_admin/extensions/url_for_extension.rb
336
+ - lib/rails_admin/support/composite_keys_serializer.rb
336
337
  - lib/rails_admin/support/csv_converter.rb
337
338
  - lib/rails_admin/support/datetime.rb
338
339
  - lib/rails_admin/support/es_module_processor.rb
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RailsAdmin
4
- module Adapters
5
- module CompositePrimaryKeys
6
- class Association < RailsAdmin::Adapters::ActiveRecord::Association
7
- def field_type
8
- if type == :belongs_to && association.foreign_key.is_a?(Array)
9
- :composite_keys_belongs_to_association
10
- else
11
- super
12
- end
13
- end
14
-
15
- def primary_key
16
- return nil if polymorphic?
17
-
18
- value = association.association_primary_key
19
-
20
- if value.is_a? Array
21
- :id
22
- else
23
- value.to_sym
24
- end
25
- end
26
-
27
- def foreign_key
28
- if association.foreign_key.is_a? Array
29
- association.foreign_key.map(&:to_sym)
30
- else
31
- super
32
- end
33
- end
34
-
35
- def key_accessor
36
- if type == :belongs_to && foreign_key.is_a?(Array)
37
- :"#{name}_id"
38
- else
39
- super
40
- end
41
- end
42
- end
43
- end
44
- end
45
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rails_admin/adapters/active_record'
4
- require 'rails_admin/adapters/composite_primary_keys/association'
5
-
6
- module RailsAdmin
7
- module Adapters
8
- module CompositePrimaryKeys
9
- include RailsAdmin::Adapters::ActiveRecord
10
-
11
- def get(id, scope = scoped)
12
- begin
13
- object = scope.find(id)
14
- rescue ::ActiveRecord::RecordNotFound
15
- return nil
16
- end
17
-
18
- object.extend(RailsAdmin::Adapters::ActiveRecord::ObjectExtension)
19
- end
20
-
21
- def associations
22
- model.reflect_on_all_associations.collect do |association|
23
- RailsAdmin::Adapters::CompositePrimaryKeys::Association.new(association, model)
24
- end
25
- end
26
-
27
- private
28
-
29
- def bulk_scope(scope, options)
30
- if primary_key.is_a? Array
31
- options[:bulk_ids].map do |id|
32
- scope.where(primary_key.zip(::CompositePrimaryKeys::CompositeKeys.parse(id)).to_h)
33
- end.reduce(&:or)
34
- else
35
- super
36
- end
37
- end
38
- end
39
- end
40
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rails_admin/config/fields/types/belongs_to_association'
4
-
5
- module RailsAdmin
6
- module Config
7
- module Fields
8
- module Types
9
- class CompositeKeysBelongsToAssociation < RailsAdmin::Config::Fields::Types::BelongsToAssociation
10
- RailsAdmin::Config::Fields::Types.register(self)
11
-
12
- register_instance_option :allowed_methods do
13
- nested_form ? [method_name] : Array(association.foreign_key)
14
- end
15
-
16
- def selected_id
17
- association.foreign_key.map { |attribute| bindings[:object].safe_send(attribute) }.to_composite_keys.to_s
18
- end
19
-
20
- def parse_input(params)
21
- return unless params[method_name].present? && association.foreign_key.is_a?(Array) && !nested_form
22
-
23
- association.foreign_key.zip(CompositePrimaryKeys::CompositeKeys.parse(params.delete(method_name))).each do |key, value|
24
- params[key] = value
25
- end
26
- end
27
- end
28
- end
29
- end
30
- end
31
- end