Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
GCP Storage とcarrierwave
その先にあるのは
TGIF 2017-10-06 @zaru
@zaru
raysCI のファイルアップロードの歴史
初期
google-cloud-storage
GCP だしストレージはStorage 一択
公式のgem が google-cloud-storage
レポートファイルのアーカイブ目的だから
雑に公式gem だけでやろう
require "google/cloud/storage"
storage = Google::Cloud::Storage.new
bucket = storage.bu...
Pros
簡単に実装できて、シンプルで良い
Cons
GCP への依存
モデルに関連付けるコードを書く必要あり
画像リサイズやURL生成は自前でやる必要あり
中期
carrierwave + fog-google
画像も表示したいし、アップロード処理
も増えてきたから、ライブラリを使う
使い方は簡単。
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
end
user = User.find 1
user.avatar
#=>
<Ava...
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だ
後期
独自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
...
クラスメソッドとインスタンスメソッド
クラスメソッドを提供するmodule が必要
class Photo
include Uploader
mount_uploader :foo
end
module Uploader
def self.included base
p "incl...
面倒なので ActiveSupport::Concern 使う
module Uploader
extend ActiveSupport::Concern
included do
class << self
def mount_uploader...
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を雑に用意して carrierwave の代表的なメソッドを作る
class BaseUploader
def initialize model, field
@model = model
@field = field
end
d...
独自Classインスタンスを参照するようにする
都度生成されないようにインスタンス変数に格納
class_eval <<-EOS
def #{field}
@uploaders = {} unless defined? @uploaders
@...
ファイルアップロードの処理を独自Classに任せる
Railsだと ActionDispatch::Http::UploadedFile のはず
class_eval <<-EOS
def #{set_name} file
return unl...
今のままだと user.avatar = file とやったタイミングでアップロード
されてしまう。とても嫌な気持ち
user.save した時にアップロードしたい
after_save を使う
included do
after_save :...
アップロードフィールドを知る必要がある
クラス変数に格納してみる
module Uploader
extend ActiveSupport::Concern
included do
class << self
@@uploaders = []
...
クラス変数?
module Uploader
extend ActiveSupport::Concern
included do
class << self
@@uploaders = []
def mount_uploader field
@@upload...
Photo.class_variable_get(:@@uploaders)
#=> [:foo, :bar]
取れてます
じゃあ、別のクラスを作ってみます
class Movie
include Uploader
mount_uploader...
それぞれ、呼び出します
Photo.class_variable_get(:@@uploaders)
#=> [:hoge, :piyo]
Movie.class_variable_get(:@@uploaders)
#=> [:hoge, :...
ActiveSupport の class_attribute を使う
module Uploader
extend ActiveSupport::Concern
included do
class_attribute :uploaders
s...
Photo.uploaders
#=> [:foo, :bar]
Movie.uploaders
#=> [:hoge, :piyo]
めでたい
おわり
CarrierWaveにちょっと互換あるGCP Storage対応クラス
CarrierWaveにちょっと互換あるGCP Storage対応クラス
Upcoming SlideShare
Loading in …5
×

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

2,051 views

Published on

CarrierWaveにちょっと互換あるGCP Storage対応クラス。メタプログラミングまわりについて。

Published in: Technology
  • Be the first to comment

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

  1. 1. GCP Storage とcarrierwave その先にあるのは TGIF 2017-10-06 @zaru
  2. 2. @zaru
  3. 3. raysCI のファイルアップロードの歴史
  4. 4. 初期 google-cloud-storage
  5. 5. GCP だしストレージはStorage 一択 公式のgem が google-cloud-storage
  6. 6. レポートファイルのアーカイブ目的だから 雑に公式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"
  7. 7. Pros 簡単に実装できて、シンプルで良い Cons GCP への依存 モデルに関連付けるコードを書く必要あり 画像リサイズやURL生成は自前でやる必要あり
  8. 8. 中期 carrierwave + fog-google
  9. 9. 画像も表示したいし、アップロード処理 も増えてきたから、ライブラリを使う
  10. 10. 使い方は簡単。 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'
  11. 11. fog-google gem "fog-google" gem "google-api-client", "> 0.8.5", "< 0.9" gem "mime-types"
  12. 12. "> 0.8.5", "< 0.9"
  13. 13. 嫌な予感
  14. 14. 該当バージョン
  15. 15. 最新バージョン
  16. 16. 実際に導入してみると…
  17. 17. user = User.find 1 user.avatar.read Excon::Error::Socket: end of file reached (EOFError) ファイルをダウンロードして読み込もうとすると死。 再現したりしなかったり、不安定。
  18. 18. Pros 設定だけで実装がほぼ必要ない 画像リサイズ・URLの取得などができる Cons 古いgoogle-api-client に依存している 不安定
  19. 19. よろしい、ならば独自Classだ
  20. 20. 後期 独自Class + google-cloud-storage
  21. 21. carrierwave に戻る可能性もある 最低限の互換性を保ちたい アプリが全体的にGCPに依存している google-api-client は最新を使いたい
  22. 22. carrierwave 互換ポイント
  23. 23. class User < ActiveRecord::Base mount_uploader :avatar, AvatarUploader end mount_uploader でフィールドを指定できる user = User.find 1 user.avatar #=> <AvatarUploader> 対象フィールドを参照するとアップロードクラスが返ってくる つまり .url や .read などのメソッドを生やせる まずは、最低限ということでこの2つを満たすような形で実装を行う。
  24. 24. クラスメソッドとインスタンスメソッド
  25. 25. クラスメソッドを提供する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
  26. 26. 面倒なので ActiveSupport::Concern 使う module Uploader extend ActiveSupport::Concern included do class << self def mount_uploader field p "setting #{field}" end end end end
  27. 27. AcriveRecord のフィールド参照メソッドをオーバーライド user.avatar user.avatar = この2つのメソッドを独自Classの処理に置き換えたい。
  28. 28. def mount_uploader field set_name = "#{field}=".to_sym class_eval <<-EOS def #{field} end def #{set_name} file end EOS end class_eval を使ってオーバーライド
  29. 29. 独自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
  30. 30. 独自Classインスタンスを参照するようにする 都度生成されないようにインスタンス変数に格納 class_eval <<-EOS def #{field} @uploaders = {} unless defined? @uploaders @uploaders[__method__] ||= BaseUploader.new self, __method__ end EOS
  31. 31. ファイルアップロードの処理を独自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
  32. 32. 今のままだと user.avatar = file とやったタイミングでアップロード されてしまう。とても嫌な気持ち user.save した時にアップロードしたい after_save を使う included do after_save :file_upload_callback def file_upload_callback # ファイルアップロードする処理 end end
  33. 33. アップロードフィールドを知る必要がある クラス変数に格納してみる module Uploader extend ActiveSupport::Concern included do class << self @@uploaders = [] end end end class Photo include Uploader def self.uploaders @@uploaders end end
  34. 34. クラス変数?
  35. 35. 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
  36. 36. Photo.class_variable_get(:@@uploaders) #=> [:foo, :bar] 取れてます じゃあ、別のクラスを作ってみます class Movie include Uploader mount_uploader :hoge mount_uploader :piyo end
  37. 37. それぞれ、呼び出します Photo.class_variable_get(:@@uploaders) #=> [:hoge, :piyo] Movie.class_variable_get(:@@uploaders) #=> [:hoge, :piyo] 上書きされてる
  38. 38. 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
  39. 39. Photo.uploaders #=> [:foo, :bar] Movie.uploaders #=> [:hoge, :piyo]
  40. 40. めでたい
  41. 41. おわり

×