diff --git a/app/views/fields/attached/many/_form.html.erb b/app/views/fields/attached/many/_form.html.erb
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/app/views/fields/attached/many/_show.html.erb b/app/views/fields/attached/many/_show.html.erb
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/app/views/fields/attached/one/_attachment.html.erb b/app/views/fields/attached/one/_attachment.html.erb
new file mode 100644
index 0000000000..a85fce7ca4
--- /dev/null
+++ b/app/views/fields/attached/one/_attachment.html.erb
@@ -0,0 +1,3 @@
+
+ <%= image_tag field.data %>
+
diff --git a/app/views/fields/attached/one/_form.html.erb b/app/views/fields/attached/one/_form.html.erb
new file mode 100644
index 0000000000..de1940d693
--- /dev/null
+++ b/app/views/fields/attached/one/_form.html.erb
@@ -0,0 +1,24 @@
+<%#
+ # Attached One Form Partial
+
+ This partial renders an input element for a single attached attribute.
+ By default, the input is a file field for the attached file.
+
+ ## Local variables:
+
+ - `f`:
+ A Rails form generator, used to help create the appropriate input fields.
+ - `field`:
+ An instance of [Administrate::Field::Attached::One][1].
+ A wrapper around the attachment pulled from the database.
+
+ [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Attached/One
+%>
+
+
+
+ <%= f.label field.attribute %>
+
+
+ <%= f.file_field(field.attribute) %>
+
diff --git a/app/views/fields/attached/one/_show.html.erb b/app/views/fields/attached/one/_show.html.erb
new file mode 100644
index 0000000000..2bfc2119ce
--- /dev/null
+++ b/app/views/fields/attached/one/_show.html.erb
@@ -0,0 +1,22 @@
+<%#
+ # Attached One Show Partial
+
+ This partial renders an attachment attribute,
+ to be displayed on a resource's show page.
+
+ By default, the attribute is rendered as an attachment tag.
+
+ ## Local variables:
+
+ - `field`:
+ An instance of [Administrate::Field::Attached::One][1].
+ A wrapper around the attachment pulled from the database.
+
+ [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Attached::One
+%>
+
+<% if field.attached? %>
+ <%= render partial: "fields/attached/one/attachment", locals: { field: field } %>
+<% else %>
+ No attachment
+<% end %>
diff --git a/lib/administrate/base_dashboard.rb b/lib/administrate/base_dashboard.rb
index 61ef6abed8..22db1acac0 100644
--- a/lib/administrate/base_dashboard.rb
+++ b/lib/administrate/base_dashboard.rb
@@ -1,3 +1,5 @@
+require "administrate/field/attached/one"
+require "administrate/field/attached/many"
require "administrate/field/belongs_to"
require "administrate/field/boolean"
require "administrate/field/date_time"
diff --git a/lib/administrate/field/attached/many.rb b/lib/administrate/field/attached/many.rb
new file mode 100644
index 0000000000..1b3e86519c
--- /dev/null
+++ b/lib/administrate/field/attached/many.rb
@@ -0,0 +1,15 @@
+require "administrate/field/base"
+
+module Administrate
+ module Field
+ module Attached
+ class Many < Administrate::Field::Base
+ delegate :attached?, to: :data, allow_nil: true
+
+ def to_partial_path
+ "fields/attached/many/#{page}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/administrate/field/attached/one.rb b/lib/administrate/field/attached/one.rb
new file mode 100644
index 0000000000..212d9f7b55
--- /dev/null
+++ b/lib/administrate/field/attached/one.rb
@@ -0,0 +1,15 @@
+require "administrate/field/base"
+
+module Administrate
+ module Field
+ module Attached
+ class One < Administrate::Field::Base
+ delegate :attached?, to: :data, allow_nil: true
+
+ def to_partial_path
+ "fields/attached/one/#{page}"
+ end
+ end
+ end
+ end
+end
diff --git a/spec/administrate/views/fields/attached/one/_form_spec.rb b/spec/administrate/views/fields/attached/one/_form_spec.rb
new file mode 100644
index 0000000000..e812193192
--- /dev/null
+++ b/spec/administrate/views/fields/attached/one/_form_spec.rb
@@ -0,0 +1,24 @@
+require "rails_helper"
+require "administrate/field/attached/one"
+
+describe "field/attached/one/_form", type: :view do
+ it "renders a singular file field" do
+ model = build(:product, hero_image: nil)
+ field = instance_double(
+ "Administrate::Field::Attached::One",
+ attribute: :hero_image,
+ data: nil
+ )
+
+ fields model: model do |f|
+ concat render(
+ partial: "fields/attached/one/form",
+ locals: {field: field, f: f}
+ )
+ end
+
+ expect(rendered).to have_field(
+ "product[hero_image]", type: "file"
+ )
+ end
+end
diff --git a/spec/administrate/views/fields/attached/one/_show_spec.rb b/spec/administrate/views/fields/attached/one/_show_spec.rb
new file mode 100644
index 0000000000..cdf374de0f
--- /dev/null
+++ b/spec/administrate/views/fields/attached/one/_show_spec.rb
@@ -0,0 +1,29 @@
+require "rails_helper"
+require "administrate/field/attached/one"
+
+describe "fields/attached/one/_show", type: :view do
+ it "renders a 'No attachment' when missing a value" do
+ field = instance_double(
+ "Administrate::Field::Attached::One",
+ attached?: false
+ )
+
+ render(partial: "fields/attached/one/show", locals: {field: field})
+
+ expect(rendered).to have_text("No attachment")
+ end
+
+ it "renders a preview when attached" do
+ model = build(:product, hero_image: file_fixture("image.png"))
+ field = instance_double(
+ "Administrate::Field::Attached::One",
+ data: model.hero_image,
+ attached?: true
+ )
+
+ render(partial: "fields/attached/one/show", locals: {field: field})
+
+ expect(rendered).to have_element("img", src: rails_blob_url(model.hero_image))
+ .and(have_no_text("No attachment"))
+ end
+end
diff --git a/spec/example_app/app/dashboards/product_dashboard.rb b/spec/example_app/app/dashboards/product_dashboard.rb
index c6e0d76814..039ec2a82e 100644
--- a/spec/example_app/app/dashboards/product_dashboard.rb
+++ b/spec/example_app/app/dashboards/product_dashboard.rb
@@ -9,7 +9,9 @@ class ProductDashboard < Administrate::BaseDashboard
:image_url,
:product_meta_tag,
:release_year,
- :banner
+ :banner,
+ :hero_image,
+ :thumbnails
]
ATTRIBUTE_TYPES = {
@@ -24,7 +26,9 @@ class ProductDashboard < Administrate::BaseDashboard
product_meta_tag: Field::HasOne.with_options(order: "meta_title"),
release_year: Field::Select.with_options(
collection: -> { (Time.current.year - 10)..Time.current.year }
- )
+ ),
+ hero_image: Field::Attached::One,
+ thumbnails: Field::Attached::Many,
}
COLLECTION_ATTRIBUTES = ATTRIBUTES
diff --git a/spec/example_app/app/models/product.rb b/spec/example_app/app/models/product.rb
index fbbc10cc10..0f015d9f75 100644
--- a/spec/example_app/app/models/product.rb
+++ b/spec/example_app/app/models/product.rb
@@ -1,5 +1,7 @@
class Product < ApplicationRecord
has_rich_text :banner
+ has_one_attached :hero_image
+ has_many_attached :thumbnails
def self.policy_class=(policy)
@policy_class = policy
diff --git a/spec/factories.rb b/spec/factories.rb
index 981f3daf01..aaf1b4c270 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -45,6 +45,16 @@
end
product_meta_tag
release_year { [2018, 2019, 2020].sample }
+
+ transient do
+ hero_image { nil }
+ end
+
+ after :build do |record, factory|
+ if (pathname = factory.hero_image)
+ record.hero_image.attach io: pathname.open, filename: pathname.to_s
+ end
+ end
end
factory :product_meta_tag do
diff --git a/spec/features/products_form_spec.rb b/spec/features/products_form_spec.rb
index 4eddddfb48..2b8a99d467 100644
--- a/spec/features/products_form_spec.rb
+++ b/spec/features/products_form_spec.rb
@@ -3,21 +3,23 @@
describe "product form has_one relationship" do
include ActiveSupport::Testing::TimeHelpers
- it "saves product and meta tag data correctly" do
+ it "saves product and meta tag data correctly", :js do
visit new_admin_product_path
fill_in "Name", with: "Example"
fill_in "Price", with: "0"
fill_in "Description", with: "Example"
fill_in "Image url", with: "http://imageurlthatdoesnotexist"
- fill_in "Meta title", with: "Example meta title"
- fill_in "Meta description", with: "Example meta description"
-
- expect(page).to have_css("legend", text: "Product Meta Tag")
+ within_fieldset "Product Meta Tag" do
+ fill_in "Meta title", with: "Example meta title"
+ fill_in "Meta description", with: "Example meta description"
+ end
+ attach_file "Hero image", file_fixture("image.png")
click_on "Create Product"
expect(page).to have_link("Example meta title")
+ .and(have_element("img", src: /image.png/))
expect(page).to have_flash(
t("administrate.controller.create.success", resource: "Product")
)
@@ -53,17 +55,21 @@
ProductDashboard::ATTRIBUTE_TYPES[:release_year] = old_release_year
end
- it "edits product and meta tag data correctly" do
- product = create(:product)
+ it "edits product and meta tag data correctly", :js do
+ product = create(:product, hero_image: file_fixture("image.svg"))
+ hero_image = product.hero_image
visit edit_admin_product_path(product)
-
+ attach_file "Hero image", file_fixture("image.png")
click_on "Update Product"
expect(page).to have_link(product.product_meta_tag.meta_title.to_s)
+ .and(have_element("img", src: /image.png/))
+ .and(have_no_element("img", src: /image.svg/))
expect(page).to have_flash(
t("administrate.controller.update.success", resource: "Product")
)
+ expect { hero_image.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
describe "has_one relationships" do
diff --git a/spec/fixtures/files/image.png b/spec/fixtures/files/image.png
new file mode 100644
index 0000000000..96d1aa048b
Binary files /dev/null and b/spec/fixtures/files/image.png differ
diff --git a/spec/fixtures/files/image.svg b/spec/fixtures/files/image.svg
new file mode 100644
index 0000000000..2ecf7b5071
--- /dev/null
+++ b/spec/fixtures/files/image.svg
@@ -0,0 +1,5 @@
+
diff --git a/spec/support/factory_girl.rb b/spec/support/factory_bot.rb
similarity index 100%
rename from spec/support/factory_girl.rb
rename to spec/support/factory_bot.rb