Hpricot で一部の要素のみ通すフィルタ
require "rubygems"
require "hpricot"
def html_filter(parent, opts, &block)
block = Proc.new {|x| x} unless block
def escape(str)
_escape = { '<' => '<', '>' => '>', '"' => '"' }
str.gsub(/#{_escape.keys.join("|")}/) {|m| _escape[m] }
end
ret = ""
parent.each_child do |c|
if c.elem?
empty = Hpricot::ElementContent[c.name] == :EMPTY
if opts.key?(c.name)
ret << "<#{c.name}"
ret << c.attributes.select {|k,v| opts[c.name].include? k}.map {|k,v| %| #{k}="#{escape v}"| }.join
ret << "#{empty ? " /" : ""}>"
ret << html_filter(c, opts, &block)
ret << "</#{c.name}>" unless empty
else
ret << "<#{c.name}"
ret << c.attributes.map {|k,v| %| #{k}="#{escape v}"| }.join
ret << "#{empty ? " /" : ""}>"
ret << html_filter(c, opts, &block)
ret << "</#{c.name}>" unless empty
end
else
ret << block.call(c.to_s)
end
end
ret
end
doc = Hpricot(inputs)
puts doc
puts html_filter(doc, opts) { |text|
text.gsub(/¥n/, "<br />¥n")
}こんなかんじかなぁ。ちょっときもいけど……
関係ないけど Mac でバックスラッシュをコピーして Fx にペーストすると円マークになってこまる。そのままペーストしてるけど、あとからコピペしようとすると使えないんだよね……
Ruby 1.9 のイテレータ (ちょーすごい)
で、そんなことより Mochkit.Iter の Synopsis の例を Ruby でやろうとおもったらいろいろ足りない感じ
(Enumerator は http://subtech.g.hatena.ne.jp/secondlife/20071104/1194144583 でちゃんと紹介されているよ! )
theSum = sum(takewhile(
partial(operator.gt, 10),
imap(
partial(operator.mul, 2),
count()
)
)
));
assert( theSum == (0 + 0 + 2 + 4 + 6 + 8) );これこれ。
class Numeric
def countup
(self..(1.0/0)).to_enum
end
end
module Enumerable
def imap(&mapfun)
Proc.new {|&block|
self.each do |i|
block.call mapfun.call(i)
end
}.to_enum(:call)
end
end
# (sum はぬかしてる)
p 0.countup.imap {|x| x * 2}.take_while {|x| x < 10}imap はなんか適当に書いてみたらこうなってしまったんだけどもっと簡単にかけたりするのかな…… 演算子の partial は定義しないといけないのと、ブロック書いたほうがはやいのでとりあえずとばし
countup はもっと簡単に書けないかとおもったけど微妙にめんどいっぽい。id:secondlife さんにもきいた。
# Float になっちゃう
p 0.to_enum(:step, 1.0/0).take(10)
# みじかいけど 1.0/0 (Infinity) がわかりにくい
p (0..(1.0/0)).to_enum.take(10)
# ながいよ
p lambda {|x,&block| loop { block.call(x); x += 1 } }.to_enum(:call, 0).take(10)
# Float::MAX が無限っぽくない
p 0.to_enum(:upto, Float::MAX).take(10)
ウェブサービスの XHTML 化
ユーザの入力をどうするか
- HTML を書けない記法しか許さない
- HTML を書けるがフィルタでとにかく well-formed にする
hpricot つかえば簡単に well-formed で XHTML にできてしまうなぁ。しかもはやい……
Hpricot(html, :xhtml_strict => true)
一部のHTML要素のみ通すフィルタ
ちがうちがうおれはなにをやっているんだ……
require "strscan"
# HTMLFilter
# 許した要素のみ残しながら、タグの対応を補完する
class HTMLFilter
ESCAPE = { '<' => '<', '>' => '>', '"' => '"' }
EMPTY_ELEMENTS = ["br", "hr", "img"]
def initialize(allow, callback=proc {|x| x})
@allow = allow
@callback = callback
end
def filter(input)
ret = ""
pool = ""
s = StringScanner.new(input)
elements = []
until s.eos?
if s.scan(%r{<(/)?(#{@allow.keys.join("|")})}i)
ret << @callback[escape(pool)]
pool = ""
name = s[2]
if s[1]
# end tag
s.scan(%r{¥s*>})
while opened_but_close = elements.pop
ret << "</#{opened_but_close}>"
break if name == opened_but_close
end
# remove the end tag if it was not opened.
else
# start tag
attrs = []
while s.scan(%r{¥s+([a-z-]+)=(?:"([^"]*)"|'([^']*)')}i)
an = s[1]
av = s[2] || s[3]
attrs << "#{an}='#{escape(av)}'" if @allow[name].include?(an)
end
attrs = attrs.empty?? "" : " #{attrs.join(" ")}"
empty = false
if s.scan(%r{¥s*(/)?>})
case
when EMPTY_ELEMENTS.include?(name)
empty = true
ret << "<#{name}#{attrs} />"
when s[1]
empty = true
ret << "<#{name}#{attrs}></#{name}>"
else
ret << "<#{name}#{attrs}>"
end
else
# invalid but continue
ret << "<#{name}#{attrs}>"
end
elements.push(name) unless empty
end
else
pool << s.getch
end
end
ret << @callback[escape(pool)]
ret << "</#{opened_but_close}>" while (opened_but_close = elements.pop)
ret
end
def escape(str)
str.gsub(/#{ESCAPE.keys.join("|")}/) {|m|
ESCAPE[m]
}
end
end
opts = {
"a" => ["href", "name"],
"strong" => [],
"br" => [],
"p" => [],
"ins" => ["datetime"],
"del" => ["datetime"],
}
inputs = DATA.read
out = HTMLFilter.new(opts, proc {|str|
str.gsub(/¥n/, "<br />¥n")
}).filter(inputs)
puts out
__END__
<script foo="<script>alert('bar')</script>">alert('foo')</script>
<script foo="<a href='link'>link</a>">alert('foo')</script>
<a href='www.g>oogle.com'>link</a>
<a href="hoge" name="aaa">hoge<strong style="">ttt</strong></a>
<a href="hoge" name="aa
{a">hoge<br><br/></a>
<ins datetime="">
<p>
aaa
</p>
<p>aaa
</ins>
<a></strong>
<p>aaa
どう書く?org のお題 をやっていたんだった。なんかだんだんズレてきたので投稿がためらわれる。改行を br にしろというお題 に対応ずみ。
Firefox 3.0 beta1
いれた!
ロケーションバーは重いままだなぁ。ヒストリがでかすぎるのかなぁ