Shrine: When ActiveRecord method(build) calling, metadata not overwrite.

Created on 11 Jan 2021  Â·  5Comments  Â·  Source: shrinerb/shrine

Brief Description

Now, I do trying upgrade Shrine from 2.19.3 to 3.3.0.
When minitest extract, metadata['filename'] not overwrite with 3.3.0,
But, metadata['filename'] overwrite with 2.19.3.

Where should I confirm with source code(shrine core or plugin...)?

Expected behavior

attach_test.rb

require "test_helper"

class AttachmentTest < ActiveSupport::TestCase
  test "has a valid factory" do
    assert { build(:private_attachment, :contact).valid? }
  end

  test "file metadata shortcut interfaces" do
    file_data = %!{"id":"691f6d7cd6ad078a622ba043207e9dbe.jpg","storage":"store","metadata":{"filename":"0FB52F2E-3AB7-41A7-BCB5-6251FA754C99.jpeg","size":976220,"mime_type":"image/jpeg","width":4032,"height":3024}}!
    at = build(:private_attachment, :contact, file_data: file_data)
    assert { at.valid? }
    assert { at.filename == "0FB52F2E-3AB7-41A7-BCB5-6251FA754C99.jpeg" }
    assert { at.filesize == 976220 }
    assert { at.mime_type == "image/jpeg" }
  end
end

dumy.jpg is creating with FactoryBot

FactoryBot.define do
  factory :private_attachment do
    parent { nil }
    file { Rack::Test::UploadedFile.new(Rails.root.join("test", "fixtures", "dummy.jpg"), "image/jpg") }

    trait :contact do
      parent { build(:contact) }
    end
  end
end

config/initialize/shrine.rb

(Skip)
Shrine.plugin :cached_attachment_data
Shrine.plugin :activerecord
Shrine.plugin :backgrounding
Shrine.plugin :determine_mime_type, analyzer: :mimemagic
Shrine.logger = Rails.logger
Shrine.plugin :instrumentation
(Skip)

app/models/attachment.rb

class Attachment < ApplicationRecord
  include Uploader[:file]
(Skip)

  def filename
    file.metadata["filename"]
  end

  def filesize
    file.metadata["size"]
  end

  def mime_type
    file.metadata["mime_type"]
  end

When Shrine 2.19.3

app/uploaders/attachment/uploader.rb

class Attachment::Uploader < ::PrivateUploader
  MIME_TYPES = Rails.application.config.x.uploader.file_mime_types
  MAX_SIZE   = Rails.application.config.x.uploader.file_max_size

  folder :private

  Attacher.validate do
    validate_max_size(MAX_SIZE)
    validate_mime_type_inclusion(MIME_TYPES)
  end

  Attacher.delete do |data|
    DeleteJob.perform_async(data)
  end
end

app/jobs/promote_job.rb

class PromoteJob
  include Sidekiq::Worker
  sidekiq_options queue: :"default_#{Rails.application.config.x.caravan_env}", retry: 3

  def perform(data)
    Shrine::Attacher.promote(data)

    Shrine::Attacher.delete(data)
  end
end

app/jobs/delete_job.rb

class DeleteJob
  include Sidekiq::Worker
  sidekiq_options queue: :"default_#{Rails.application.config.x.caravan_env}", retry: 3

  def perform(data)
    Shrine::Attacher.delete(data)
  end
end

Running:

bundle exec rake test

[Minitest::CI] Generating test report in JUnit XML format...


Finished in 8.681666s, 0.2304 runs/s, 0.5759 assertions/s.
2 runs, 5 assertions, 0 failures, 0 errors, 0 skips

Actual behavior

But Shrine 3.3.0

app/uploaders/attachment/uploader.rb

class Attachment::Uploader < ::PrivateUploader
  MIME_TYPES = Rails.application.config.x.uploader.file_mime_types
  MAX_SIZE   = Rails.application.config.x.uploader.file_max_size

  folder :private

  Attacher.validate do
    validate_max_size(MAX_SIZE)
    validate_mime_type_inclusion(MIME_TYPES)
  end

  Attacher.delete do |data|
    DeleteJob.perform_async(data)
  end
