rails_admin 3.2.0.beta → 3.2.0.rc

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 (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