Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Composite Primary Keys gem #3527

Merged
merged 1 commit into from
Jun 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ jobs:
orm: active_record
adapter: postgresql
asset: sprockets
- ruby: "3.0"
gemfile: gemfiles/composite_primary_keys.gemfile
orm: active_record
adapter: sqlite3
asset: sprockets
- ruby: 2.7
gemfile: gemfiles/rails_6.0.gemfile
orm: mongoid
Expand Down
18 changes: 18 additions & 0 deletions Appraisals
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,21 @@ appraise 'rails-7.0' do
gem 'paper_trail', '>= 12.0'
end
end

appraise 'composite_primary_keys' do
gem 'rails', '~> 7.0.0'
gem 'sassc-rails', '~> 2.1'
gem 'devise', '~> 4.8'

group :test do
gem 'cancancan', '~> 3.2'
gem 'kt-paperclip'
gem 'rspec-rails', '>= 4.0.0.beta2'
gem 'shrine', '~> 3.0'
end

group :active_record do
gem 'composite_primary_keys'
gem 'paper_trail', '>= 12.0'
end
end
5 changes: 2 additions & 3 deletions app/controllers/rails_admin/main_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,13 @@ def back_or_index
end

def get_sort_hash(model_config)
abstract_model = model_config.abstract_model
field = model_config.list.fields.detect { |f| f.name.to_s == params[:sort] }
# If no sort param, default to the `sort_by` specified in the list config
field ||= model_config.list.possible_fields.detect { |f| f.name == model_config.list.sort_by.to_sym }
field ||= model_config.list.possible_fields.detect { |f| f.name == model_config.list.sort_by.try(:to_sym) }

column =
if field.nil? || field.sortable == false # use default sort, asked field does not exist or is not sortable
"#{abstract_model.table_name}.#{model_config.list.sort_by}"
model_config.list.sort_by
else
field.sort_column
end
Expand Down
8 changes: 8 additions & 0 deletions app/helpers/rails_admin/form_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ def dom_name(field)
(@dom_name ||= {})[field.name] ||= %(#{@object_name}#{options[:index] && "[#{options[:index]}]"}[#{field.method_name}]#{field.is_a?(Config::Fields::Association) && field.multiple? ? '[]' : ''})
end

def hidden_field(method, options = {})
if method == :id
super method, {value: object.id.to_s}
else
super
end
end

protected

def generator_action(action, nested)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
source_abstract_model = RailsAdmin.config(form.object.class).abstract_model

selected = form.object.send(field.name)
selected_ids = selected.map{|s| s.send(field.associated_primary_key)}
selected_ids = selected.map{|s| s.send(field.associated_primary_key).to_s}

current_action = params[:action].in?(['create', 'new']) ? 'create' : 'update'

Expand All @@ -13,7 +13,7 @@
selected.map { |o| [o.send(field.associated_object_label_method), o.send(field.associated_primary_key)] }
else
i = 0
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)] }.sort_by {|a| [selected_ids.index(a[1]) || selected_ids.size, i+=1] }
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] }
end

js_data = {
Expand Down
2 changes: 1 addition & 1 deletion app/views/rails_admin/main/_form_filtering_select.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

xhr = !field.associated_collection_cache_all

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)] }
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] }

