Skip to content

Commit

Permalink
Support for other types of association fields (#1176)
Browse files Browse the repository at this point in the history
After merging thoughtbot/administrate#945, a problem surfaced with plugins that provide new types of association fields, such as https://github.com/pablobm/administrate-field-nested_has_many.

`Administrate::BaseDashboard` hard-codes the possible types of association fields in a list that is later used to determined the permitted params. This means that new types of fields cannot make permitted params and things break.

My proposed solution (see `lib/administrate/base_dashboard.rb`) does a programmatic search for field classes that inherit from `Administrate::Field::Associative`. This allows plugin authors not to worry about adding their new type to a list or any other sort of setup.

A potential problem with this approach is that it includes `Administrate::Field::Polymorphic` on the list. I don't know if this can cause problems down the line or not.
  • Loading branch information
svqualitydev committed Oct 5, 2018
1 parent bde8417 commit 78ba21c
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 11 deletions.
8 changes: 6 additions & 2 deletions lib/administrate/base_dashboard.rb
Expand Up @@ -59,8 +59,6 @@ def display_resource(resource)
end

def association_includes
association_classes = [Field::HasMany, Field::HasOne, Field::BelongsTo]

collection_attributes.map do |key|
field = self.class::ATTRIBUTE_TYPES[key]

Expand All @@ -74,5 +72,11 @@ def association_includes
def attribute_not_found_message(attr)
"Attribute #{attr} could not be found in #{self.class}::ATTRIBUTE_TYPES"
end

def association_classes
@association_classes ||=
ObjectSpace.each_object(Class).
select { |klass| klass < Administrate::Field::Associative }
end
end
end
3 changes: 2 additions & 1 deletion spec/example_app/app/dashboards/customer_dashboard.rb
@@ -1,4 +1,5 @@
require "administrate/base_dashboard"
require "administrate/field/has_many_variant"

class CustomerDashboard < Administrate::BaseDashboard
ATTRIBUTE_TYPES = {
Expand All @@ -8,7 +9,7 @@ class CustomerDashboard < Administrate::BaseDashboard
lifetime_value: Field::Number.with_options(prefix: "$", decimals: 2),
name: Field::String,
orders: Field::HasMany.with_options(limit: 2, sort_by: :id),
log_entries: Field::HasMany.with_options(limit: 2, sort_by: :id),
log_entries: Field::HasManyVariant.with_options(limit: 2, sort_by: :id),
updated_at: Field::DateTime,
kind: Field::Select.with_options(collection: Customer::KINDS),
country: Field::BelongsTo.with_options(
Expand Down
13 changes: 13 additions & 0 deletions spec/example_app/app/views/fields/has_many_variant/_form.html.erb
@@ -0,0 +1,13 @@
<%#
Just a copy of the HasMany _form partial, here to test that
things won't break if the app adds new types of association fields
%>

<div class="field-unit__label">
<%= f.label field.attribute_key, field.attribute %>
</div>
<div class="field-unit__field">
<%= f.select(field.attribute_key, nil, {}, multiple: true) do %>
<%= options_for_select(field.associated_resource_options, field.selected_options) %>
<% end %>
</div>
@@ -0,0 +1,6 @@
<%#
Just a copy of the HasMany _index partial, here to test that
things won't break if the app adds new types of association fields
%>
<%= pluralize(field.data.size, field.attribute.to_s.humanize.downcase.singularize) %>
23 changes: 23 additions & 0 deletions spec/example_app/app/views/fields/has_many_variant/_show.html.erb
@@ -0,0 +1,23 @@
<%#
Just a copy of the HasMany _show partial, here to test that
things won't break if the app adds new types of association fields
%>
<% if field.resources.any? %>
<% order = field.order_from_params(params.fetch(field.name, {})) %>
<% page_number = params.fetch(field.name, {}).fetch(:page, nil) %>
<%= render(
"collection",
collection_presenter: field.associated_collection(order),
collection_field_name: field.name,
page: page,
resources: field.resources(page_number, order),
table_title: field.name,
) %>
<% if field.more_than_limit? %>
<%= paginate field.resources(page_number), param_name: "#{field.name}[page]" %>
<% end %>
<% else %>
<%= t("administrate.fields.has_many.none", default: "–") %>
<% end %>
9 changes: 9 additions & 0 deletions spec/example_app/lib/administrate/field/has_many_variant.rb
@@ -0,0 +1,9 @@
require "administrate/field/has_many"

module Administrate
module Field
class HasManyVariant < HasMany
# Only here to test that this works out of the box
end
end
end
20 changes: 12 additions & 8 deletions spec/features/show_page_spec.rb
Expand Up @@ -10,15 +10,15 @@

visit admin_customer_path(customer)

within(".attribute-data--has-many table[aria-labelledby=orders]") do
within(table_for_attribute(:orders)) do
ids_in_page1 = ids_in_table
expect(ids_in_page1.count).to eq 2
expect(order_ids).to include(*ids_in_page1)
end

click_on("Next ›")

within(".attribute-data--has-many table[aria-labelledby=orders]") do
within(table_for_attribute(:orders)) do
ids_in_page2 = ids_in_table
expect(ids_in_page2.count).to eq 2
expect(order_ids).to include(*ids_in_page2)
Expand Down Expand Up @@ -73,15 +73,15 @@

order_ids = orders.sort_by(&:id).map(&:id).reverse

within(".attribute-data--has-many table[aria-labelledby=orders]") do
within(table_for_attribute(:orders)) do
expect(order_ids.first(2)).to eq(ids_in_table)
end

visit admin_customer_path(customer, orders: {
order: :id, direction: :desc, page: 2
})

within(".attribute-data--has-many table[aria-labelledby=orders]") do
within(table_for_attribute(:orders)) do
expect(order_ids.last(2)).to eq(ids_in_table)
end
end
Expand All @@ -104,11 +104,11 @@
order_ids = orders.sort_by(&:id).map(&:id).reverse
log_entry_ids = log_entries.sort_by(&:id).map(&:id)

within(".attribute-data--has-many table[aria-labelledby=orders]") do
within(table_for_attribute(:orders)) do
expect(order_ids.first(2)).to eq(ids_in_table)
end

within(".attribute-data--has-many table[aria-labelledby=log_entries]") do
within(table_for_attribute(:log_entries)) do
expect(log_entry_ids.first(2)).to eq(ids_in_table)
end

Expand All @@ -121,11 +121,11 @@
},
)

within(".attribute-data--has-many table[aria-labelledby=orders]") do
within(table_for_attribute(:orders)) do
expect(order_ids.last(2)).to eq(ids_in_table)
end

within(".attribute-data--has-many table[aria-labelledby=log_entries]") do
within(table_for_attribute(:log_entries)) do
expect(log_entry_ids.first(2)).to eq(ids_in_table)
end
end
Expand Down Expand Up @@ -211,4 +211,8 @@
def ids_in_table
all("tr td:first-child").map(&:text).map(&:to_i)
end

def table_for_attribute(attr_name)
find("table[aria-labelledby=#{attr_name}]")
end
end

0 comments on commit 78ba21c

Please sign in to comment.