tDiaryのプラグインアーキテクチャを読む
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
#contents
*はじめに [#lda23308]
今回はtDiaryのプラグインの仕組みを読んでみたいと思います...
なお、今回読んだtDiaryのバージョンは2.1.4です。
* index.rb [#hc454ba3]
何はなくともindex.rbを見てみましょう。早速興味深いコード...
#pre{{
if FileTest::symlink?( __FILE__ ) then
org_path = File::dirname( File::readlink( __FILE__ ) )
else
org_path = File::dirname( __FILE__ )
end
$:.unshift( org_path.untaint )
require 'tdiary'
}}
$:はファイルのロードパスです。そこにindex.rb((複数の日記...
次にTDiary::Configオブジェクトを作ることでtdiary.confのロ...
#pre{{
instance_variables.each do |v|
v.sub!( /@/, '' )
instance_eval( <<-SRC
def #{v}
@#{v}
end
def #{v}=(p)
@#{v} = p
end
SRC
)
end
}}
インスタンス変数からgetterメソッドとsetterメソッドを動的...
その後、渡された引数に応じてTDiary::TDiaryBaseから派生し...
*TDiary::Plugin [#e66cb375]
さて、eval_rhtmメソッドl(実体はprotectedなdo_eval_rhtmlメ...
#pre{{
Dir::glob( "#{plugin_path}/*.rb" ).sort.each do |file|
plugin_file = file
load_plugin( file )
@plugin_files << plugin_file
end
}}
pluginディレクトリは00default.rb, 05referer.rb, 10spamfil...
個々のプラグインは次のコードで読み込まれています。
#pre{{
def load_plugin( file )
@resource_loaded = false
begin
res_file = File::dirname( file ) + "/#{@conf.lang}/" + ...
open( res_file.untaint ) do |src|
instance_eval( src.read.untaint, "(plugin/#{@conf.lang...
end
@resource_loaded = true
rescue IOError, Errno::ENOENT
end
File::open( file.untaint ) do |src|
instance_eval( src.read.untaint, "(plugin/#{File::basen...
end
end
}}
興味深いところは2つあります。言語名ディレクトリにあるプラ...
#pre{{
module TDiary
class Plugin
def foo
...
end
end
end
}}
としなくても
#pre{{
def foo
...
end
}}
とすればよいことになります。プラグインの仕掛けを知らなく...
*50sp.rb [#v07a2e0f]
さて、上のコードではtdiaryディレクトリ直下のpluginディレ...
#pre{{
# Finally, we can eval the selected plugins as tdiary.rb ...
if sp_option( 'selected' ) then
sp_option( 'selected' ).untaint.split( /?n/ ).collect{ |...
@sp_path.each do |dir|
path = "#{dir}/#{filename}"
if File.readable?( path ) then
begin
load_plugin( path )
@plugin_files << path
rescue Exception
raise PluginError::new( "Plugin error in '#{path}'.?...
end
break
end
end
end
end
}}
@data_path/tdiary.confを見るとoptions2配列にsp.selectedと...
ところで、いつ@data_path/tdiary.confを読んでいるんだろう...
#pre{{
load_cgi_conf
}}
という行があります。これにより、カレントディレクトリのtdi...
*diary.rhtml [#s9447463]
それではdo_eval_rhtmlメソッドに戻りましょう。do_eval_html...
#pre{{
r = ERB::new( rhtml.untaint ).result( binding )
r = ERB::new( r ).src
}}
としています。何故2回ERBにかけているのでしょう?サンプル...
#pre{{
<% latest( @conf.latest_limit ) do |diary| %>
<%= diary.eval_rhtml( param, PATH ) %>
<hr class="sep">
<% end %>
}}
またeval_rhtmlメソッドが呼ばれています。メソッド名が同じ...
さて、DiaryBaseモジュールをincludeしているのは誰なのでし...
さてと、DiaryBase.eval_rhtmlメソッドを見てみましょう、す...
#pre{{
<%%= body_enter_proc( Time::at( <%=@date.to_i%> ) ) %>
<%= to_html( opt ) %>
<%%= body_leave_proc( Time::at( <%=@date.to_i%> ) ) %>
}}
to_htmlで日記本文が出力されそうです。body_enter_procとbod...
それでは<%%=の謎解きをしましょう。erb.rbを参照すると以下...
#pre{{
<%% or %%> -- replace with <% or %> respectively
}}
つまり、ERBを一回かけると
#pre{{
<%= body_enter_proc( Time::at( <%=@date.to_i%> ) ) %>
日記本文
<%=foo%>
<%= body_leave_proc( Time::at( <%=@date.to_i%> ) ) %>
}}
となるようです(本文中にfooプラグインを呼び出していたとし...
#pre{{
class ERB
def initialize(str, safe_level=nil, trim_mode=nil, eout...
@safe_level = safe_level
compiler = ERB::Compiler.new(trim_mode)
set_eoutvar(compiler, eoutvar)
@src = compiler.compile(str)
@filename = nil
end
end
}}
という処理が行われるため、srcメソッドの返値は・・・
#pre{{
body_enter_proc( Time::at( <%=@date.to_i%> ) )
日記本文
foo
body_leave_proc( Time::at( <%=@date.to_i%> ) )
}}
となります。
*TDiary::Plugin再び [#d141e19f]
さて、do_eval_rhtmlメソッドに戻ります。ERBを二度がけした...
#pre{{
# apply plugins
r = @plugin.eval_src( r.untaint, @conf.secure ) if @plugin
}}
という処理をしています。
Plugin.eval_srcでは(セキュリティのためのコードを除くと)単...
#pre{{
eval( src, binding, "(TDiary::Plugin#eval_src)", 1 )
}}
プラグインの読み込みの段階でPluginクラスにfooメソッドが定...
#pre{{
def body_enter_proc( date )
r = []
@body_enter_procs.each do |proc|
r << proc.call( date )
end
r.join
end
}}
と登録されているprocを順次呼び出すことで本文の前に出力し...
#pre{{
add_body_enter_proc do |date| weather( date ) end
}}
というprocを追加しています。
ところで、"日記本文"をevalしたらそんなメソッドなんてあり...
*おわりに [#v570af54]
今回はtDiaryのソースをプラグインの実現という部分に注目し...
-Pluginクラス内でプラグインファイルをinstance_evalするこ...
-コールバック系プラグインを<%%= xxx_proc %>とrhtmlに書き...
率直な感想として、「たださんすげぇ!」の一言です。
それではみなさんもよいコードリーディングを。
終了行:
#contents
*はじめに [#lda23308]
今回はtDiaryのプラグインの仕組みを読んでみたいと思います...
なお、今回読んだtDiaryのバージョンは2.1.4です。
* index.rb [#hc454ba3]
何はなくともindex.rbを見てみましょう。早速興味深いコード...
#pre{{
if FileTest::symlink?( __FILE__ ) then
org_path = File::dirname( File::readlink( __FILE__ ) )
else
org_path = File::dirname( __FILE__ )
end
$:.unshift( org_path.untaint )
require 'tdiary'
}}
$:はファイルのロードパスです。そこにindex.rb((複数の日記...
次にTDiary::Configオブジェクトを作ることでtdiary.confのロ...
#pre{{
instance_variables.each do |v|
v.sub!( /@/, '' )
instance_eval( <<-SRC
def #{v}
@#{v}
end
def #{v}=(p)
@#{v} = p
end
SRC
)
end
}}
インスタンス変数からgetterメソッドとsetterメソッドを動的...
その後、渡された引数に応じてTDiary::TDiaryBaseから派生し...
*TDiary::Plugin [#e66cb375]
さて、eval_rhtmメソッドl(実体はprotectedなdo_eval_rhtmlメ...
#pre{{
Dir::glob( "#{plugin_path}/*.rb" ).sort.each do |file|
plugin_file = file
load_plugin( file )
@plugin_files << plugin_file
end
}}
pluginディレクトリは00default.rb, 05referer.rb, 10spamfil...
個々のプラグインは次のコードで読み込まれています。
#pre{{
def load_plugin( file )
@resource_loaded = false
begin
res_file = File::dirname( file ) + "/#{@conf.lang}/" + ...
open( res_file.untaint ) do |src|
instance_eval( src.read.untaint, "(plugin/#{@conf.lang...
end
@resource_loaded = true
rescue IOError, Errno::ENOENT
end
File::open( file.untaint ) do |src|
instance_eval( src.read.untaint, "(plugin/#{File::basen...
end
end
}}
興味深いところは2つあります。言語名ディレクトリにあるプラ...
#pre{{
module TDiary
class Plugin
def foo
...
end
end
end
}}
としなくても
#pre{{
def foo
...
end
}}
とすればよいことになります。プラグインの仕掛けを知らなく...
*50sp.rb [#v07a2e0f]
さて、上のコードではtdiaryディレクトリ直下のpluginディレ...
#pre{{
# Finally, we can eval the selected plugins as tdiary.rb ...
if sp_option( 'selected' ) then
sp_option( 'selected' ).untaint.split( /?n/ ).collect{ |...
@sp_path.each do |dir|
path = "#{dir}/#{filename}"
if File.readable?( path ) then
begin
load_plugin( path )
@plugin_files << path
rescue Exception
raise PluginError::new( "Plugin error in '#{path}'.?...
end
break
end
end
end
end
}}
@data_path/tdiary.confを見るとoptions2配列にsp.selectedと...
ところで、いつ@data_path/tdiary.confを読んでいるんだろう...
#pre{{
load_cgi_conf
}}
という行があります。これにより、カレントディレクトリのtdi...
*diary.rhtml [#s9447463]
それではdo_eval_rhtmlメソッドに戻りましょう。do_eval_html...
#pre{{
r = ERB::new( rhtml.untaint ).result( binding )
r = ERB::new( r ).src
}}
としています。何故2回ERBにかけているのでしょう?サンプル...
#pre{{
<% latest( @conf.latest_limit ) do |diary| %>
<%= diary.eval_rhtml( param, PATH ) %>
<hr class="sep">
<% end %>
}}
またeval_rhtmlメソッドが呼ばれています。メソッド名が同じ...
さて、DiaryBaseモジュールをincludeしているのは誰なのでし...
さてと、DiaryBase.eval_rhtmlメソッドを見てみましょう、す...
#pre{{
<%%= body_enter_proc( Time::at( <%=@date.to_i%> ) ) %>
<%= to_html( opt ) %>
<%%= body_leave_proc( Time::at( <%=@date.to_i%> ) ) %>
}}
to_htmlで日記本文が出力されそうです。body_enter_procとbod...
それでは<%%=の謎解きをしましょう。erb.rbを参照すると以下...
#pre{{
<%% or %%> -- replace with <% or %> respectively
}}
つまり、ERBを一回かけると
#pre{{
<%= body_enter_proc( Time::at( <%=@date.to_i%> ) ) %>
日記本文
<%=foo%>
<%= body_leave_proc( Time::at( <%=@date.to_i%> ) ) %>
}}
となるようです(本文中にfooプラグインを呼び出していたとし...
#pre{{
class ERB
def initialize(str, safe_level=nil, trim_mode=nil, eout...
@safe_level = safe_level
compiler = ERB::Compiler.new(trim_mode)
set_eoutvar(compiler, eoutvar)
@src = compiler.compile(str)
@filename = nil
end
end
}}
という処理が行われるため、srcメソッドの返値は・・・
#pre{{
body_enter_proc( Time::at( <%=@date.to_i%> ) )
日記本文
foo
body_leave_proc( Time::at( <%=@date.to_i%> ) )
}}
となります。
*TDiary::Plugin再び [#d141e19f]
さて、do_eval_rhtmlメソッドに戻ります。ERBを二度がけした...
#pre{{
# apply plugins
r = @plugin.eval_src( r.untaint, @conf.secure ) if @plugin
}}
という処理をしています。
Plugin.eval_srcでは(セキュリティのためのコードを除くと)単...
#pre{{
eval( src, binding, "(TDiary::Plugin#eval_src)", 1 )
}}
プラグインの読み込みの段階でPluginクラスにfooメソッドが定...
#pre{{
def body_enter_proc( date )
r = []
@body_enter_procs.each do |proc|
r << proc.call( date )
end
r.join
end
}}
と登録されているprocを順次呼び出すことで本文の前に出力し...
#pre{{
add_body_enter_proc do |date| weather( date ) end
}}
というprocを追加しています。
ところで、"日記本文"をevalしたらそんなメソッドなんてあり...
*おわりに [#v570af54]
今回はtDiaryのソースをプラグインの実現という部分に注目し...
-Pluginクラス内でプラグインファイルをinstance_evalするこ...
-コールバック系プラグインを<%%= xxx_proc %>とrhtmlに書き...
率直な感想として、「たださんすげぇ!」の一言です。
それではみなさんもよいコードリーディングを。
ページ名: