RubyのRSS::MakerでRSS1.0生成すると、content:encodedが自動的にhtml_escapeされてしまい、CDATAとして埋め込んでくれないので、モンキーパッチングしてみた。
rss_cdata.rb
require 'rss'
module RSS
module BaseModel
def install_cdata_element(tag_name, uri, occurs, name=nil, type=nil, disp_name=nil)
name ||= tag_name
disp_name ||= name
self::ELEMENTS << name
add_need_initialize_variable(name)
install_model(tag_name, uri, occurs, name)
def_corresponded_attr_writer name, type, disp_name
convert_attr_reader name
install_element(name) do |n, elem_name|
<<-EOC
if @#{n}
rv = "\#{indent}<#{elem_name}>"
value = "<![CDATA[" + eval("@#{n}") + "]]>"
if need_convert
rv << convert(value)
else
rv << value
end
rv << "</#{elem_name}>"
rv
else
''
end
EOC
end
end
end
module ContentModel
def self.append_features(klass)
super
klass.install_must_call_validator(CONTENT_PREFIX, CONTENT_URI)
%w(encoded).each do |name|
klass.install_cdata_element(name, CONTENT_URI, "?", "#{CONTENT_PREFIX}_#{name}")
end
end
end
class RDF
class Item; include ContentModel; end
end
end
試してみる
require 'rss'
require './rss_cdata'
rss = RSS::Maker.make("1.0") do | maker |
maker.channel.about = "http://hoge.fuga/index.rdf"
maker.channel.title = "harahoro"
maker.channel.description = "hirehare"
maker.channel.link = "http://hoge.fuga/"
item = maker.items.new_item
item.link = "http://hoge.fuga/piyo.html"
item.title = "aheaheuhiha"
item.content_encoded = "<p>aheuhihaaa</p>"
item.dc_date = Time.now()
end
puts rss.to_s
結果
<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF xmlns:image="http://web.resource.org/rss/1.0/modules/image/"
xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"
xmlns="http://purl.org/rss/1.0/">
<channel rdf:about="http://hoge.fuga/index.rdf">
<title>harahoro</title>
<link>http://hoge.fuga/</link>
<description>hirehare</description>
<items>
<rdf:Seq>
<rdf:li resource="http://hoge.fuga/piyo.html"/>
</rdf:Seq>
</items>
<taxo:topics>
<rdf:Bag/>
</taxo:topics>
</channel>
<item rdf:about="http://hoge.fuga/piyo.html">
<title>aheaheuhiha</title>
<link>http://hoge.fuga/piyo.html</link>
<content:encoded><![CDATA[<p>aheuhihaaa</p>]]></content:encoded>
<dc:date>2009-12-02T13:14:24+09:00</dc:date>
<taxo:topics>
<rdf:Bag/>
</taxo:topics>
<content:encoded><![CDATA[<p>aheuhihaaa</p>]]></content:encoded>
</item>
</rdf:RDF>
どうよ。
いろいろな処理をするファイルをプラグインとして適当なディレクトリに入れておくと、それらをロードしてオブジェクト化するやりかた。
バッチ処理とかでカスタムな方法をインプリしたいときとかに使えるかと思う。
plugin_loader.rb
class PluginLoader
attr_reader :plugins
def initialize(plugin_base, extension = 'rb')
@plugin_base = plugin_base
@plugins = []
Dir.glob(@plugin_base + "*.#{extension}").each do |p|
require p
@plugins << Object.const_get(to_classname(p)).new
end
end
private
def to_classname(str)
str.sub(/^#{@plugin_base}/){""}.sub(/\.rb$/){""}.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
end
end
使い方はこんな感じ(あらかじめpluginsディレクトリにxxxx.rbファイルを作っておく)
test.rb
require './plugin_loader'
pl = PluginLoader.new(File.dirname(__FILE__) + "/plugins/")
pl.plugins.each do | p |
p.run
end
Rubyで動的に関数定義3
で書いた方法をつかって、PerlのTest::Baseっぽいのを書いてみる。
(Ruby Test::Baseってのがすでにあるみたいだけどね。)
まずは、test_base.rb
require 'test/unit'
class TestBase < Test::Unit::TestCase
@@test_template = {}
def run(*args)
return if@method_name.to_s == "default_test"
super
end
def self.start_test
self.parse if(@@test_template.empty?)
self.generate_tests
end
private
def self.parse
test_name = ""
buf = ""
buf_mode = ""
while line = DATA.gets do
line.chomp!
if(line =~ /^(===|---)/)
if(buf != "" && test_name != "" && buf_mode != "")
@@test_template[test_name][buf_mode] = eval(buf)
buf = ""
end
if(line =~ /^=== (.*)$/)
test_name = $1
@@test_template[test_name] = {}
elsif(line =~ /--- input (.*)$/)
@@test_template[test_name][:method_name] = $1
buf_mode = :input
elsif(line = ~/--- expected/)
buf_mode= :expected
end
else
buf += line
end
end
if(buf != "" && test_name != "" && buf_mode != "")
@@test_template[test_name][buf_mode] = eval(buf)
buf = ""
end
end
def self.generate_tests
@@test_template.each do |test_name,test_args|
define_method(test_name) do
result=__send__(test_args[:method_name],test_args[:input])
assert_equal test_args[:expected], result, " error on #{test_name}"
end
end
end
end
DATAの処理のところが、ちょっと(かなり?)スパゲッティーだけど、generate_testsの所でテスト用の関数をDATA内容に合わせて作成してる。
Test::Unit::TestCaseのrunメソッドで”default_test”を無視するようにして、TestCaseがテスト未定義時に勝手にdefault_testを実行するのを防いでる。(この部分を抜くとどうなるか試せば言ってる意味がわかるはず)
実際のテストのほうは、こんな感じで書く。
require './test_base'
require './testee'
class TestBaseTest < TestBase
def setup #setupとかは通常のTestCaseと同じ
@testee = Testee.new
end
def login_test(login_name) # DATAを利用するテストはtest_以外のメソッド名
@testee.login(login_name)
@testee.who_am_i?
end
def test_without_data # DATAを使わないテストはtest_で始まるメソッド名
true
end
end
TestBaseTest.start_test #最後にstart_testでテストを生成しておく
__END__ #ここからテストに渡すパラメータ
=== test_should_login_by_hoge
--- input login_test #inputの後ろに利用するメソッド名
:hoge
--- expected
:hoge
=== test_should_login_by_fuga
--- input login_test
:fuga
--- expected
:fuga
inputの後ろのメソッド名が一個しかかけなかったり、フィルタとか便利なものがなかったり、”ruby Test::Base”よりかなりはしょってるけど、1ファイルで出来てるのでその辺はしょうがないと割り切る。
じゃあの。
javascriptによる簡易regexでは対応できないケースがでてしまったので、いろいろ参考にしつつruby版を作った。
require 'resolv'
require 'pp'
class MailAddressValidator
def self.validate(address)
return validate_by_regex(address) && validate_by_MX(address)
end
def self.validate_by_regex(address)
addr_spec = %r{^(?:(?:(?:(?:[a-zA-Z0-9_!#\$\%&'*+/=?\^`{}~|\-]+)(?:\.(?:[a-zA-Z0-9_!#\$\%&'*+/=?\^`{}~|\-]+))*)|(?:"(?:\\[^\r\n]|[^\\"])*")))\@(?:(?:(?:(?:[a-zA-Z0-9_!#\$\%&'*+/=?\^`{}~|\-]+)(?:\.(?:[a-zA-Z0-9_!#\$\%&'*+/=?\^`{}~|\-]+))*)|(?:\[(?:\\\S|[\x21-\x5a\x5e-\x7e])*\])))$}
address =~addr_spec
end
def self.validate_by_MX(address)
mxdomain = address[/[^@]+$/]
Resolv::DNS.new.getresource(mxdomain,Resolv::DNS::Resource::IN::MX) rescue nil
end
private_class_method :validate_by_regex, :validate_by_MX
end
使うときはこんな感じ
require File.dirname(__FILE__) + '/mail_address_validator'
print (MailAddressValidator.validate('hogefuga@hogefugadennnenn.naiyo')) 'ok':'ng' #=> ng
これをつかって、同期Ajaxでチェックしてる。
日本語が変だな。。。要は javascriptでxmlhttprequest.open(‘GET’,”xxxxxx”, false)という形で同期的にチェックしてる。
参考:
http://blog.livedoor.jp/dankogai/archives/51189905.html
http://www.ruby-lang.org/ja/man/html/resolv.html
追記 2009-04-13:
spellミスを直しました。id:Uchimataさんありがとうございます。
はてなブックマークがはじめてコメント欄として機能した記念すべき日です。
Railsを使わないで、rubyからActionMailerでメールを送信したくなったので、やり方のメモ。
ActionMailerは、gemでインストール済みと仮定。
Sakuraレンタルサーバ(さくら)の場合、”POP before SMTP”なので、送信前にpop認証が必要となります。
require 'rubygems'
require 'action_mailer'
require 'net/pop'
class MyMailer < ActionMailer::Base
alias_method :base_perform_delivery_smtp, :perform_delivery_smtp
@@pop3_auth_done = nil
def self.setup_for_sakura
self.delivery_method = :smtp
self.smtp_settings = {
:address => 'xxx.sakura.ne.jp',
:port => 587,
:domain => 'xxx.sakura.ne.jp',
:pop3_auth => {
:server => 'xxx.sakura.ne.jp',
:user_name => 'hoge@xxx.sakura.ne.jp',
:password => 'passw0rd',
:expires => 1.hour,
:authentication => :login
}
}
self.template_root = File.dirname(__FILE__) + "/templates"
self.perform_deliveries = true
end
def mail_contents(user,contents)
from "hoge <hoge@xxx.sakura.ne.jp>"
recipients "#{user} <#{user}>"
subject "日本語タイトルほげふが"
body :user => user, :contents => contents
end
private
def perform_delivery_smtp(mail)
do_pop_auth if !@@pop3_auth_done or (Time.now - smtp_settings[:pop3_auth][:expires]) >= @@pop3_auth_done
base_perform_delivery_smtp(mail)
end
def do_pop_auth
pop = Net::POP3.new(smtp_settings[:pop3_auth][:server])
pop.start(smtp_settings[:pop3_auth][:user_name], smtp_settings[:pop3_auth][:password])
@@pop3_auth_done = Time.now
pop.finish
end
end
で、./templates/my_mailer/mail_contents.erbに、テンプレートを適当に作成。
<%= @user %> 様
<%= @contents[:hello] %>ですねん。
ほな
これを利用する側はこんな感じ。
require 'rubygems'
require File.dirname(__FILE__) + "/my_mailer"
MyMailer.setup_for_sakura
MyMailer.deliver_mail_report('user@hogefuga.com', {:hello => "こんにちは"})
rubygemsへのパスが通ってない場合は、$LOAD_PATH.push(“パス”), ENV[‘GEM_HOME’] = “パス”、でライブラリ及びGEM_HOMEのパスを通しておきましょう。
参考: http://railsforum.com/viewtopic.php?pid=47672