0.01
No commit activity in last 3 years
No release in over 3 years
Extend existing shrine gem with using official azure-storage-blob SDK
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 2.0.2
~> 10.0

Runtime

 Project Readme

Shrine::Storage::AzureBlob

Provided memory leakless interface for AzureBlob images/files processing within SHRINE

Mem leak

Installation

Add this lines to your application's Gemfile:

...
gem 'shrine', '~> 2.11'
gem 'azure-storage-blob'
gem 'shrine-redis'
gem 'image_processing', '~> 1.7.1'
gem 'shrine-storage'
gem 'sidekiq'
...

And then execute:

$ bundle

Or install it yourself as:

$ gem install shrine-storage

Usage

  • Create file config/initilizers/shine.rb
require 'shrine'
require 'shrine/storage/redis'
require 'shrine/storage/file_system'

if Rails.env.production?
  azure_options = {
    account_name: ENV.fetch('AZURE_ACCOUNT_NAME'),
    access_key: ENV.fetch('AZURE_ACCESS_KEY'),
    container_name: ENV.fetch('AZURE_CONTAINER')
  }

  Shrine.storages = {
    cache: Shrine::Storage::Redis.new(client: Redis.current, expire: 120),
    store: Shrine::Storage::AzureBlob.new(**azure_options)
  }
else
  Shrine.storages = {
    cache: Shrine::Storage::FileSystem.new('public', prefix: 'storage/cache'), # INFO: temporary
    store: Shrine::Storage::FileSystem.new('public', prefix: 'storage') # INFO: permanent
  }
end

# INFO: Best practice default plugins
Shrine.plugin :activerecord
Shrine.plugin :cached_attachment_data # INFO: for forms
Shrine.plugin :restore_cached_data # INFO: re-extract metadata when attaching a cached file
Shrine.plugin :backgrounding
Shrine.plugin :logging, logger: Rails.logger unless Rails.env.test?

# INFO: Using for provide uploading process through Sidekiq async BG jobs
Shrine::Attacher.promote { |data| AttachmentUploadJob.perform_async(data) }
Shrine::Attacher.delete { |data| AttachmentDeleteJob.perform_async(data) }
  • Add to config/application.rb uploaders dir
...
config.autoload_paths += %w[
  app/uploaders
  ...
].map { |path| Rails.root.join(path).to_s }
...
  • Example of uploader app/uploaders/image_uploader.rb
class ImageUploader < Shrine
  MAX_SIZE = 20
  plugin :remote_url, max_size: MAX_SIZE * 1024 * 1024
  plugin :processing # INFO: allows hooking into promoting
  plugin :validation_helpers
  plugin :versions   # INFO: enable Shrine to handle a hash of files
  plugin :remove_invalid # INFO: immediately delete the file if it failed validations

  unless Rails.env.test? # INFO: delete processed && promoted files after uploading
    plugin :delete_raw
    plugin :delete_promoted
  end

  plugin :determine_mime_type, analyzer: lambda { |io, analyzers|
    mime_type = analyzers[:file].call(io)
    mime_type = analyzers[:mime_types].call(io) if mime_type == 'text/plain'
    mime_type
  }

  opts[:type] = 'image'

  Attacher.validate do
    validate_max_size MAX_SIZE * 1024 * 1024, message: "is too large (max is #{MAX_SIZE} MB)"
    validate_mime_type_inclusion %w[image/jpeg image/png image/gif image/bmp]
  end

  process(:store) do |io, _context|
    # INFO: versions = {} # { original: io } - retain original
    versions = { original: io }
    io.download do |original|
      pipeline = ImageProcessing::MiniMagick.source(original)
      versions[:large]  = pipeline.resize_to_limit!(1280, 1280)
      versions[:medium] = pipeline.resize_to_limit!(640, 640)
      versions[:small]  = pipeline.resize_to_limit!(200, 200)
    end
    versions # INFO: return the hash of processed files
  end
end
  • Create DB migration (for polymorphic association use)
create_table :attachments, id: :serial, force: :cascade do |t|
  t.json :file_data
  t.string :type
  t.string :file_remote_url
  t.string :attachable_type
  t.bigint :attachable_id
  t.index %i[attachable_type attachable_id]
end
  • Create AR models
# INFO: app/models/attachemnt.rb
class Attachment < ApplicationRecord
  include AttachmentUploader::Attachment.new(:file)
  belongs_to :attachable, polymorphic: true, optional: true
end

# INFO: app/models/image.rb
class Image < Attachment
  include ImageUploader::Attachment.new(:file)
end
  • Create Sidekiq Jobs
# INFO: app/jobs/attachemnt_upload.rb
class AttachmentUploadJob
  include Sidekiq::Worker
  sidekiq_options queue: :"attachment:upload", retry: 3

  def perform(data)
    ActiveRecord::Base.uncached do
      klass, id = data['record']
      Shrine::Attacher.promote(data)
    end
  end
end

# INFO: app/jobs/attachemnt_delete.rb
class AttachmentDeleteJob
  include Sidekiq::Worker
  sidekiq_options queue: :"attachment:delete", retry: false

  def perform(data)
    klass, id = data['record']
    Shrine::Attacher.delete(data)
  end
end
  • Configure Sidekiq
# INFO:config/sidekiq.yml
---
production:
  :concurrency: 1
development:
  :concurrency: 1
:queues:
  - 'catalog:attachment:upload'
  - 'catalog:attachment:delete'

# INFO: config/initializers/redis.rb
  Redis.current = Redis.new(host: 'redis', db: '1', port: 6379)

# INFO: config/initializers/sidekiq.rb
  Sidekiq.configure_server { |config| config.redis =  Redis.current }
  Sidekiq.configure_client { |config| config.redis =  Redis.current }
  • Examples of using
  # INFO: app/models/product.rb
  class Product < ApplicationRecord
    has_many :images, as: :attachable, dependent: :destroy
  end

Development

After checking out the repo, run bin/setup to install dependencies. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/anerhan/shrine-storage. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the Shrine::Storage project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.