43
GCP Storage carrierwave TGIF 2017-10-06 @zaru

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

Embed Size (px)

Citation preview

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

GCP Storage と carrierwave

その先にあるのは

TGIF 2017-10-06 @zaru

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

@zaru

Page 3: CarrierWaveにちょっと互換あるGCP Storage対応クラス
Page 4: CarrierWaveにちょっと互換あるGCP Storage対応クラス
Page 5: CarrierWaveにちょっと互換あるGCP Storage対応クラス

raysCI のファイルアップロードの歴史

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

初期

google-cloud-storage

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

GCP だしストレージは Storage 一択

公式の gem が google-cloud-storage

Page 8: CarrierWaveにちょっと互換あるGCP 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"

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

Pros簡単に実装できて、シンプルで良い

ConsGCP への依存

モデルに関連付けるコードを書く必要あり

画像リサイズやURL生成は自前でやる必要あり

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

中期

carrierwave + fog-google

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

画像も表示したいし、アップロード処理も増えてきたから、ライブラリを使う

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

使い方は簡単。

class User < ActiveRecord::Base mount_uploader :avatar, AvatarUploaderend

user = User.find 1user.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'

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

fog-googlegem "fog-google"gem "google-api-client", "> 0.8.5", "< 0.9"gem "mime-types"

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

"> 0.8.5", "< 0.9"

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

嫌な予感

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

該当バージョン

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

最新バージョン

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

実際に導入してみると…

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

user = User.find 1user.avatar.read

Excon::Error::Socket: end of filereached (EOFError)

ファイルをダウンロードして読み込もうとすると死。

再現したりしなかったり、不安定。

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

Pros設定だけで実装がほぼ必要ない

画像リサイズ・URLの取得などができる

Cons古い google-api-client に依存している

不安定

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

よろしい、ならば独自Classだ

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

後期

独自Class + google-cloud-storage

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

carrierwave に戻る可能性もある

最低限の互換性を保ちたい

アプリが全体的にGCPに依存している

google-api-client は最新を使いたい

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

carrierwave 互換ポイント

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

class User < ActiveRecord::Base mount_uploader :avatar, AvatarUploaderend

mount_uploader でフィールドを指定できる

user = User.find 1user.avatar#=> <AvatarUploader>

対象フィールドを参照するとアップロードクラスが返ってくる

つまり .url や .read などのメソッドを生やせる

まずは、最低限ということでこの2つを満たすような形で実装を行う。

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

クラスメソッドとインスタンスメソッド

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

クラスメソッドを提供する module が必要

class Photo include Uploader mount_uploader :fooend

module Uploader def self.included base p "included by #{base}"

base.extend ClassMethods end

module ClassMethods def mount_uploader field p "setting #{field}" end endend

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

面倒なので ActiveSupport::Concern 使う

module Uploader extend ActiveSupport::Concern included do class << self def mount_uploader field p "setting #{field}" end end endend

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

AcriveRecord のフィールド参照メソッドをオーバーライド

user.avataruser.avatar =

この2つのメソッドを独自Classの処理に置き換えたい。

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

def mount_uploader field

set_name = "#{field}=".to_sym

class_eval <<-EOS def #{field} end

def #{set_name} file end EOSend

class_eval を使ってオーバーライド

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

独自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

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

独自Classインスタンスを参照するようにする

都度生成されないようにインスタンス変数に格納

class_eval <<-EOS

def #{field} @uploaders = {} unless defined? @uploaders @uploaders[__method__] ||= BaseUploader.new self, __method__end

EOS

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

ファイルアップロードの処理を独自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_filenameend

EOS

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

今のままだと user.avatar = file とやったタイミングでアップロードされてしまう。とても嫌な気持ち

user.save した時にアップロードしたい

after_save を使う

included do after_save :file_upload_callback

def file_upload_callback # ファイルアップロードする処理 end

end

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

アップロードフィールドを知る必要がある

クラス変数に格納してみる

module Uploader extend ActiveSupport::Concern

included do class << self @@uploaders = [] end endend

class Photo include Uploader def self.uploaders @@uploaders endend

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

クラス変数?

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

module Uploader extend ActiveSupport::Concern

included do class << self @@uploaders = [] def mount_uploader field @@uploaders << field end end endend

class Photo include Uploader mount_uploader :foo mount_uploader :barend

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

Photo.class_variable_get(:@@uploaders)#=> [:foo, :bar]

取れてます

じゃあ、別のクラスを作ってみます

class Movie include Uploader mount_uploader :hoge mount_uploader :piyoend

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

それぞれ、呼び出します

Photo.class_variable_get(:@@uploaders)#=> [:hoge, :piyo]

Movie.class_variable_get(:@@uploaders)#=> [:hoge, :piyo]

上書きされてる

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

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 endend

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

Photo.uploaders#=> [:foo, :bar]

Movie.uploaders#=> [:hoge, :piyo]

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

めでたい

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

おわり