Add attachments

This commit is contained in:
David Heinemeier Hansson 2017-07-05 15:18:50 +02:00
parent 97aa328bb1
commit aaf8415188
9 changed files with 132 additions and 25 deletions

View File

@ -6,26 +6,11 @@
```ruby
class Person < ApplicationRecord
has_one :avatar
end
class Avatar < ApplicationRecord
belongs_to :person
belongs_to :image, class_name: 'ActiveVault::Blob'
has_file :image
has_file :avatar
end
avatar.image.url(expires_in: 5.minutes)
class ActiveVault::DownloadsController < ActionController::Base
def show
head :ok, ActiveVault::Blob.locate(params[:id]).download_headers
end
end
class AvatarsController < ApplicationController
def create
# @avatar = Avatar.create \

View File

@ -0,0 +1,27 @@
require "active_vault/blob"
require "global_id"
require "active_support/core_ext/module/delegation"
# Schema: id, record_gid, blob_id, created_at
class ActiveVault::Attachment < ActiveRecord::Base
self.table_name = "active_vault_attachments"
belongs_to :blob, class_name: "ActiveVault::Blob"
delegate_missing_to :blob
def record
@record ||= GlobalID::Locator.locate(record_gid)
end
def record=(record)
@record = record
self.record_gid = record&.to_gid
end
def purge
blob.purge
destroy
record.public_send "#{name}=", nil
end
end

View File

@ -0,0 +1,30 @@
require "active_vault/attachment"
require "action_dispatch/http/upload"
module ActiveVault::Attachments
def has_file(name)
define_method(name) do
(@active_vault_attachments ||= {})[name] ||=
ActiveVault::Attachment.find_by(record_gid: to_gid.to_s, name: name)&.tap { |a| a.record = self }
end
define_method(:"#{name}=") do |attachable|
case attachable
when ActiveVault::Blob
blob = attachable
when ActionDispatch::Http::UploadedFile
blob = ActiveVault::Blob.create_after_upload! \
io: attachable.open,
filename: attachable.original_filename,
content_type: attachable.content_type
when Hash
blob = ActiveVault::Blob.create_after_upload!(attachable)
when NilClass
blob = nil
end
(@active_vault_attachments ||= {})[name] = blob ?
ActiveVault::Attachment.create!(record_gid: to_gid.to_s, name: name, blob: blob)&.tap { |a| a.record = self } : nil
end
end
end

View File

@ -1,15 +1,28 @@
class ActiveVault::CreateBlobs < ActiveRecord::Migration[5.1]
class ActiveVault::CreateTables < ActiveRecord::Migration[5.1]
def change
t.string :key
t.string :filename
t.string :content_type
t.text :metadata
create_table :active_vault_blobs do |t|
t.string :key
t.string :filename
t.string :content_type
t.text :metadata
t.integer :byte_size
t.string :checksum
t.time :created_at
t.string :checksum
t.time :created_at
t.index [ :key ], unique: true
end
create_table :active_vault_attachments do |t|
t.string :name
t.string :record_gid
t.integer :blob_id
t.time :created_at
t.index :record_gid
t.index :blob_id
t.index [ :record_gid, :name ]
t.index [ :record_gid, :blob_id ], unique: true
end
end
end

View File

@ -15,5 +15,13 @@ module ActiveVault
end
end
end
initializer "action_file.attachments" do
require "active_vault/attachments"
ActiveSupport.on_load(:active_record) do
extend ActiveVault::Attachments
end
end
end
end

27
test/attachments_test.rb Normal file
View File

@ -0,0 +1,27 @@
require "test_helper"
require "database/setup"
require "active_vault/blob"
# ActiveRecord::Base.logger = Logger.new(STDOUT)
class User < ActiveRecord::Base
has_file :avatar
end
class ActiveVault::AttachmentsTest < ActiveSupport::TestCase
setup { @user = User.create!(name: "DHH") }
test "create attachment from existing blob" do
@user.avatar = create_blob filename: "funky.jpg"
assert_equal "funky.jpg", @user.avatar.filename.to_s
end
test "purge attached blob" do
@user.avatar = create_blob filename: "funky.jpg"
avatar_key = @user.avatar.key
@user.avatar.purge
assert_nil @user.avatar
assert_not ActiveVault::Blob.site.exist?(avatar_key)
end
end

View File

@ -0,0 +1,7 @@
class ActiveVault::CreateUsers < ActiveRecord::Migration[5.1]
def change
create_table :users do |t|
t.string :name
end
end
end

View File

@ -1,4 +1,6 @@
require "active_vault/migration"
require_relative "create_users_migration"
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
ActiveVault::CreateBlobs.migrate(:up)
ActiveVault::CreateTables.migrate(:up)
ActiveVault::CreateUsers.migrate(:up)

View File

@ -17,4 +17,12 @@ class ActiveSupport::TestCase
def create_blob(data: "Hello world!", filename: "hello.txt", content_type: "text/plain")
ActiveVault::Blob.create_after_upload! io: StringIO.new(data), filename: filename, content_type: content_type
end
end
end
require "active_vault/attachments"
ActiveRecord::Base.send :extend, ActiveVault::Attachments
require "global_id"
GlobalID.app = "ActiveVaultExampleApp"
ActiveRecord::Base.send :include, GlobalID::Identification