• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Working Effectively With Legacy Tdiary Code Using Cucumber And Rspec
 

Working Effectively With Legacy Tdiary Code Using Cucumber And Rspec

on

  • 5,864 views

Trying to make tdiary testable with cucumber on SapporoRubyKaigi01

Trying to make tdiary testable with cucumber on SapporoRubyKaigi01

Statistics

Views

Total Views
5,864
Views on SlideShare
5,186
Embed Views
678

Actions

Likes
7
Downloads
21
Comments
0

9 Embeds 678

http://kakutani.com 666
http://www.slideshare.net 4
https://twitter.com 2
http://fieldnotes.sytes.net 1
http://static.slideshare.net 1
http://72.14.235.132 1
http://74.125.153.132 1
http://www.www.kakutani.com 1
http://s.deeeki.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Working Effectively With Legacy Tdiary Code Using Cucumber And Rspec Working Effectively With Legacy Tdiary Code Using Cucumber And Rspec Presentation Transcript

    • Working Effectively with Legacy tDiary Code using Cucumber and RSpec KAKUTANI Shintaro; Eiwa System Management,Inc.; Nihon Ruby-no-kai
    • Working Effectively with Legacy tDiary code using Cucumber and Rspec
    • Working Effectively with Legacy tDiary code using Cucumber and Rspec
    • Working Effectively with Legacy tDiary code using Cucumber and Rspec
    • Working Effectively with Legacy tDiary code using Cucumber and Rspec
    • Working Effectively with Legacy tDiary code using Cucumber and Rspec
    • ✓ ✓ ✓ ✓
    • ✓ ✓ ✓ ✓ ✓
    • ✓ ✓ ✓ ✓ ✓
    • ✓ ✓ ✓
    • ✓ ‣ ‣ ‣ ✓ ‣ ‣
    • ✓ ✓ ✓ ✓ ✓
    • eval( quot;@#{key} = params['#{key}']quot; ) end # for 1.4 compatibility @index = @conf.index @update = @conf.update @author_name = @conf.author_name || '' @author_mail = @conf.author_mail || '' @index_page = @conf.index_page || '' @html_title = @conf.html_title || '' @theme = @conf.theme @css = @conf.css @date_format = @conf.date_format @referer_table = @conf.referer_table @options = @conf.options # for ruby 1.6.x support if @conf.secure then @cgi.params.each_value do |p|
    • eval( quot;@#{key} = params['#{key}']quot; ) end # for 1.4 compatibility @index = @conf.index ✓ @update = @conf.update @author_name = @conf.author_name || '' @author_mail = @conf.author_mail || '' ✓ @index_page = @conf.index_page || '' @html_title = @conf.html_title || '' ✓ @theme = @conf.theme @css = @conf.css @date_format = @conf.date_format ✓ @referer_table = @conf.referer_table @options = @conf.options ✓ # for ruby 1.6.x support if @conf.secure then @cgi.params.each_value do |p|
    • ✓ ✓ ✓ ✓ ✓
    • Photo by rla579: http://flickr.com/photos/rla579/2482286520/
    • ✓ ✓ ✓ ✓ ✓
    • ✓ ✓ ✓
    • eval( quot;@#{key} = params['#{key}']quot; ) end # for 1.4 compatibility @index = @conf.index ✓ @update = @conf.update @author_name = @conf.author_name || '' @author_mail = @conf.author_mail || '' ✓ @index_page = @conf.index_page || '' @html_title = @conf.html_title || '' ✓ @theme = @conf.theme @css = @conf.css @date_format = @conf.date_format ✓ @referer_table = @conf.referer_table @options = @conf.options ✓ # for ruby 1.6.x support if @conf.secure then @cgi.params.each_value do |p|
    • To me, legacy code is simply code without tests. Michael Feathers,“Workin Effectively with Legacy Code”
    • ✓ ‣ ✓ ‣
    • Photo by pfish: http://flickr.com/photos/pfish/16518187/
    • http://rack.rubyforge.org/ ✓ ✓ ✓ ✓ ✓
    • class HelloApp def call(env) [200, {quot;Content-Typequot; => quot;text/plainquot;}, quot;Hello, Sapporo!quot;] end end require 'hello_app' run HelloApp.new $ rackup hello.ru
    • if /HEAD/i =~ @cgi.request_method then head['Pragma'] = 'no-cache' head['Cache-Control'] = 'no-cache' print @cgi.header( head ) else #!/usr/bin/env ruby if @cgi.mobile_agent? then # body = conf.to_mobile( tdiary.eval_rhtml( 'i.' ) ) # index.rb $Revision: 1.35 $ head['charset'] = conf.mobile_encoding # head['Content-Length'] = body.size.to_s # Copyright (C) 2001-2006, TADA Tadashi <sho@spc.gr.jp> else # You can redistribute it and/or modify it under GPL2. require 'digest/md5' # body = tdiary.eval_rhtml BEGIN { $defout.binmode } head['ETag'] = %Q[quot;#{Digest::MD5.hexdigest( body )}quot;] $KCODE = 'n' if ENV['HTTP_IF_NONE_MATCH'] == head['ETag'] and /^GET$/i =~ @cgi. head['status'] = CGI::HTTP_STATUS['NOT_MODIFIED'] begin body = '' if FileTest::symlink?( __FILE__ ) then else org_path = File::dirname( File::readlink( __FILE__ ) ) head['charset'] = conf.encoding else head['Content-Length'] = body.size.to_s org_path = File::dirname( __FILE__ ) end end head['Pragma'] = 'no-cache' $:.unshift( org_path.untaint ) head['Cache-Control'] = 'no-cache' require 'tdiary' end head['cookie'] = tdiary.cookies if tdiary.cookies.size > 0 @cgi = CGI::new print @cgi.header( head ) conf = TDiary::Config::new(@cgi) print body tdiary = nil end status = nil rescue TDiary::ForceRedirect if %r[/d{4,8}(-d+)?.html?$] =~ @cgi.redirect_url and not @cgi.valid?( 'date' ) then head = { @cgi.params['date'] = [@cgi.redirect_url.sub( /.*/(d+)(-d+)?.html$/, '12' )] #'Location' => $!.path status = CGI::HTTP_STATUS['OK'] 'type' => 'text/html', end } head['cookie'] = tdiary.cookies if tdiary.cookies.size > 0 begin print @cgi.header( head ) if @cgi.valid?( 'comment' ) then print %Q[ tdiary = TDiary::TDiaryComment::new( @cgi, quot;day.rhtmlquot;, conf ) <html> elsif @cgi.valid?( 'date' ) <head> date = @cgi.params['date'][0] <meta http-equiv=quot;refreshquot; content=quot;1;url=#{$!.path}quot;> if /^d{8}-d+$/ =~ date then <title>moving...</title> tdiary = TDiary::TDiaryLatest::new( @cgi, quot;latest.rhtmlquot;, conf ) </head> elsif /^d{8}$/ =~ date <body>Wait or <a href=quot;#{$!.path}quot;>Click here!</a></body> tdiary = TDiary::TDiaryDay::new( @cgi, quot;day.rhtmlquot;, conf ) </html>] elsif /^d{6}$/ =~ date then rescue TDiary::NotFound tdiary = TDiary::TDiaryMonth::new( @cgi, quot;month.rhtmlquot;, conf ) if @cgi then elsif /^d{4}$/ =~ date then print @cgi.header( 'status' => CGI::HTTP_STATUS['NOT_FOUND'], 'type' tdiary = TDiary::TDiaryNYear::new( @cgi, quot;month.rhtmlquot;, conf ) else end print quot;Status: 404 Not Foundnquot; elsif @cgi.valid?( 'category' ) print quot;Content-Type: text/htmlnnquot; tdiary = TDiary::TDiaryCategoryView::new( @cgi, quot;category.rhtmlquot;, conf ) end elsif @cgi.valid?( 'q' ) puts quot;<h1>404 Not Found</h1>quot; tdiary = TDiary::TDiarySearch::new( @cgi, quot;search.rhtmlquot;, conf ) puts quot;<div>#{' ' * 500}</div>quot; else end tdiary = TDiary::TDiaryLatest::new( @cgi, quot;latest.rhtmlquot;, conf ) rescue Exception end if @cgi then rescue TDiary::PermissionError print @cgi.header( 'status' => CGI::HTTP_STATUS['SERVER_ERROR'], 'type' raise else rescue TDiary::TDiaryError print quot;Status: 500 Internal Server Errornquot; end print quot;Content-Type: text/htmlnnquot; tdiary = TDiary::TDiaryLatest::new( @cgi, quot;latest.rhtmlquot;, conf ) if not tdiary end puts quot;<h1>500 Internal Server Error</h1>quot; begin puts quot;<pre>quot; head = { puts CGI::escapeHTML( quot;#{$!} (#{$!.class})quot; ) 'type' => 'text/html', puts quot;quot; 'Vary' => 'User-Agent' puts CGI::escapeHTML( $@.join( quot;nquot; ) ) } puts quot;</pre>quot; head['status'] = status if status puts quot;<div>#{' ' * 500}</div>quot; body = '' end
    • ✓ ✓ ✓
    • ✓ ✓ ✓ ✓
    • ✓ ✓ ✓ ‣
    • ✓ ✓ ✓
    • ✓ ✓
    • “Programming is the art of doing one thing at a time.” Michael Feathers,“Workin Effectively with Legacy Code”
    • Refactoring GREEN E D R
    • ✓ ✓ ✓ ✓
    • ✓ ✓ ✓ ✓
    • ✓ ✓ ✓ ✓
    • SUMMARY
    • ✓ ‣ ✓ ‣ ✓ ‣
    • ✓ ✓ ✓ ✓ ✓
    • ✓ ✓
    • #!/usr/bin/env ruby require 'webrick' ... class TDiaryDevelopmentServer ... def initialize @server = WEBrick::HTTPServer.new( :Port => 10080, :BindAddress => '127.0.0.1', :DocumentRoot => TDIARY_CORE_DIR, :MimeTypes => tdiary_mime_types, :Logger => WEBrick::Log::new($stderr, WEBrick::Log::DEBUG) ) @server.logger.level = WEBrick::Log::DEBUG trap(quot;INTquot;) { @server.shutdown } trap(quot;TERMquot;) { @server.shutdown } @server.mount(quot;/index.cgiquot;, WEBrick::HTTPServlet::CGIHandler, File.expand_path(quot;index.rbquot;, TDIARY_CORE_DIR)) @server.mount(quot;/update.cgiquot;, WEBrick::HTTPServlet::CGIHandler, File.expand_path(quot;update.rbquot;, TDIARY_CORE_DIR)) end ... end if $0 == __FILE__ TDiaryDevelopmentServer.run end
    • ✓ ✓ ✓
    • module TDiary ... # # class Config # configuration class # class Config ... private # loading tdiary.conf in current directory def load @secure = true unless @secure @options = {} eval( File::open( quot;tdiary.confquot; ){|f| f.read }.untaint, binding, quot;(tdiary.conf)quot;, 1 ) # language setup @lang = 'ja' unless @lang begin ...
    • ✓ ‣ ✓ ‣ ‣ ✓
    • ✓ ‣ ‣ ✓ ‣ ‣
    • ✓ ✓ ✓ ‣ ✓ ‣
    • Photo by whiteoakart: http://www.flickr.com/photos/whiteoakart/249859836/
    • http://github.com/aslakhellesoy/cucumber/ ✓ ✓ ✓ ✓ ‣
    • ✓ ‣ ✓ ‣ ‣ ✓
    • ✓ ✓ ✓ ‣
    • ✓ ✓ ✓
    • module TDiary ... class Config def self.tdiary_config_file_path( filename = quot;tdiary.confquot; ) filename end ... private # loading tdiary.conf in current directory def load @secure = true unless @secure @options = {} load_current_directory_conf ... end def load_current_directory_conf eval( File::open( Config.tdiary_config_file_path ) {|f| f.read }.untaint, b, quot;(#{Config.tdiary_config_file_path})quot;, 1 ) end ...
    • ✓ ✓ ✓ ‣ ✓
    • # -*- coding: utf-8 -*- Given quot; tdiary.confquot; do ... @driver = TDiaryDriver.configure do data_path File.expand_path( quot;../fixtures/just_installed/tdiary.confquot;, File.dirname(__FILE__)) end end ... Given /(?: | ) (.*)/ do |param| @driver.append_param(param) end When /(.*) (?: | ) / do |uri| @status, @header, @response = @driver.invoke(uri) end ...
    • class TDiaryDriver ... def data_path(path) stub(TDiary::Config).tdiary_config_file_path { path } end ... def invoke(path) raw_result = StringIO.new begin stdin_spy = StringIO.new(quot;quot;) ... # $stdin = stdin_spy ... $stdout = raw_result # tdiary_cgi_path = File.expand_path(path, tdiary_base_dir) load tdiary_cgi_path ensure ... # RSpce expectation wrap @res = ResponseHelper.parse(raw_result.read) end ... [@res.status_code, @res.headers, @res.body] end
    • class TDiaryDriver ... def data_path(path) stub(TDiary::Config).tdiary_config_file_path { path } end ... def invoke(path) raw_result = StringIO.new begin stdin_spy = StringIO.new(quot;quot;) ... # $stdin = stdin_spy ... $stdout = raw_result # tdiary_cgi_path = File.expand_path(path, tdiary_base_dir) load tdiary_cgi_path ensure ... # RSpce expectation wrap @res = ResponseHelper.parse(raw_result.read) end ... [@res.status_code, @res.headers, @res.body] end
    • stub(TDiary::Config).tdiary_config_file_path { path } TDiary::Config.stub!(:tdiary_config_file_path). and_return { path }
    • Photo by thomasvignaud: http://www.flickr.com/photos/thomasvignaud/2160847847/
    • http://github.com/btakita/rr/ ✓ ✓ ✓ ‣ ‣
    • http://capsctrl.que.jp/kdmsnr/wiki/bliki/?TestDouble
    • http://xunitpatterns.com/Test%20Double.html
    • ✓ ‣ ‣ ‣ ✓ ✓
    • ✓ ‣ ‣ ✓ ‣ ‣ ‣
    • ✓ ‣ ‣ ✓ ‣
    • class TDiaryDriver ... def data_path(path) stub(TDiary::Config).tdiary_config_file_path { path } end ... def invoke(path) raw_result = StringIO.new begin stdin_spy = StringIO.new(quot;quot;) ... # $stdin = stdin_spy ... $stdout = raw_result # tdiary_cgi_path = File.expand_path(path, tdiary_base_dir) load tdiary_cgi_path ensure ... # RSpce expectation wrap @res = ResponseHelper.parse(raw_result.read) end ... [@res.status_code, @res.headers, @res.body] end ...
    • module Rack class TDiaryApp class << self def index new(TDiaryDriver.index) end ... end ... def call(env) # req = Request.new(env) tdiary_conf = ::File.expand_path( quot;../../tdiary.confquot;, ::File.dirname(__FILE__)) driver = @driver.configure { data_path tdiary_conf } driver.invoke end end end
    • ... use Rack::ShowExceptions use Rack::CommonLogger use Rack::Lint use Rack::Reloader use Rack::Static, :urls => [quot;/themequot;], :root => parent_dir map quot;/quot; do run Rack::TDiaryApp.index end map quot;/index.rbquot; do run Rack::TDiaryApp.index end map quot;/update.rbquot; do run Rack::TDiaryApp.update end
    • ... module Rack app = ShowExceptions.new( CommonLogger.new( Lint.new( TDiaryApp.index))) Handler::CGI.run app end
    • ✓ ‣ ‣ ‣ ✓ ‣
    • ✓ ‣ ✓ ‣ ‣ ‣ ‣
    • Let’s face it, working in legacy code is surgery, and doctors never operate alone. Michael Feathers,“Workin Effectively with Legacy Code”
    • github http://github.com/kakutani/testable_tdiary/
    • ✓ ✓ ✓ ✓ ✓