From f3776794ba34370bf1364cae660c144e2a1a8664 Mon Sep 17 00:00:00 2001 From: nakamura Date: Fri, 13 Sep 2024 17:41:11 +0000 Subject: [PATCH 01/13] Support for virtual fields --- lib/administrate/field/base.rb | 13 ++++++++ lib/administrate/field/deferred.rb | 8 +++++ lib/administrate/page/base.rb | 7 +---- .../app/dashboards/customer_dashboard.rb | 4 +++ spec/lib/fields/base_spec.rb | 30 +++++++++++++++++++ 5 files changed, 56 insertions(+), 6 deletions(-) diff --git a/lib/administrate/field/base.rb b/lib/administrate/field/base.rb index c086603afb..a87eea5687 100644 --- a/lib/administrate/field/base.rb +++ b/lib/administrate/field/base.rb @@ -38,6 +38,7 @@ def initialize(attribute, data, page, options = {}) @page = page @resource = options.delete(:resource) @options = options + @data = read_value if @data.nil? end def html_class @@ -52,6 +53,18 @@ def name attribute.to_s end + def read_value + if options.key?(:getter) + if options[:getter].respond_to?(:call) + options[:getter].call(self) + else + resource&.public_send(options[:getter]) + end + else + resource&.public_send(attribute) + end + end + def to_partial_path "/fields/#{self.class.field_type}/#{page}" end diff --git a/lib/administrate/field/deferred.rb b/lib/administrate/field/deferred.rb index 10609735af..06ac106ff1 100644 --- a/lib/administrate/field/deferred.rb +++ b/lib/administrate/field/deferred.rb @@ -21,6 +21,10 @@ def ==(other) options == other.options end + def getter + options.fetch(:getter, nil) + end + def associative? deferred_class.associative? end @@ -57,6 +61,10 @@ def permitted_attribute(attr, opts = {}) end end + def read_value(resource, attribute_name) + @deferred_class.read_value(resource, attribute_name, options) + end + delegate :html_class, to: :deferred_class end end diff --git a/lib/administrate/page/base.rb b/lib/administrate/page/base.rb index 78fbf1a6bd..56c61892d3 100644 --- a/lib/administrate/page/base.rb +++ b/lib/administrate/page/base.rb @@ -30,13 +30,8 @@ def item_associations 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.new(attribute_name, nil, page, resource: 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 79786fa4f7..abd203f765 100644 --- a/spec/example_app/app/dashboards/customer_dashboard.rb +++ b/spec/example_app/app/dashboards/customer_dashboard.rb @@ -8,6 +8,10 @@ class CustomerDashboard < Administrate::BaseDashboard email_subscriber: Field::Boolean, lifetime_value: Field::Number.with_options(prefix: "$", decimals: 2), name: Field::String, + nickname: Field::String.with_options( + getter: ->(field) { "Mr. #{field.resource.name}man" if field.resource.name.present? }, + searchable: false + ), orders: Field::HasMany.with_options(limit: 2, sort_by: :id), log_entries: Field::HasManyVariant.with_options(limit: 2, sort_by: :id), updated_at: Field::DateTime, diff --git a/spec/lib/fields/base_spec.rb b/spec/lib/fields/base_spec.rb index 43b71efc50..459207468b 100644 --- a/spec/lib/fields/base_spec.rb +++ b/spec/lib/fields/base_spec.rb @@ -167,4 +167,34 @@ expect(field.required?).to eq(false) end end + + describe "read_value" do + it "reads the value from the resource" do + resource = double(attribute: "value") + field = field_class.new(:attribute, :date, :page, resource: resource) + + expect(field.read_value).to eq("value") + end + + it "reads the value from the resource with a custom getter" do + resource = double(custom_getter: "value") + field = field_class.new(:attribute, :date, :page, resource: resource, getter: :custom_getter) + + expect(field.read_value).to eq("value") + end + + it "reads the value from the resource with a custom getter block" do + resource = double + field = field_class.new(:attribute, :date, :page, resource: resource, getter: ->(field) { field.resource.custom_getter }) + + expect(resource).to receive(:custom_getter) + field.read_value + end + + it "returns nil if the resource is nil" do + field = field_class.new(:attribute, :date, :page, resource: nil) + + expect(field.read_value).to eq(nil) + end + end end From a575bdedaa2b9d5ce9edc54a50b244f31368572d Mon Sep 17 00:00:00 2001 From: nakamura Date: Sat, 14 Sep 2024 13:13:53 +0000 Subject: [PATCH 02/13] WIP: fix read_value method in Administrate::Field::Base --- lib/administrate/field/base.rb | 11 ++++---- spec/lib/fields/base_spec.rb | 4 +-- spec/lib/fields/virtual_field_spec.rb | 37 +++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 spec/lib/fields/virtual_field_spec.rb diff --git a/lib/administrate/field/base.rb b/lib/administrate/field/base.rb index a87eea5687..bda4bedc4d 100644 --- a/lib/administrate/field/base.rb +++ b/lib/administrate/field/base.rb @@ -34,11 +34,10 @@ def self.permitted_attribute(attr, _options = nil) def initialize(attribute, data, page, options = {}) @attribute = attribute - @data = data @page = page @resource = options.delete(:resource) @options = options - @data = read_value if @data.nil? + @data = read_value(data) end def html_class @@ -53,15 +52,17 @@ def name attribute.to_s end - def read_value + def read_value(data = nil) if options.key?(:getter) if options[:getter].respond_to?(:call) options[:getter].call(self) else - resource&.public_send(options[:getter]) + resource.try(options[:getter]) end + elsif data.nil? + resource.try(attribute) else - resource&.public_send(attribute) + data end end diff --git a/spec/lib/fields/base_spec.rb b/spec/lib/fields/base_spec.rb index 459207468b..cc537198df 100644 --- a/spec/lib/fields/base_spec.rb +++ b/spec/lib/fields/base_spec.rb @@ -184,11 +184,11 @@ end it "reads the value from the resource with a custom getter block" do - resource = double + resource = double("Model", custom_getter: "value") field = field_class.new(:attribute, :date, :page, resource: resource, getter: ->(field) { field.resource.custom_getter }) expect(resource).to receive(:custom_getter) - field.read_value + expect(field.read_value).to eq("value") end it "returns nil if the resource is nil" do diff --git a/spec/lib/fields/virtual_field_spec.rb b/spec/lib/fields/virtual_field_spec.rb new file mode 100644 index 0000000000..8340b27769 --- /dev/null +++ b/spec/lib/fields/virtual_field_spec.rb @@ -0,0 +1,37 @@ +require "rails_helper" +require "administrate/field/base" + +module Administrate + module Field + class VirtualField < Field::Base + def self.searchable? + false + end + + def read_value(data = nil) + resource.inspect + end + + def foobar + "This is a Foobar: #{data}" + end + end + end +end + +describe Administrate::Field::VirtualField do + describe "#foobar" do + it "displays the foobar" do + resource = double("Model", inspect: "Inspecting") + + field = Administrate::Field::VirtualField.new( + :virtual_field, + nil, + :show, + resource: resource + ) + + expect(field.foobar).to eq("This is a Foobar: Inspecting") + end + end +end From 7266daa86392db64574c18b028fa994a7c974fdb Mon Sep 17 00:00:00 2001 From: nakamura Date: Wed, 25 Sep 2024 19:20:08 +0000 Subject: [PATCH 03/13] Add virtual field full_address for sample purposes --- .../app/dashboards/order_dashboard.rb | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/spec/example_app/app/dashboards/order_dashboard.rb b/spec/example_app/app/dashboards/order_dashboard.rb index 1e1407b52d..a83efbb2c3 100644 --- a/spec/example_app/app/dashboards/order_dashboard.rb +++ b/spec/example_app/app/dashboards/order_dashboard.rb @@ -10,6 +10,19 @@ class OrderDashboard < Administrate::BaseDashboard address_city: Field::String, address_state: Field::String, address_zip: Field::String, + full_address: Field::String.with_options( + getter: ->(field) { + r = field.resource + [ + r.address_line_one, + r.address_line_two, + r.address_city, + r.address_state, + r.address_zip + ].compact.join("\n") + }, + searchable: false + ), customer: Field::BelongsTo.with_options(order: "name"), line_items: Field::HasMany.with_options( collection_attributes: %i[product quantity unit_price total_price] @@ -29,7 +42,7 @@ class OrderDashboard < Administrate::BaseDashboard COLLECTION_ATTRIBUTES = [ :id, :customer, - :address_state, + :full_address, :total_price, :line_items, :shipped_at @@ -50,8 +63,11 @@ class OrderDashboard < Administrate::BaseDashboard address_zip ] }.freeze - SHOW_PAGE_ATTRIBUTES = FORM_ATTRIBUTES.merge( - "" => %i[customer created_at updated_at], - "details" => %i[line_items total_price shipped_at payments] - ).freeze + SHOW_PAGE_ATTRIBUTES = FORM_ATTRIBUTES + .except("address") + .merge( + "" => %i[customer full_address created_at updated_at], + "details" => %i[line_items total_price shipped_at payments] + ) + .freeze end From 07e927d10da121a147fb682f1748332af8886b1b Mon Sep 17 00:00:00 2001 From: nakamura Date: Sun, 29 Sep 2024 15:46:03 +0900 Subject: [PATCH 04/13] Update spec/lib/fields/base_spec.rb Co-authored-by: Pablo Brasero <36066+pablobm@users.noreply.github.com> --- spec/lib/fields/base_spec.rb | 50 ++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/spec/lib/fields/base_spec.rb b/spec/lib/fields/base_spec.rb index cc537198df..49cd56d221 100644 --- a/spec/lib/fields/base_spec.rb +++ b/spec/lib/fields/base_spec.rb @@ -168,33 +168,49 @@ end end - describe "read_value" do - it "reads the value from the resource" do - resource = double(attribute: "value") - field = field_class.new(:attribute, :date, :page, resource: resource) + describe "#data" do + context "when given nil data" do + it "reads the value from the resource" do + resource = double(attribute: "resource value") + field = field_class.new(:attribute, nil, :page, resource: resource) + + expect(field.data).to eq("resource value") + end + end + + context "when given non-nil data" do + it "uses the given data" do + resource = double(attribute: "resource value") + field = field_class.new(:attribute, "given value", :page, resource: resource) - expect(field.read_value).to eq("value") + expect(field.data).to eq("given value") + end end - it "reads the value from the resource with a custom getter" do - resource = double(custom_getter: "value") - field = field_class.new(:attribute, :date, :page, resource: resource, getter: :custom_getter) + context "when given a :getter value" do + it "reads the attribute with the name of the value" do + resource = double(custom_getter: "custom value") + field = field_class.new(:attribute, :date, :page, resource: resource, getter: :custom_getter) - expect(field.read_value).to eq("value") + expect(field.data).to eq("custom value") + end end - it "reads the value from the resource with a custom getter block" do - resource = double("Model", custom_getter: "value") - field = field_class.new(:attribute, :date, :page, resource: resource, getter: ->(field) { field.resource.custom_getter }) + context "when given a :getter block" do + it "uses it to produce a value" do + resource = double("Model", custom_getter: "custom value") + field = field_class.new(:attribute, :date, :page, resource: resource, getter: ->(f) { f.resource.custom_getter + " from block" }) - expect(resource).to receive(:custom_getter) - expect(field.read_value).to eq("value") + expect(field.data).to eq("custom value from block") + end end - it "returns nil if the resource is nil" do - field = field_class.new(:attribute, :date, :page, resource: nil) + context "when given a :getter block" do + it "returns nil if the resource is nil" do + field = field_class.new(:attribute, nil, :page, resource: nil) - expect(field.read_value).to eq(nil) + expect(field.data).to eq(nil) + end end end end From c6c32a4f892879d08b8a993c2c101731f1a017a1 Mon Sep 17 00:00:00 2001 From: nakamura Date: Sun, 29 Sep 2024 07:35:23 +0000 Subject: [PATCH 05/13] Add ReceiptLink field as virtual field sample --- .../app/dashboards/payment_dashboard.rb | 5 +++- .../views/fields/receipt_link/_index.html.erb | 1 + .../views/fields/receipt_link/_show.html.erb | 1 + .../lib/administrate/field/receipt_link.rb | 15 ++++++++++++ spec/features/payments_index_spec.rb | 23 +++++++++++++++++++ 5 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 spec/example_app/app/views/fields/receipt_link/_index.html.erb create mode 100644 spec/example_app/app/views/fields/receipt_link/_show.html.erb create mode 100644 spec/example_app/lib/administrate/field/receipt_link.rb create mode 100644 spec/features/payments_index_spec.rb diff --git a/spec/example_app/app/dashboards/payment_dashboard.rb b/spec/example_app/app/dashboards/payment_dashboard.rb index 2460311de2..a7ede659be 100644 --- a/spec/example_app/app/dashboards/payment_dashboard.rb +++ b/spec/example_app/app/dashboards/payment_dashboard.rb @@ -1,15 +1,18 @@ +require "administrate/field/receipt_link" require "administrate/base_dashboard" class PaymentDashboard < Administrate::BaseDashboard ATTRIBUTE_TYPES = { id: Field::Number, + receipt: Field::ReceiptLink, created_at: Field::DateTime, updated_at: Field::DateTime, order: Field::BelongsTo } COLLECTION_ATTRIBUTES = [ - :id + :id, + :receipt ] SHOW_PAGE_ATTRIBUTES = ATTRIBUTE_TYPES.keys diff --git a/spec/example_app/app/views/fields/receipt_link/_index.html.erb b/spec/example_app/app/views/fields/receipt_link/_index.html.erb new file mode 100644 index 0000000000..9f755667e8 --- /dev/null +++ b/spec/example_app/app/views/fields/receipt_link/_index.html.erb @@ -0,0 +1 @@ +<%= link_to field.filename, field.data %> diff --git a/spec/example_app/app/views/fields/receipt_link/_show.html.erb b/spec/example_app/app/views/fields/receipt_link/_show.html.erb new file mode 100644 index 0000000000..9f755667e8 --- /dev/null +++ b/spec/example_app/app/views/fields/receipt_link/_show.html.erb @@ -0,0 +1 @@ +<%= link_to field.filename, field.data %> diff --git a/spec/example_app/lib/administrate/field/receipt_link.rb b/spec/example_app/lib/administrate/field/receipt_link.rb new file mode 100644 index 0000000000..0756b259ce --- /dev/null +++ b/spec/example_app/lib/administrate/field/receipt_link.rb @@ -0,0 +1,15 @@ +require "administrate/field/base" + +module Administrate + module Field + class ReceiptLink < Base + def data + "/files/receipts/#{filename}" + end + + def filename + "receipt-#{resource.id}.pdf" + end + end + end +end diff --git a/spec/features/payments_index_spec.rb b/spec/features/payments_index_spec.rb new file mode 100644 index 0000000000..1d16401767 --- /dev/null +++ b/spec/features/payments_index_spec.rb @@ -0,0 +1,23 @@ +require "rails_helper" + +RSpec.describe "payment index page" do + it "displays payments' id and receipt link" do + payment = create(:payment) + + visit admin_payments_path + + expect(page).to have_header("Payments") + expect(page).to have_content(payment.id) + expect(page).to have_content("receipt-#{payment.id}.pdf") + end + + it "links to the payment show page", :js do + payment = create(:payment) + + visit admin_payments_path + click_row_for(payment) + + expect(page).to have_content(payment.id) + expect(current_path).to eq(admin_payment_path(payment)) + end +end From bb4e196e81cf3d02cf84bf917553dd59af0956e3 Mon Sep 17 00:00:00 2001 From: nakamura Date: Sun, 29 Sep 2024 07:44:17 +0000 Subject: [PATCH 06/13] Remove unnecessary examples and specs --- .../app/dashboards/customer_dashboard.rb | 4 -- spec/lib/fields/virtual_field_spec.rb | 37 ------------------- 2 files changed, 41 deletions(-) delete mode 100644 spec/lib/fields/virtual_field_spec.rb diff --git a/spec/example_app/app/dashboards/customer_dashboard.rb b/spec/example_app/app/dashboards/customer_dashboard.rb index abd203f765..79786fa4f7 100644 --- a/spec/example_app/app/dashboards/customer_dashboard.rb +++ b/spec/example_app/app/dashboards/customer_dashboard.rb @@ -8,10 +8,6 @@ class CustomerDashboard < Administrate::BaseDashboard email_subscriber: Field::Boolean, lifetime_value: Field::Number.with_options(prefix: "$", decimals: 2), name: Field::String, - nickname: Field::String.with_options( - getter: ->(field) { "Mr. #{field.resource.name}man" if field.resource.name.present? }, - searchable: false - ), orders: Field::HasMany.with_options(limit: 2, sort_by: :id), log_entries: Field::HasManyVariant.with_options(limit: 2, sort_by: :id), updated_at: Field::DateTime, diff --git a/spec/lib/fields/virtual_field_spec.rb b/spec/lib/fields/virtual_field_spec.rb deleted file mode 100644 index 8340b27769..0000000000 --- a/spec/lib/fields/virtual_field_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -require "rails_helper" -require "administrate/field/base" - -module Administrate - module Field - class VirtualField < Field::Base - def self.searchable? - false - end - - def read_value(data = nil) - resource.inspect - end - - def foobar - "This is a Foobar: #{data}" - end - end - end -end - -describe Administrate::Field::VirtualField do - describe "#foobar" do - it "displays the foobar" do - resource = double("Model", inspect: "Inspecting") - - field = Administrate::Field::VirtualField.new( - :virtual_field, - nil, - :show, - resource: resource - ) - - expect(field.foobar).to eq("This is a Foobar: Inspecting") - end - end -end From cab60966f9eb8edca26c5bfd18b75962c59691eb Mon Sep 17 00:00:00 2001 From: nakamura Date: Sun, 29 Sep 2024 08:30:18 +0000 Subject: [PATCH 07/13] Force searchable to false when getter is specified --- lib/administrate/field/deferred.rb | 6 +++++- spec/example_app/app/dashboards/order_dashboard.rb | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/administrate/field/deferred.rb b/lib/administrate/field/deferred.rb index 06ac106ff1..fac8c28cad 100644 --- a/lib/administrate/field/deferred.rb +++ b/lib/administrate/field/deferred.rb @@ -34,7 +34,11 @@ def eager_load? end def searchable? - options.fetch(:searchable, deferred_class.searchable?) + if options.key?(:getter) + false + else + options.fetch(:searchable, deferred_class.searchable?) + end end def searchable_field diff --git a/spec/example_app/app/dashboards/order_dashboard.rb b/spec/example_app/app/dashboards/order_dashboard.rb index a83efbb2c3..631572e217 100644 --- a/spec/example_app/app/dashboards/order_dashboard.rb +++ b/spec/example_app/app/dashboards/order_dashboard.rb @@ -20,8 +20,7 @@ class OrderDashboard < Administrate::BaseDashboard r.address_state, r.address_zip ].compact.join("\n") - }, - searchable: false + } ), customer: Field::BelongsTo.with_options(order: "name"), line_items: Field::HasMany.with_options( From d1fd3a9b08eebe9c78f355a5d40c5935648ba41c Mon Sep 17 00:00:00 2001 From: nakamura Date: Sun, 29 Sep 2024 20:01:00 +0000 Subject: [PATCH 08/13] Add docs about virtual fields --- docs/customizing_dashboards.md | 130 +++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/docs/customizing_dashboards.md b/docs/customizing_dashboards.md index 6d4d839d49..31abcf9e0b 100644 --- a/docs/customizing_dashboards.md +++ b/docs/customizing_dashboards.md @@ -436,3 +436,133 @@ en: ``` If not defined (see `SHOW_PAGE_ATTRIBUTES` above), Administrate will default to the given strings. + +## Virtual Attributes + +For all field types, you can use the `getter` option to change where the data is retrieved from or to set the data directly. + +By using this, you can define an attribute in `ATTRIBUTE_TYPES` that doesn’t exist in the model, and use it for various purposes. + +### Attribute Aliases + +You can create an alias for an attribute. For example: + +```ruby + ATTRIBUTE_TYPES = { + shipped_at: Field::DateTime, + shipped_on: Field::Date.with_options( + getter: :shipped_at + ) + } + COLLECTION_ATTRIBUTES = [ + :shipped_on + } + SHOW_PAGE_ATTRIBUTES = [ + :shipped_at + } +``` + +In this example, a virtual attribute `shipped_on` based on the value of `shipped_at` is defined as a `Date` type and used for display on the index page (this can help save table cell space). + +### Decorated Attributes + +You can also use this to decorate data. For example: + +```ruby + ATTRIBUTE_TYPES = { + price: Field::Number, + price_including_tax: Field::Number.with_options( + getter: -> (field) { + field.resource.price * 1.1 if field.resource.price.present? + } + ) + } +``` + +### Composite Attributes + +You can dynamically create a virtual attribute by combining multiple attributes for display. For example: + +```ruby + ATTRIBUTE_TYPES = { + first_name: Field::String, + last_name: Field::String, + full_name: Field::String.with_options( + getter: -> (field) { + [ + field.resource.first_name, + field.resource.last_name + ].compact_blank.join(' ') + } + ) + } +``` + +## Virtual Fields + +Custom fields can also be combined with virtual attributes. + +```ruby + ATTRIBUTE_TYPES = { + id: Field::Number, + receipt: Field::ReceiptLink + } +``` + +```ruby +module Administrate + module Field + class ReceiptLink < Base + def data + resource.id + end + + def filename + "receipt-#{data}.pdf" + end + + def url + "/files/receipts/#{filename}" + end + end + end +end +``` + +```erb +<%= link_to field.filename, field.url %> +``` + +### Custom Actions via Virtual Field + +By creating custom fields that are not dependent on specific attributes, you can insert custom views into any screen. +For example, you can add custom buttons like this: + +```ruby + ATTRIBUTE_TYPES = { + id: Field::Number, + custom_index_actions: Field::CustomActionButtons, + custom_show_actions: Field::CustomActionButtons, + } +``` + +```ruby +module Administrate + module Field + class CustomActionButtons < Base + def data + resource.id + end + end + end +end +``` + +```erb +<%# app/views/fields/custom_action_buttons/_index.html.erb %> +<% if field.data.present? %> + <%= button_to "some action 1", [:some_action_1, namespace, field.resource] %> + <%= button_to "some action 2", [:some_action_2, namespace, field.resource] %> + <%= button_to "some action 3", [:some_action_3, namespace, field.resource] %> +<% end %> +``` From bf727b70df6cf475e6220b564b4b540de5f31e94 Mon Sep 17 00:00:00 2001 From: nakamura Date: Tue, 22 Oct 2024 23:07:16 +0900 Subject: [PATCH 09/13] Update docs/customizing_dashboards.md Co-authored-by: Pablo Brasero <36066+pablobm@users.noreply.github.com> --- docs/customizing_dashboards.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/customizing_dashboards.md b/docs/customizing_dashboards.md index 31abcf9e0b..e5ea877ffc 100644 --- a/docs/customizing_dashboards.md +++ b/docs/customizing_dashboards.md @@ -500,7 +500,7 @@ You can dynamically create a virtual attribute by combining multiple attributes ## Virtual Fields -Custom fields can also be combined with virtual attributes. +Custom fields can also be defined using virtual fields. ```ruby ATTRIBUTE_TYPES = { From 87c7e4c08af19459f0c5e4ea21dff1c28198a651 Mon Sep 17 00:00:00 2001 From: nakamura Date: Tue, 22 Oct 2024 14:38:11 +0000 Subject: [PATCH 10/13] Refactor base_spec.rb to remove unnecessary code --- spec/lib/fields/base_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/lib/fields/base_spec.rb b/spec/lib/fields/base_spec.rb index 49cd56d221..44af91340e 100644 --- a/spec/lib/fields/base_spec.rb +++ b/spec/lib/fields/base_spec.rb @@ -203,9 +203,7 @@ expect(field.data).to eq("custom value from block") end - end - context "when given a :getter block" do it "returns nil if the resource is nil" do field = field_class.new(:attribute, nil, :page, resource: nil) From ff843b8070fe938b1ce070ef8a5abdd8d74bbe2c Mon Sep 17 00:00:00 2001 From: Pablo Brasero Date: Thu, 24 Oct 2024 09:36:31 +0100 Subject: [PATCH 11/13] Unused. Leftover from changes? --- lib/administrate/field/base.rb | 2 +- lib/administrate/field/deferred.rb | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/administrate/field/base.rb b/lib/administrate/field/base.rb index bda4bedc4d..65121587c9 100644 --- a/lib/administrate/field/base.rb +++ b/lib/administrate/field/base.rb @@ -52,7 +52,7 @@ def name attribute.to_s end - def read_value(data = nil) + def read_value(data) if options.key?(:getter) if options[:getter].respond_to?(:call) options[:getter].call(self) diff --git a/lib/administrate/field/deferred.rb b/lib/administrate/field/deferred.rb index fac8c28cad..ae3a79e851 100644 --- a/lib/administrate/field/deferred.rb +++ b/lib/administrate/field/deferred.rb @@ -65,10 +65,6 @@ def permitted_attribute(attr, opts = {}) end end - def read_value(resource, attribute_name) - @deferred_class.read_value(resource, attribute_name, options) - end - delegate :html_class, to: :deferred_class end end From 9d432754a9c76cfa4e8bf5ac771900d7d5a7c787 Mon Sep 17 00:00:00 2001 From: Pablo Brasero Date: Thu, 24 Oct 2024 10:12:32 +0100 Subject: [PATCH 12/13] Better experience for example app --- spec/example_app/app/controllers/files_controller.rb | 12 ++++++++++++ spec/example_app/config/routes.rb | 2 ++ .../lib/administrate/field/receipt_link.rb | 2 +- spec/features/payments_index_spec.rb | 12 +++++++++++- 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 spec/example_app/app/controllers/files_controller.rb diff --git a/spec/example_app/app/controllers/files_controller.rb b/spec/example_app/app/controllers/files_controller.rb new file mode 100644 index 0000000000..e6f6b2ea15 --- /dev/null +++ b/spec/example_app/app/controllers/files_controller.rb @@ -0,0 +1,12 @@ +class FilesController < ApplicationController + def download + filename = params[:filename] + match = %r{receipt-(\d+)}.match(filename) + if match + payment_id = match[1] + send_data("This is the receipt for payment ##{payment_id}", filename: "#{filename}.txt") + else + render status: 404, layout: false, file: Rails.root.join("public/404.html") + end + end +end diff --git a/spec/example_app/config/routes.rb b/spec/example_app/config/routes.rb index f8fc984e8b..53ce954aec 100644 --- a/spec/example_app/config/routes.rb +++ b/spec/example_app/config/routes.rb @@ -24,6 +24,8 @@ root to: "customers#index" end + get "/files/receipts/*filename.txt", to: "files#download" + get "/*page", to: "docs#show", constraints: ->(request) { !request.path.start_with?("/rails/") } root to: "docs#index" end diff --git a/spec/example_app/lib/administrate/field/receipt_link.rb b/spec/example_app/lib/administrate/field/receipt_link.rb index 0756b259ce..bbe7192109 100644 --- a/spec/example_app/lib/administrate/field/receipt_link.rb +++ b/spec/example_app/lib/administrate/field/receipt_link.rb @@ -8,7 +8,7 @@ def data end def filename - "receipt-#{resource.id}.pdf" + "receipt-#{resource.id}.txt" end end end diff --git a/spec/features/payments_index_spec.rb b/spec/features/payments_index_spec.rb index 1d16401767..b5365ff142 100644 --- a/spec/features/payments_index_spec.rb +++ b/spec/features/payments_index_spec.rb @@ -8,7 +8,17 @@ expect(page).to have_header("Payments") expect(page).to have_content(payment.id) - expect(page).to have_content("receipt-#{payment.id}.pdf") + expect(page).to have_content("receipt-#{payment.id}.txt") + end + + it "allows downloading the receipt" do + payment = create(:payment) + + visit admin_payments_path + click_on("receipt-#{payment.id}.txt") + + expect(page.body).to eq("This is the receipt for payment ##{payment.id}") + expect(response_headers["Content-Disposition"]).to match(%r{^attachment; filename=}) end it "links to the payment show page", :js do From 4ea760a20e30b5346d7c9936555fb4669fc0fb3d Mon Sep 17 00:00:00 2001 From: nakamura Date: Thu, 24 Oct 2024 14:07:48 +0000 Subject: [PATCH 13/13] Refactor payment_index_spec to use have_current_path instead of current_path --- spec/features/payments_index_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/payments_index_spec.rb b/spec/features/payments_index_spec.rb index b5365ff142..3b21ce23b4 100644 --- a/spec/features/payments_index_spec.rb +++ b/spec/features/payments_index_spec.rb @@ -28,6 +28,6 @@ click_row_for(payment) expect(page).to have_content(payment.id) - expect(current_path).to eq(admin_payment_path(payment)) + expect(page).to have_current_path(admin_payment_path(payment)) end end