js_data = {
xhr: xhr,
Expand Down
2 changes: 1 addition & 1 deletion app/views/rails_admin/main/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@
<tr class="<%= @abstract_model.param_key %>_row <%= @model_config.list.with(object: object).row_css_class %>">
<% if checkboxes %>
<td class="sticky">
<%= check_box_tag "bulk_ids[]", object.id, false %>
<%= check_box_tag "bulk_ids[]", object.id.to_s, false %>
</td>
<% end %>
<% properties.map{ |property| property.bind(:object, object) }.each do |property| %>
Expand Down
23 changes: 23 additions & 0 deletions config/initializers/active_record_extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,27 @@ def safe_send(value)
end
end
end

if defined?(CompositePrimaryKeys)
# Apply patch until the fix is released:
# https://github.com/composite-primary-keys/composite_primary_keys/pull/572
CompositePrimaryKeys::CompositeKeys.class_eval do
alias_method :to_param, :to_s
end

CompositePrimaryKeys::CollectionAssociation.prepend(Module.new do
def ids_writer(ids)
if reflection.association_primary_key.is_a? Array
ids = CompositePrimaryKeys.normalize(Array(ids).reject(&:blank?), reflection.association_primary_key.size)
reflection.association_primary_key.each_with_index do |primary_key, i|
pk_type = klass.type_for_attribute(primary_key)
ids.each do |id|
id[i] = pk_type.cast(id[i]) if id.is_a? Array
end
end
end
super ids
end
end)
end
end
52 changes: 52 additions & 0 deletions gemfiles/composite_primary_keys.gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# This file was generated by Appraisal

source "https://rubygems.org"

gem "appraisal", ">= 2.0"
gem "devise", "~> 4.8"
gem "rails", "~> 7.0.0"
gem "webpacker", require: false
gem "webrick", "~> 1.7"
gem "sassc-rails", "~> 2.1"

group :active_record do
gem "paper_trail", ">= 12.0"
gem "composite_primary_keys"

platforms :ruby, :mswin, :mingw, :x64_mingw do
gem "mysql2", ">= 0.3.14"
gem "sqlite3", ">= 1.3"
end
end

group :development, :test do
gem "pry", ">= 0.9"
end

group :test do
gem "cancancan", "~> 3.2"
gem "carrierwave", [">= 2.0.0.rc", "< 3"]
gem "cuprite"
gem "database_cleaner-active_record", ">= 2.0", require: false
gem "database_cleaner-mongoid", ">= 2.0", require: false
gem "dragonfly", "~> 1.0"
gem "factory_bot", ">= 4.2"
gem "generator_spec", ">= 0.8"
gem "launchy", ">= 2.2"
gem "mini_magick", ">= 3.4"
gem "pundit"
gem "rack-cache", require: "rack/cache"
gem "rspec-expectations", "!= 3.8.3"
gem "rspec-rails", ">= 4.0.0.beta2"
gem "rspec-retry"
gem "rubocop", ["~> 1.20", "!= 1.22.2"], require: false
gem "rubocop-performance", require: false
gem "simplecov", ">= 0.9", require: false
gem "simplecov-lcov", require: false
gem "timecop", ">= 0.5"
gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]
gem "kt-paperclip"
gem "shrine", "~> 3.0"
end

gemspec path: "../"
9 changes: 7 additions & 2 deletions lib/rails_admin/abstract_model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,13 @@ def each_associated_children(object)

def initialize_active_record
@adapter = :active_record
require 'rails_admin/adapters/active_record'
extend Adapters::ActiveRecord
if defined?(::CompositePrimaryKeys)
require 'rails_admin/adapters/composite_primary_keys'
extend Adapters::CompositePrimaryKeys
else
require 'rails_admin/adapters/active_record'
extend Adapters::ActiveRecord
end
end

def initialize_mongoid
Expand Down
25 changes: 23 additions & 2 deletions lib/rails_admin/adapters/active_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ def all(options = {}, scope = nil)
scope ||= scoped
scope = scope.includes(options[:include]) if options[:include]
scope = scope.limit(options[:limit]) if options[:limit]
scope = scope.where(primary_key => options[:bulk_ids]) if options[:bulk_ids]
scope = bulk_scope(scope, options) if options[:bulk_ids]
scope = query_scope(scope, options[:query]) if options[:query]
scope = filter_scope(scope, options[:filters]) if options[:filters]
scope = scope.send(Kaminari.config.page_method_name, options[:page]).per(options[:per]) if options[:page] && options[:per]
scope = scope.reorder("#{options[:sort]} #{options[:sort_reverse] ? 'asc' : 'desc'}") if options[:sort]
scope = sort_scope(scope, options) if options[:sort]
scope
end

Expand Down Expand Up @@ -107,6 +107,27 @@ def adapter_supports_joins?
true
end

private

def bulk_scope(scope, options)
scope.where(primary_key => options[:bulk_ids])
end

def sort_scope(scope, options)
direction = options[:sort_reverse] ? :asc : :desc
case options[:sort]
when String, Symbol
scope.reorder("#{options[:sort]} #{direction}")
when Array
scope.reorder(options[:sort].zip(Array.new(options[:sort].size) { direction }).to_h)
when Hash
scope.reorder(options[:sort].map { |table_name, column| "#{table_name}.#{column}" }.
zip(Array.new(options[:sort].size) { direction }).to_h)
else
raise ArgumentError.new("Unsupported sort value: #{options[:sort]}")
end
end

class WhereBuilder
def initialize(scope)
@statements = []
Expand Down
8 changes: 8 additions & 0 deletions lib/rails_admin/adapters/active_record/association.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ def type
association.macro
end