end

app/jobs/promote_job.rb

class PromoteJob
  include Sidekiq::Worker
  sidekiq_options queue: :"default_#{Rails.application.config.x.caravan_env}", retry: 3

  def perform(*args)
    if args.one?
      file_data, (record_class, record_id), name, shrine_class =
        args.first.values_at("attachment", "record", "name", "shrine_class")

      record         = Object.const_get(record_class).find(record_id) # if using Active Record 
      attacher_class = Object.const_get(shrine_class)::Attacher
    else
      attacher_class, record_class, record_id, name, file_data = args

      attacher_class = Object.const_get(attacher_class)
      record         = Object.const_get(record_class).find(record_id) # if using Active Record 
    end

    attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
    attacher.atomic_promote
    if args.one?
      data, shrine_class = args.first.values_at("attachment", "shrine_class")

      data           = JSON.parse(data)
      attacher_class = Object.const_get(shrine_class)::Attacher
    else
      attacher_class, data = args

      attacher_class = Object.const_get(attacher_class)
    end

    attacher = attacher_class.from_data(data)
    attacher.destroy
  rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
    # attachment has changed or record has been deleted, nothing to do 
  end
end

app/jobs/delete_job.rb

class DeleteJob
  include Sidekiq::Worker
  sidekiq_options queue: :"default_#{Rails.application.config.x.caravan_env}", retry: 3

  def perform(*args)
    if args.one?
      data, shrine_class = args.first.values_at("attachment", "shrine_class")

      data           = JSON.parse(data)
      attacher_class = Object.const_get(shrine_class)::Attacher
    else
      attacher_class, data = args

      attacher_class = Object.const_get(attacher_class)
    end

    attacher = attacher_class.from_data(data)
    attacher.destroy
  end
end

Running:

bundle exec rake test

Failure:
PrivateAttachmentTest#test_file_metadata_shortcut_interfaces [/caravan-pc/test/models/private_attchment_test.rb:13]:
    assert { at.filename == "0FB52F2E-3AB7-41A7-BCB5-6251FA754C99.jpeg" }
             |  |        |
             |  |        false
             |  "dummy.jpg"
             #<PrivateAttachment id: nil, parent_type: "Contact", parent_id: nil, uid: "ycxgoctf", file_data: "{\"id\":\"691f6d7cd6ad078a622ba043207e9dbe.jpg\",\"stor...", expires_at: nil, created_at: nil, updated_at: nil>

System configuration

  • ruby: 2.6
  • Rails: 5.2
  • Shrine: 2.19.3 → 3.3.0

All 5 comments

Is dummy.jpg file in your project?

  • Difficult problem.
  • We need to see your code.
  • Please show ALL your code.
# try:
file_data = %!{"id":"***.jpg","storage":"store","metadata":{"filename":"***-****-****-***-****.jpeg","size":976220,"mime_type":"image/jpeg","width":4032,"height":3024}}!
at = PrivateAttachment.new(file_data: file_data)
assert { at.filename == "***-****-****-***-****.jpeg" } # dummy.jpg??

@benkoshy
Thanks for reply.
I'm sorry.
I do show all my code above issue.

@tayagi-aim i see that you have updated your response, but i don't receive notifications as a result otherwise you may have received a sooner reply from somebody. let's see if we can't replicate this issue on my end. but that won't be for a few days, unless the issue is solved before then.

@tayagi-aim OK I think I managed to reproduce this problem.

I suspect it comes from the order in which factory_bot overrides methods. Trty with file_data in your factory.rb and not file as you've done before.

Please also see this link: https://shrinerb.com/docs/testing if you want to set up a helper method.

FactoryBot.define do
  factory :private_attachment do   
   #  file # no no no no
      file_data  ...
  end
end

here's what i used to test/reproduce: https://github.com/benkoshy/debugging-shrine-example/tree/debug-522

As @benkoshy said, for test attachment data it's better to assign to the *_data column, as shown in the testing guide.

Was this page helpful?
0 / 5 - 0 ratings