aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorToshiaki Asai <toshi.alternative@gmail.com>2016-11-15 16:17:36 +0900
committerToshiaki Asai <toshi.alternative@gmail.com>2016-11-27 11:43:00 +0900
commit129323d7be734b9778607063b9a40418f5cd880b (patch)
tree4b93e808941b3ced696a30bda457d1b1124b9c54
parente3f5cabd7fde059713f07af415ec8b423ed577cc (diff)
downloadmikutter-129323d7be734b9778607063b9a40418f5cd880b.tar.gz
openimgのPhotoModelのメイン処理を分離 refs #934
openimgに依存しない独自のPhotoModelを定義できるようになった
-rw-r--r--core/lib/retriever/mixin/photo_mixin.rb148
-rw-r--r--core/plugin/openimg/model/photo.rb144
2 files changed, 161 insertions, 131 deletions
diff --git a/core/lib/retriever/mixin/photo_mixin.rb b/core/lib/retriever/mixin/photo_mixin.rb
new file mode 100644
index 00000000..3867d726
--- /dev/null
+++ b/core/lib/retriever/mixin/photo_mixin.rb
@@ -0,0 +1,148 @@
+# -*- coding: utf-8 -*-
+
+=begin rdoc
+画像リソースを扱うModelのためのmix-in。
+これをincludeすると、画像データを保存するblobフィールドが追加される。
+
+このmoduleをincludeしたクラスは、必要に応じて _download_routine_ をオーバライドする
+=end
+module Retriever::PhotoMixin
+ def self.included(klass)
+ klass.field.string :blob
+ end
+
+ # 画像をダウンロードする。
+ # partialを指定すると、ダウンロードの進捗があれば、前回呼び出されたときから
+ # ダウンロードできた内容を引数に呼び出される。
+ # 既にダウンロードが終わっていれば、 _blob_ の戻り値がそのまま渡される。
+ # このメソッドは、複数回呼び出されても画像のダウンロードを一度しか行わない。
+ # ==== Args
+ # [&partial_callback] 現在ダウンロードできたデータの一部(String)
+ # ==== Return
+ # [Delayer::Deferred::Deferredable] ダウンロードが完了したらselfを引数に呼び出される
+ def download(&partial_callback) # :yield: part
+ case @state
+ when :complete
+ partial_callback.(blob) if block_given?
+ Delayer::Deferred.new.next{ self }
+ when :download
+ append_download_queue(&partial_callback)
+ else
+ download!(&partial_callback)
+ end
+ end
+
+ # 画像のダウンロードが終わっていれば真を返す。
+ # 真を返す時、 _blob_ には完全な画像の情報が存在している
+ def completed?
+ @state == :complete
+ end
+
+ # 画像をダウロード中なら真
+ def downloading?
+ @state == :download
+ end
+
+ # ダウンロードが始まっていなければ真
+ def ready?
+ !@state
+ end
+
+ def inspect
+ if @state == :complete
+ "#<#{self.class}: #{uri} (state: #{@state}, #{self.blob.size} bytes cached)>"
+ else
+ "#<#{self.class}: #{uri} (state: #{@state})>"
+ end
+ end
+
+ private
+
+ def download!(&partial_callback)
+ atomic do
+ return download(&partial_callback) unless ready?
+ promise = initialize_download(&partial_callback)
+ Thread.new(&method(:download_routine)).next{|success|
+ if success
+ finalize_download_as_success
+ else
+ Delayer::Deferred.fail false
+ end
+ }.trap{|exception|
+ finalize_download_as_fail(exception)
+ }.terminate('error')
+ promise
+ end
+ end
+
+ def append_download_queue(&partial_callback)
+ atomic do
+ return download(&partial_callback) unless downloading?
+ register_partial_callback(partial_callback)
+ register_promise
+ end
+ end
+
+ def register_promise
+ promise = Delayer::Deferred.new(true)
+ (@promises ||= Set.new) << promise
+ promise
+ end
+
+ def register_partial_callback(cb)
+ @partials ||= Set.new
+ if cb
+ @partials << cb
+ cb.(@buffer) if !@buffer.empty?
+ end
+ end
+
+ def download_routine
+ begin
+ open(uri.to_s) do |is|
+ download_mainloop(is)
+ end
+ rescue EOFError
+ true
+ end
+ end
+
+ # _input_stream_ から、画像をダウンロードし、 _@buffer_ に格納する。
+ # このインスタンスの _download_ メソッドが既に呼ばれていて、ブロックが渡されている場合、
+ # そのブロックに一定の間隔でダウンロードしたデータを渡す。
+ # PhotoMixinをincludeしたクラスでオーバライドされた _download_routine_ から呼ばれることを想定している。
+ # ==== Args
+ # [input_stream] 画像データがどんどん出てくる IO のインスタンス
+ def download_mainloop(input_stream)
+ loop do
+ Thread.pass
+ partial = input_stream.readpartial(1024**2).freeze
+ @buffer << partial
+ atomic{ @partials.each{|c|c.(partial)} }
+ end
+ end
+
+ def initialize_download(&partial_callback)
+ @state = :download
+ @buffer = String.new
+ register_partial_callback(partial_callback)
+ register_promise
+ end
+
+ def finalize_download_as_success
+ atomic do
+ self.blob = @buffer.freeze
+ @state = :complete
+ @promises.each{|p| p.call(self) }
+ @buffer = @promises = @partials = nil
+ end
+ end
+
+ def finalize_download_as_fail(exception)
+ atomic do
+ @state = nil
+ @promises.each{|p| p.fail(exception) }
+ @buffer = @promises = @partials = nil
+ end
+ end
+end
diff --git a/core/plugin/openimg/model/photo.rb b/core/plugin/openimg/model/photo.rb
index b0e61e3c..25d185c9 100644
--- a/core/plugin/openimg/model/photo.rb
+++ b/core/plugin/openimg/model/photo.rb
@@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
+miquire :lib, 'retriever/mixin/photo_mixin'
module Plugin::Openimg
class Photo < Retriever::Model
+ include Retriever::PhotoMixin
register :openimg_photo, name: Plugin[:openimg]._('画像ビューア')
field.uri :perma_link
- field.string :blob
handle ->uri{
uri_str = uri.to_s
@@ -15,139 +16,20 @@ module Plugin::Openimg
new(perma_link: uri)
end
- # 画像をダウンロードする。
- # partialを指定すると、ダウンロードの進捗があれば、前回呼び出されたときから
- # ダウンロードできた内容を引数に呼び出される。
- # 既にダウンロードが終わっていれば、 _blob_ の戻り値がそのまま渡される。
- # このメソッドは、複数回呼び出されても画像のダウンロードを一度しか行わない。
- # ==== Args
- # [&partial_callback] 現在ダウンロードできたデータの一部(String)
- # ==== Return
- # [Delayer::Deferred::Deferredable] ダウンロードが完了したらselfを引数に呼び出される
- def download(&partial_callback) # :yield: part
- case @state
- when :complete
- partial_callback.(blob) if block_given?
- Delayer::Deferred.new.next{ self }
- when :download
- append_download_queue(&partial_callback)
- else
- download!(&partial_callback)
- end
- end
-
- # 画像のダウンロードが終わっていれば真を返す。
- # 真を返す時、 _blob_ には完全な画像の情報が存在している
- def completed?
- @state == :complete
- end
-
- # 画像をダウロード中なら真
- def downloading?
- @state == :download
- end
-
- # ダウンロードが始まっていなければ真
- def ready?
- !@state
- end
-
- def inspect
- if @state == :complete
- "#<#{self.class}: #{perma_link} (state: #{@state}, #{self.blob.size} bytes cached)>"
- else
- "#<#{self.class}: #{perma_link} (state: #{@state})>"
- end
- end
-
private
- def download!(&partial_callback)
- atomic do
- return download(&partial_callback) unless ready?
- promise = initialize_download(&partial_callback)
- Thread.new(&gen_download_routine).next{|success|
- if success
- finalize_download_as_success
- else
- Delayer::Deferred.fail false
- end
- }.trap{|exception|
- finalize_download_as_fail(exception)
- }.terminate('error')
- promise
- end
- end
-
- def append_download_queue(&partial_callback)
- atomic do
- return download(&partial_callback) unless downloading?
- register_partial_callback(partial_callback)
- register_promise
- end
- end
-
- def register_promise
- promise = Delayer::Deferred.new(true)
- (@promises ||= Set.new) << promise
- promise
- end
-
- def register_partial_callback(cb)
- @partials ||= Set.new
- if cb
- @partials << cb
- cb.(@buffer) if !@buffer.empty?
- end
- end
-
- def gen_download_routine
- -> do
- begin
- _, raw = Plugin.filtering(:openimg_raw_image_from_display_url, perma_link.to_s, nil)
- if raw
- download_mainloop(raw)
- else
- raise "couldn't resolve actual image url of #{perma_link}."
- end
- rescue EOFError
- true
- ensure
- raw.close rescue nil
+ def download_routine
+ begin
+ _, raw = Plugin.filtering(:openimg_raw_image_from_display_url, perma_link.to_s, nil)
+ if raw
+ download_mainloop(raw)
+ else
+ raise "couldn't resolve actual image url of #{perma_link}."
end
- end
- end
-
- def download_mainloop(raw)
- loop do
- Thread.pass
- partial = raw.readpartial(1024**2).freeze
- @buffer << partial
- atomic{ @partials.each{|c|c.(partial)} }
- end
- end
-
- def initialize_download(&partial_callback)
- @state = :download
- @buffer = String.new
- register_partial_callback(partial_callback)
- register_promise
- end
-
- def finalize_download_as_success
- atomic do
- self.blob = @buffer.freeze
- @state = :complete
- @promises.each{|p| p.call(self) }
- @buffer = @promises = @partials = nil
- end
- end
-
- def finalize_download_as_fail(exception)
- atomic do
- @state = nil
- @promises.each{|p| p.fail(exception) }
- @buffer = @promises = @partials = nil
+ rescue EOFError
+ true
+ ensure
+ raw.close rescue nil
end
end
end