-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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 for virtual fields (regression) #1586
Comments
A bit related to this, Jumpstart Rails runs a fork of Administrate with this extra commit: Maybe such a patch would enable the described scenario? |
The thing with that patch is that it is for a generator, rather than for code that runs day-to-day. Do they have anything else? |
Not that I’m aware of. |
Then I'm not sure that the linked commit fixes the problem. Instead, it appears to add detection of virtual attributes added with |
It looks like getting the value from the resource in administrate/lib/administrate/page/base.rb Lines 28 to 36 in 844c270
I can think of some solutions (but I guess there would be more):
I guess the last would be the most flexible - but it may lead to other wishes (perhaps a custom value-setter option). I'm actually having a slightly different use-case for virtual fields: combining several model attributes in a single element using a custom field type (for me that's year and week of a publication that is always entered together, and it has a better user- experience to treat it as a single element). |
Thanks to all who helped explain this issue! @wkirby, @Timmitry - For the moment, a workaround might be to add a method of this name (in your example def download
end Can people confirm is this is enough for them while we get to a better solution (which admittedly will still take time)? |
@wvengen - Thank you for looking into this :-) Initially, I'm more inclined towards the first option. I have seen other use cases where having the field decide its value by looking at the whole resource would be useful, and this is exactly a case of that. You are right that it would break the Fields API, so I would want this to happen in an ordered fashion that allowed for initial back-compatibility while plugins and users adapt, first showing warnings for a few versions, then dropping it altogether. Only since recently, the whole Any ideas? I'd welcome a PR if you have the time. A tentative PR to explore the solution would be great, so that there's no pressure to get it right first time, and we can evaluate solutions before committing to one. |
Thanks for your response, @pablobm. We're still evaluating the use of Administrate (coming from Brightcontent). If we go for it, we'll probably have a go at a PR (but it'll take some time). |
I had a quick look at this today in the context of something else. Same as I mentioned back in #1586 (comment), I'm favouring an option where fields would look at the resource and produce a value, instead of having Administrate produce the value after making too many assumptions. This would allow fields having their own getters and setters, which would give them extra flexibility. The following is a diff of where I got to. If someone wants to use it as a basis for a PR, you are welcome to do so! Otherwise, it'll help me remember what this is about when I return to this in the future. diff --git a/lib/administrate/field/base.rb b/lib/administrate/field/base.rb
index ae7a599..f005443 100644
--- a/lib/administrate/field/base.rb
+++ b/lib/administrate/field/base.rb
@@ -32,6 +32,10 @@ module Administrate
attr
end
+ def self.read_value(resource, attribute_name)
+ resource.public_send(attribute_name)
+ end
+
def initialize(attribute, data, page, options = {})
@attribute = attribute
@data = data
diff --git a/lib/administrate/field/deferred.rb b/lib/administrate/field/deferred.rb
index 1060973..40b8bce 100644
--- a/lib/administrate/field/deferred.rb
+++ b/lib/administrate/field/deferred.rb
@@ -10,6 +10,10 @@ module Administrate
attr_reader :deferred_class, :options
+ def read_value(resource, attribute_name)
+ @deferred_class.read_value(resource, attribute_name)
+ end
+
def new(*args)
new_options = args.last.respond_to?(:merge) ? args.pop : {}
deferred_class.new(*args, options.merge(new_options))
diff --git a/lib/administrate/field/url.rb b/lib/administrate/field/url.rb
index b5f1dad..22d2ed9 100644
--- a/lib/administrate/field/url.rb
+++ b/lib/administrate/field/url.rb
@@ -7,6 +7,17 @@ module Administrate
true
end
+ def self.read_value(object, attribute)
+ if attribute == :download_link
+ # Ideally this would be a different field type, but just for
+ # a quick prototype I'm using the Url field and using
+ # this little dirty condition to display a different value.
+ "This is a download link"
+ else
+ super
+ end
+ end
+
def truncate
data.to_s[0...truncation_length]
end
diff --git a/lib/administrate/page/base.rb b/lib/administrate/page/base.rb
index 266deb5..7f6648e 100644
--- a/lib/administrate/page/base.rb
+++ b/lib/administrate/page/base.rb
@@ -32,13 +32,9 @@ module Administrate
private
def attribute_field(dashboard, resource, attribute_name, page)
- value = get_attribute_value(resource, attribute_name)
- field = dashboard.attribute_type_for(attribute_name)
- field.new(attribute_name, value, page, resource: resource)
- end
-
- def get_attribute_value(resource, attribute_name)
- resource.public_send(attribute_name)
+ field_type = dashboard.attribute_type_for(attribute_name)
+ value = field_type.read_value(resource, attribute_name)
+ field_type.new(attribute_name, value, page, resource:)
end
attr_reader :dashboard, :options
diff --git a/spec/example_app/app/dashboards/customer_dashboard.rb b/spec/example_app/app/dashboards/customer_dashboard.rb
index 79786fa..f855706 100644
--- a/spec/example_app/app/dashboards/customer_dashboard.rb
+++ b/spec/example_app/app/dashboards/customer_dashboard.rb
@@ -17,7 +17,8 @@ class CustomerDashboard < Administrate::BaseDashboard
searchable_fields: ["name"],
include_blank: true
),
- password: Field::Password
+ password: Field::Password,
+ download_link: Field::Url,
}
COLLECTION_ATTRIBUTES = ATTRIBUTE_TYPES.keys - %i[created_at updated_at] |
When displaying the list screen, a sorting link is always added, so I think we need to take that into consideration as well. Additionally, I feel we need to redesign this partial to account for cases like the one mentioned #2468 . |
When #920 was merged, it broke apps that depended on the old behaviour.
These are two reports on this issue:
Report by @wkirby in #920 (comment)
This PR appears to break any support for virtual fields by enforcing the the resource must define a method to respond to all keys in the
Dashboard
class. Maybe this is the desired behavior, but the following use case was possible in0.12.0
, and was very useful:This allowed us to inject a download link for our S3 resource without editing the underlying views, or modifying the display for any of the actual attributes.
Is there any advice on how to fix this without defining useless methods on our models?
Report by @Timmitry in #1570 (comment)
We are having similar problems - updating to 0.13 breaks all of our virtual fields (and we have many of those). Since 0.13 fixes a CVE, this is kind of an urgent problem 😕
We fixed the issue temporarily by adding an initializer to
config/initializers
with the following code:Notes
To clarify, this is about creating fields for properties that don't exist at all: not in ActiveRecord, not as methods in the resource.
The text was updated successfully, but these errors were encountered: