結局、UI部分をモジュールをクラスに置き換えた.
前回までモジュールだったUI部分をクラス化して、injectするように.
いつもの見慣れた構造.こっちのほうが読みやすいし、テストし易い.
Spyというキーワードを使ったのは、Test Spy at XUnitPatterns.comを参考に.
set_uiよりinject_uiを使ったのは、師匠の影響.
スパイがセットされるより、スパイがインジェクトされるほうが、スパイっぽいw
最近書いているのは、今まで書いたGUIプログラミングとはちょっと違う感覚をもっている.
MVCモデルというより、UnixのパイプアンドフィルターにややこしいGUIとかが、挟まった感じ.
ややこしいのは外に追いやって、injectすると、テストし易くなる.
たぶん、ファイル検索系もややこしいところなので分けたほうが、テストし易くなると思う.
require File.dirname(__FILE__) + '/../../lib/quickmate/choice_switch_command' module QuickMate describe ChoiceSwitchCommand, "file choice targets" do def choice(expected) Pairs.new(expected) end before(:each) do @spy_ui = SpyUI.new @command = ChoiceSwitchCommand.new @command.inject_ui(@spy_ui) @fixtures_path = "#{ENV["TM_PROJECT_DIRECTORY"]}/Support/fixtures/exist_file" end it "should choice many file when current file is lib (one to many)" do @command.open_pair("#{@fixtures_path}/lib/foo/one_to_many.rb") @spy_ui.actual_paths.should == [ "#{@fixtures_path}/spec/foo/feature_1-one_to_many_spec.rb", "#{@fixtures_path}/spec/foo/feature_2-one_to_many_spec.rb", "#{@fixtures_path}/spec/foo/one_to_many_spec.rb"] end it "should choice one file when current file is spec (many to one)" do @command.open_pair("#{@fixtures_path}/spec/foo/feature_1-one_to_many_spec.rb") @spy_ui.actual_paths.should == ["#{@fixtures_path}/lib/foo/one_to_many.rb"] end it "should choice one file when current file is lib (one to one)" do @command.open_pair("#{@fixtures_path}/lib/foo/one_to_one.rb") @spy_ui.actual_paths.should == ["#{@fixtures_path}/spec/foo/one_to_one_spec.rb"] end end class SpyUI attr_reader :actual_paths def open_menu(path_line_views) @actual_paths = path_line_views.collect { |path_line_views| path_line_views[0] } end end end
require File.dirname(__FILE__) + '/ui' module QuickMate class ChoiceSwitchCommand PRE, TYPE, POST, FILE = 0, 1, 2, 3 def initialize( config = { :project_dir => ENV["TM_PROJECT_DIRECTORY"], :spec_dir => "/spec", :product_dir => "/lib", # :prefix_spec => "[^/]*-", # PEND ややこしくなったのでやめた. :sufix_spec => "_spec", }) @conf = config @ui = UI.new end def inject_ui(ui) @ui = ui end def open_pair(path) token = token_pre_type_post_file(path) paths = select_pair_targets(token) if paths.size == 0 # TODO 新規ファイルを作成ができるようにすること puts "not found pair. not support create file" else @ui.open_menu(path_line_views(paths)) end end def path_line_views(paths) paths.collect { |path| [path, 1, File.basename(path)] } end def select_pair_targets(token) if token[TYPE] == @conf[:spec_dir] return select_files(lib_pattern(token)) elsif token[TYPE] == @conf[:product_dir] return select_files(spec_pattern(token)) end puts "select pair files error!! toke => #{ toeken.inspect }, @conf=>#{ @conf.inspect }" end def select_files(pattern) Dir.glob("#{@conf[:project_dir]}/**/*.rb").select{ |file| /#{pattern}/ =~ file } end def token_pre_type_post_file(path) dir_base = File.split(path) with_post = "^(.*?)(/lib|/app|/spec)(/.*?)$" dir_base[PRE] =~ /#{ with_post }/ return $1, $2, $3, dir_base[1] unless $1 == nil # パッケージディレクトリがない場合は空文字列で補完する. without_post = "(.*?)(/lib|/app|/spec)" dir_base[PRE] =~ /#{ without_post }/ return $1, $2, '', dir_base[TYPE] unless $1 == nil puts "token error !! path => #{ path }, @conf => #{ @conf.inspect }" end def spec_pattern(token) directory = "#{ token[PRE] }#{ @conf[:spec_dir] }#{ token[POST] }" file = "[^/]*#{ token[FILE].sub(".rb", "") }#{ @conf[:sufix_spec] }.rb$" "#{directory}/#{file}" end def lib_pattern(token) directory = "#{ token[PRE] }#{ @conf[:product_dir] }#{ token[POST] }" # プレフィックスとベースネームのトークンを'-'で プレフィックスにFixture名とか付けるつもり with_prefix = "^(.*?)-(.*?)#{ @conf[:sufix_spec] }.rb$" token[FILE] =~ /#{ with_prefix }/ file = nil file = $2 + ".rb" unless $1 == nil without_prefix = "^(.*?)#{ @conf[:sufix_spec] }.rb$" token[FILE] =~ /#{ without_prefix }/ file = $1 + ".rb" if file == nil "#{directory}/#{file}$" end end end