Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ファイル検索用のインタプリタを作成する過程から、 interpreter パターンを学ぶ
- ** すべてのファイルを検索する/名前でファイルを検索する/大きなファイル/書き込み可能ファイル
- require 'find'
- class Expression
- end
- # すべてのファイルを検索する
- class All < Expression
- def evaluate(dir)
- results= []
- Find.find(dir) do |p|
- next unless File.file?(p)
- results << p
- end
- results
- end
- end
- expr_all = All.new
- files = expr_all.evaluate('test_dir')
- files # => ["test_dir/small2.txt", "test_dir/subdir/small.jpg", "test_dir/subdir/other.mp3", "test_dir/small1.txt", "test_dir/big.mp3", "test_dir/small.mp3", "test_dir/big2.mp3"]
- # 名前でファイルを検索する
- class FileName < Expression
- def initialize(pattern)
- @pattern = pattern
- end
- def evaluate(dir)
- results= []
- Find.find(dir) do |p|
- next unless File.file?(p)
- name = File.basename(p)
- results << p if File.fnmatch(@pattern, name)
- end
- results
- end
- end
- expr_mp3 = FileName.new('*.mp3')
- mp3s = expr_mp3.evaluate('test_dir')
- mp3s # => ["test_dir/subdir/other.mp3", "test_dir/big.mp3", "test_dir/small.mp3", "test_dir/big2.mp3"]
- # 大きなファイル
- class Bigger < Expression
- def initialize(size)
- @size = size
- end
- def evaluate(dir)
- results = []
- Find.find(dir) do |p|
- next unless File.file?(p)
- results << p if( File.size(p) > @size)
- end
- results
- end
- end
- expr_big = Bigger.new(80)
- bigs = expr_big.evaluate('test_dir')
- bigs # => ["test_dir/subdir/other.mp3", "test_dir/big.mp3", "test_dir/big2.mp3"]
- #書き込み可能ファイル
- class Writable < Expression
- def evaluate(dir)
- results = []
- Find.find(dir) do |p|
- next unless File.file?(p)
- results << p if( File.writable?(p) )
- end
- results
- end
- end
- expr_writable = Writable.new
- writables = expr_writable.evaluate('test_dir')
- writables # => ["test_dir/small2.txt", "test_dir/subdir/small.jpg", "test_dir/subdir/other.mp3", "test_dir/small1.txt", "test_dir/big.mp3", "test_dir/small.mp3", "test_dir/big2.mp3"]
- ** Not,And,Or を使ったより複雑な検索
- class Not < Expression
- def initialize(expression)
- @expression = expression
- end
- def evaluate(dir)
- All.new.evaluate(dir) - @expression.evaluate(dir)
- end
- end
- expr_not_writable = Not.new(Writable.new)
- readonly_files = expr_not_writable.evaluate('test_dir')
- readonly_files # => ["test_dir/small1.txt"]
- small_expr = Not.new(Bigger.new(1024))
- small_files = small_expr.evaluate('test_dir')
- small_files # => ["test_dir/small2.txt", "test_dir/subdir/small.jpg", "test_dir/small1.txt", "test_dir/small.mp3"]
- not_mp3_expr = Not.new(FileName.new('*.mp3'))
- not_mp3_files = not_mp3_expr.evaluate('test_dir')
- not_mp3_files # => ["test_dir/small2.txt", "test_dir/subdir/small.jpg", "test_dir/small1.txt"]
- class Or < Expression
- def initialize(expression1, expression2)
- @expression1 = expression1
- @expression2 = expression2
- end
- def evaluate(dir)
- result1 = @expression1.evaluate(dir)
- result2 = @expression2.evaluate(dir)
- (result1 + result2).sort.uniq
- end
- end
- # MP3ファイルまたは1KBより大きいファイルを一度に検索する
- big_or_mp3_expr = Or.new(Bigger.new(1024), FileName.new('*.mp3'))
- big_or_mp3s = big_or_mp3_expr.evaluate('test_dir')
- big_or_mp3s # => ["test_dir/big.mp3", "test_dir/big2.mp3", "test_dir/small.mp3", "test_dir/subdir/other.mp3"]
- #このような複雑なASTを作ると、別のコンテキストでそれを繰り返し利用できる。
- # big_or_mp3_expr.evaluate('test_dir')
- # big_or_mp3_expr.evaluate('/home/me/tmp')
- class And < Expression
- def initialize(expression1, expression2)
- @expression1 = expression1
- @expression2 = expression2
- end
- def evaluate(dir)
- result1 = @expression1.evaluate(dir)
- result2 = @expression2.evaluate(dir)
- (result1 & result2)
- end
- end
- ** パーサを使わないインタープリタにすることにした/便利なショートカットを導入(DSL)
- 上位クラス(Expression) にAnd,Or のシンタックスシュガーを作成して書きやすくする
- class Expression
- def |(other)
- Or.new(self, other)
- end
- def &(other)
- And.new(self, other)
- end
- end
- expr = Or.new(
- And.new(Bigger.new(1024), Not.new(Writable.new )),
- FileName.new('*.mp3'))
- files = expr.evaluate('test_dir')
- files # => ["test_dir/big.mp3", "test_dir/big2.mp3", "test_dir/small.mp3", "test_dir/subdir/other.mp3"]
- expr = (Bigger.new(1024) & Not.new(Writable.new)) | FileName.new('*.mp3') #これは短かくて読みやすい!
- files = expr.evaluate('test_dir')
- files # => ["test_dir/big.mp3", "test_dir/big2.mp3", "test_dir/small.mp3", "test_dir/subdir/other.mp3"]
Add Comment
Please, Sign In to add comment