狠狠撸

狠狠撸Share a Scribd company logo
GCP Storage とcarrierwave
その先にあるのは
TGIF 2017-10-06 @zaru
@zaru
CarrierWaveにちょっと互換あるGCP Storage対応クラス
CarrierWaveにちょっと互換あるGCP Storage対応クラス
raysCI のファイルアップロードの歴史
初期
google-cloud-storage
GCP だしストレージはStorage 一択
公式のgem が google-cloud-storage
レポートファイルのアーカイブ目的だから
雑に公式gem だけでやろう
require "google/cloud/storage"
storage = Google::Cloud::Storage.new
bucket = storage.bucket "my-bucket"
bucket.create_file "path/to/local.file.ext",
"destination/path/file.ext"
Pros
簡単に実装できて、シンプルで良い
Cons
GCP への依存
モデルに関連付けるコードを書く必要あり
画像リサイズやURL生成は自前でやる必要あり
中期
carrierwave + fog-google
画像も表示したいし、アップロード処理
も増えてきたから、ライブラリを使う
使い方は簡単。
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
end
user = User.find 1
user.avatar
#=>
<AvatarUploader:0x0055883db34618
@model=#<User id: nil, name: nil, avatar: nil, created_at: nil, updated_at: nil>,
@mounted_as=:avatar>
user.avatar.url
#=> '/url/to/file.png'
fog-google
gem "fog-google"
gem "google-api-client", "> 0.8.5", "< 0.9"
gem "mime-types"
"> 0.8.5", "< 0.9"
嫌な予感
该当バージョン
最新バージョン
実际に导入してみると…
user = User.find 1
user.avatar.read
Excon::Error::Socket: end of file
reached (EOFError)
ファイルをダウンロードして読み込もうとすると死。
再現したりしなかったり、不安定。
Pros
設定だけで実装がほぼ必要ない
画像リサイズ?URLの取得などができる
Cons
古いgoogle-api-client に依存している
不安定
よろしい、ならば独自颁濒补蝉蝉だ
後期
独自Class + google-cloud-storage
carrierwave に戻る可能性もある
最低限の互換性を保ちたい
アプリが全体的にGCPに依存している
google-api-client は最新を使いたい
carrierwave 互換ポイント
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
end
mount_uploader でフィールドを指定できる
user = User.find 1
user.avatar
#=> <AvatarUploader>
対象フィールドを参照するとアップロードクラスが返ってくる
つまり .url や .read などのメソッドを生やせる
まずは、最低限ということでこの2つを満たすような形で実装を行う。
クラスメソッドとインスタンスメソッド
クラスメソッドを提供するmodule が必要
class Photo
include Uploader
mount_uploader :foo
end
module Uploader
def self.included base
p "included by #{base}"
base.extend ClassMethods
end
module ClassMethods
def mount_uploader field
p "setting #{field}"
end
end
end
面倒なので ActiveSupport::Concern 使う
module Uploader
extend ActiveSupport::Concern
included do
class << self
def mount_uploader field
p "setting #{field}"
end
end
end
end
AcriveRecord のフィールド参照メソッドをオーバーライド
user.avatar
user.avatar =
この2つのメソッドを独自Classの処理に置き換えたい。
def mount_uploader field
set_name = "#{field}=".to_sym
class_eval <<-EOS
def #{field}
end
def #{set_name} file
end
EOS
end
class_eval を使ってオーバーライド
独自Classを雑に用意して carrierwave の代表的なメソッドを作る
class BaseUploader
def initialize model, field
@model = model
@field = field
end
def exists?
end
def read
end
def download local_path
end
def path
end
def url
独自Classインスタンスを参照するようにする
都度生成されないようにインスタンス変数に格納
class_eval <<-EOS
def #{field}
@uploaders = {} unless defined? @uploaders
@uploaders[__method__] ||= BaseUploader.new self, __method__
end
EOS
ファイルアップロードの処理を独自Classに任せる
Railsだと ActionDispatch::Http::UploadedFile のはず
class_eval <<-EOS
def #{set_name} file
return unless file.is_a? ActionDispatch::Http::UploadedFile
uploader_name = __method__.to_s.gsub "=", ""
send(uploader_name).set_file file.path, file.original_filename
end
EOS
今のままだと user.avatar = file とやったタイミングでアップロード
されてしまう。とても嫌な気持ち
user.save した時にアップロードしたい
after_save を使う
included do
after_save :file_upload_callback
def file_upload_callback
# ファイルアップロードする処理
end
end
アップロードフィールドを知る必要がある
クラス変数に格納してみる
module Uploader
extend ActiveSupport::Concern
included do
class << self
@@uploaders = []
end
end
end
class Photo
include Uploader
def self.uploaders
@@uploaders
end
end
クラス変数?
module Uploader
extend ActiveSupport::Concern
included do
class << self
@@uploaders = []
def mount_uploader field
@@uploaders << field
end
end
end
end
class Photo
include Uploader
mount_uploader :foo
mount_uploader :bar
end
Photo.class_variable_get(:@@uploaders)
#=> [:foo, :bar]
取れてます
じゃあ、別のクラスを作ってみます
class Movie
include Uploader
mount_uploader :hoge
mount_uploader :piyo
end
それぞれ、呼び出します
Photo.class_variable_get(:@@uploaders)
#=> [:hoge, :piyo]
Movie.class_variable_get(:@@uploaders)
#=> [:hoge, :piyo]
上書きされてる
ActiveSupport の class_attribute を使う
module Uploader
extend ActiveSupport::Concern
included do
class_attribute :uploaders
self.uploaders = []
class << self
def mount_uploader field
uploaders << field
end
end
end
end
Photo.uploaders
#=> [:foo, :bar]
Movie.uploaders
#=> [:hoge, :piyo]
めでたい
おわり

More Related Content

CarrierWaveにちょっと互換あるGCP Storage対応クラス