def field_type
if polymorphic?
:polymorphic_association
else
:"#{association.macro}_association"
end
end

def klass
if options[:polymorphic]
polymorphic_parents(:active_record, model.name.to_s, name) || []
Expand Down
40 changes: 40 additions & 0 deletions lib/rails_admin/adapters/composite_primary_keys.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# frozen_string_literal: true

require 'rails_admin/adapters/active_record'
require 'rails_admin/adapters/composite_primary_keys/association'

module RailsAdmin
module Adapters
module CompositePrimaryKeys
include RailsAdmin::Adapters::ActiveRecord

def get(id, scope = scoped)
begin
object = scope.find(id)
rescue ::ActiveRecord::RecordNotFound
return nil
end

object.extend(RailsAdmin::Adapters::ActiveRecord::ObjectExtension)
end

def associations
model.reflect_on_all_associations.collect do |association|
RailsAdmin::Adapters::CompositePrimaryKeys::Association.new(association, model)
end
end

private

def bulk_scope(scope, options)
if primary_key.is_a? Array
options[:bulk_ids].map do |id|
scope.where(primary_key.zip(::CompositePrimaryKeys::CompositeKeys.parse(id)).to_h)
end.reduce(&:or)
else
super
end
end
end
end
end
45 changes: 45 additions & 0 deletions lib/rails_admin/adapters/composite_primary_keys/association.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# frozen_string_literal: true

module RailsAdmin
module Adapters
module CompositePrimaryKeys
class Association < RailsAdmin::Adapters::ActiveRecord::Association
def field_type
if type == :belongs_to && association.foreign_key.is_a?(Array)
:composite_keys_belongs_to_association
else
super
end
end

def primary_key
return nil if polymorphic?

value = association.association_primary_key

if value.is_a? Array
:id
else
value.to_sym
end
end

def foreign_key
if association.foreign_key.is_a? Array
association.foreign_key.map(&:to_sym)
else
super
end
end

def key_accessor
if type == :belongs_to && foreign_key.is_a?(Array)
:"#{name}_id"
else
super
end
end
end
end
end
end
8 changes: 8 additions & 0 deletions lib/rails_admin/adapters/mongoid/association.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ def type
end
end

def field_type
if polymorphic?
:polymorphic_association
else
:"#{type}_association"
end
end

def klass
if polymorphic? && %i[referenced_in belongs_to].include?(macro)
polymorphic_parents(:mongoid, model.name, name) || []
Expand Down
9 changes: 4 additions & 5 deletions lib/rails_admin/config/fields/factories/association.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@
require 'rails_admin/config/fields/types/belongs_to_association'

RailsAdmin::Config::Fields.register_factory do |parent, properties, fields|
association = parent.abstract_model.associations.detect { |a| a.foreign_key == properties.name && %i[belongs_to has_and_belongs_to_many].include?(a.type) }
if association
field = RailsAdmin::Config::Fields::Types.load("#{association.polymorphic? ? :polymorphic : association.type}_association").new(parent, association.name, association)
parent.abstract_model.associations.filter { |a| Array(a.foreign_key).include?(properties.name) && %i[belongs_to has_and_belongs_to_many].include?(a.type) }.each do |association|
field = RailsAdmin::Config::Fields::Types.load(association.field_type).new(parent, association.name, association)
fields << field

child_columns = []
possible_field_names = if association.polymorphic?
%i[foreign_key foreign_type foreign_inverse_of]
else
[:foreign_key]
end.collect { |k| association.send(k) }.compact
end.flat_map { |k| Array(association.send(k)) }.compact

parent.abstract_model.properties.select { |p| possible_field_names.include? p.name }.each do |column|
child_field = fields.detect { |f| f.name.to_s == column.name.to_s }
Expand All @@ -29,5 +28,5 @@
end

field.children_fields child_columns.collect(&:name)
end
end.any?
end
1 change: 1 addition & 0 deletions lib/rails_admin/config/fields/types/all.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
require 'rails_admin/config/fields/types/belongs_to_association'
require 'rails_admin/config/fields/types/boolean'
require 'rails_admin/config/fields/types/bson_object_id'
require 'rails_admin/config/fields/types/composite_keys_belongs_to_association'
require 'rails_admin/config/fields/types/date'
require 'rails_admin/config/fields/types/datetime'
require 'rails_admin/config/fields/types/decimal'
Expand Down
Loading