SlideShare ist ein Scribd-Unternehmen logo
1 von 140
Downloaden Sie, um offline zu lesen
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/
✓
✓
✓
✓
✓
Working Effectively With Legacy Tdiary Code Using Cucumber And Rspec

Weitere ähnliche Inhalte

Was ist angesagt?

Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleHugo Hamon
 
Python Ireland Nov 2010 Talk: Unit Testing
Python Ireland Nov 2010 Talk: Unit TestingPython Ireland Nov 2010 Talk: Unit Testing
Python Ireland Nov 2010 Talk: Unit TestingPython Ireland
 
Advanced php testing in action
Advanced php testing in actionAdvanced php testing in action
Advanced php testing in actionJace Ju
 
Future of HTTP in CakePHP
Future of HTTP in CakePHPFuture of HTTP in CakePHP
Future of HTTP in CakePHPmarkstory
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & RESTHugo Hamon
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistenceHugo Hamon
 
PHP tips and tricks
PHP tips and tricks PHP tips and tricks
PHP tips and tricks Damien Seguy
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design PatternsHugo Hamon
 
Symfony without the framework
Symfony without the frameworkSymfony without the framework
Symfony without the frameworkGOG.com dev team
 
PHP Data Objects
PHP Data ObjectsPHP Data Objects
PHP Data ObjectsWez Furlong
 
Dependency Injection with PHP and PHP 5.3
Dependency Injection with PHP and PHP 5.3Dependency Injection with PHP and PHP 5.3
Dependency Injection with PHP and PHP 5.3Fabien Potencier
 
Webinar: Replication and Replica Sets
Webinar: Replication and Replica SetsWebinar: Replication and Replica Sets
Webinar: Replication and Replica SetsMongoDB
 
From mysql to MongoDB(MongoDB2011北京交流会)
From mysql to MongoDB(MongoDB2011北京交流会)From mysql to MongoDB(MongoDB2011北京交流会)
From mysql to MongoDB(MongoDB2011北京交流会)Night Sailer
 
CoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairCoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairMark
 
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011Alessandro Nadalin
 
MTDDC 2010.2.5 Tokyo - Brand new API
MTDDC 2010.2.5 Tokyo - Brand new APIMTDDC 2010.2.5 Tokyo - Brand new API
MTDDC 2010.2.5 Tokyo - Brand new APISix Apart KK
 
購物車程式架構簡介
購物車程式架構簡介購物車程式架構簡介
購物車程式架構簡介Jace Ju
 

Was ist angesagt? (20)

Agile database access with CakePHP 3
Agile database access with CakePHP 3Agile database access with CakePHP 3
Agile database access with CakePHP 3
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et Pimple
 
Python Ireland Nov 2010 Talk: Unit Testing
Python Ireland Nov 2010 Talk: Unit TestingPython Ireland Nov 2010 Talk: Unit Testing
Python Ireland Nov 2010 Talk: Unit Testing
 
Perl object ?
Perl object ?Perl object ?
Perl object ?
 
Advanced php testing in action
Advanced php testing in actionAdvanced php testing in action
Advanced php testing in action
 
Future of HTTP in CakePHP
Future of HTTP in CakePHPFuture of HTTP in CakePHP
Future of HTTP in CakePHP
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
 
PHP tips and tricks
PHP tips and tricks PHP tips and tricks
PHP tips and tricks
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
 
Symfony without the framework
Symfony without the frameworkSymfony without the framework
Symfony without the framework
 
PHP Data Objects
PHP Data ObjectsPHP Data Objects
PHP Data Objects
 
Dependency Injection with PHP and PHP 5.3
Dependency Injection with PHP and PHP 5.3Dependency Injection with PHP and PHP 5.3
Dependency Injection with PHP and PHP 5.3
 
Webinar: Replication and Replica Sets
Webinar: Replication and Replica SetsWebinar: Replication and Replica Sets
Webinar: Replication and Replica Sets
 
From mysql to MongoDB(MongoDB2011北京交流会)
From mysql to MongoDB(MongoDB2011北京交流会)From mysql to MongoDB(MongoDB2011北京交流会)
From mysql to MongoDB(MongoDB2011北京交流会)
 
Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010
 
CoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairCoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love Affair
 
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
 
MTDDC 2010.2.5 Tokyo - Brand new API
MTDDC 2010.2.5 Tokyo - Brand new APIMTDDC 2010.2.5 Tokyo - Brand new API
MTDDC 2010.2.5 Tokyo - Brand new API
 
購物車程式架構簡介
購物車程式架構簡介購物車程式架構簡介
購物車程式架構簡介
 

Andere mochten auch

Agile Estimating and Planning:the Yin Side
Agile Estimating and Planning:the Yin SideAgile Estimating and Planning:the Yin Side
Agile Estimating and Planning:the Yin SideShintaro Kakutani
 
OedoRubyKaigi01 Opening Talk
OedoRubyKaigi01 Opening TalkOedoRubyKaigi01 Opening Talk
OedoRubyKaigi01 Opening TalkShintaro Kakutani
 
All about Nihon Ruby-no-kai Season6(TochigiRubyKaigi02 Edit)
All about Nihon Ruby-no-kai Season6(TochigiRubyKaigi02 Edit)All about Nihon Ruby-no-kai Season6(TochigiRubyKaigi02 Edit)
All about Nihon Ruby-no-kai Season6(TochigiRubyKaigi02 Edit)Shintaro Kakutani
 
Agile Estimating and Planning
Agile Estimating and PlanningAgile Estimating and Planning
Agile Estimating and PlanningShintaro Kakutani
 
For Nature Of Software Develoment
For Nature Of Software DevelomentFor Nature Of Software Develoment
For Nature Of Software DevelomentShintaro Kakutani
 
Nature Of Software Development
Nature Of Software DevelopmentNature Of Software Development
Nature Of Software DevelopmentShintaro Kakutani
 
Integration Test Hell
Integration Test HellIntegration Test Hell
Integration Test HellDavid Völkel
 
Working Effectively With Legacy Perl Code
Working Effectively With Legacy Perl CodeWorking Effectively With Legacy Perl Code
Working Effectively With Legacy Perl Codeerikmsp
 
Property Based BDD Examples (ETSI UCAAT 2016, Budapest)
Property Based BDD Examples (ETSI UCAAT 2016, Budapest)Property Based BDD Examples (ETSI UCAAT 2016, Budapest)
Property Based BDD Examples (ETSI UCAAT 2016, Budapest)Gáspár Nagy
 
Transformation Priority Premise @Softwerkskammer MUC
Transformation Priority Premise @Softwerkskammer MUCTransformation Priority Premise @Softwerkskammer MUC
Transformation Priority Premise @Softwerkskammer MUCDavid Völkel
 
Working Effectively with Legacy Code: Lessons in Practice
Working Effectively with Legacy Code: Lessons in PracticeWorking Effectively with Legacy Code: Lessons in Practice
Working Effectively with Legacy Code: Lessons in PracticeAmar Shah
 
Infrastructure code in Agile software development
Infrastructure code in Agile software developmentInfrastructure code in Agile software development
Infrastructure code in Agile software developmentElad Sofer
 
Working Compassionately with Legacy Code, RubyConf 2015
Working Compassionately with Legacy Code, RubyConf 2015Working Compassionately with Legacy Code, RubyConf 2015
Working Compassionately with Legacy Code, RubyConf 2015Amar Shah
 
Keeping Code Agile
Keeping Code AgileKeeping Code Agile
Keeping Code AgileDavid Legge
 
Mockist vs. Classicists TDD
Mockist vs. Classicists TDDMockist vs. Classicists TDD
Mockist vs. Classicists TDDDavid Völkel
 
Baby Steps TDD Approaches
Baby Steps TDD ApproachesBaby Steps TDD Approaches
Baby Steps TDD ApproachesDavid Völkel
 

Andere mochten auch (20)

Agile Estimating and Planning:the Yin Side
Agile Estimating and Planning:the Yin SideAgile Estimating and Planning:the Yin Side
Agile Estimating and Planning:the Yin Side
 
OedoRubyKaigi01 Opening Talk
OedoRubyKaigi01 Opening TalkOedoRubyKaigi01 Opening Talk
OedoRubyKaigi01 Opening Talk
 
All about Nihon Ruby-no-kai Season6(TochigiRubyKaigi02 Edit)
All about Nihon Ruby-no-kai Season6(TochigiRubyKaigi02 Edit)All about Nihon Ruby-no-kai Season6(TochigiRubyKaigi02 Edit)
All about Nihon Ruby-no-kai Season6(TochigiRubyKaigi02 Edit)
 
Agile Estimating and Planning
Agile Estimating and PlanningAgile Estimating and Planning
Agile Estimating and Planning
 
For Nature Of Software Develoment
For Nature Of Software DevelomentFor Nature Of Software Develoment
For Nature Of Software Develoment
 
Take The Red Pill
Take The Red PillTake The Red Pill
Take The Red Pill
 
From Java To Ruby In Action
From Java To Ruby In ActionFrom Java To Ruby In Action
From Java To Ruby In Action
 
Nature Of Software Development
Nature Of Software DevelopmentNature Of Software Development
Nature Of Software Development
 
Keccon LT by kakutani
Keccon LT by kakutaniKeccon LT by kakutani
Keccon LT by kakutani
 
Integration Test Hell
Integration Test HellIntegration Test Hell
Integration Test Hell
 
Working Effectively With Legacy Perl Code
Working Effectively With Legacy Perl CodeWorking Effectively With Legacy Perl Code
Working Effectively With Legacy Perl Code
 
Property Based BDD Examples (ETSI UCAAT 2016, Budapest)
Property Based BDD Examples (ETSI UCAAT 2016, Budapest)Property Based BDD Examples (ETSI UCAAT 2016, Budapest)
Property Based BDD Examples (ETSI UCAAT 2016, Budapest)
 
Transformation Priority Premise @Softwerkskammer MUC
Transformation Priority Premise @Softwerkskammer MUCTransformation Priority Premise @Softwerkskammer MUC
Transformation Priority Premise @Softwerkskammer MUC
 
Working Effectively with Legacy Code: Lessons in Practice
Working Effectively with Legacy Code: Lessons in PracticeWorking Effectively with Legacy Code: Lessons in Practice
Working Effectively with Legacy Code: Lessons in Practice
 
Infrastructure code in Agile software development
Infrastructure code in Agile software developmentInfrastructure code in Agile software development
Infrastructure code in Agile software development
 
Working Compassionately with Legacy Code, RubyConf 2015
Working Compassionately with Legacy Code, RubyConf 2015Working Compassionately with Legacy Code, RubyConf 2015
Working Compassionately with Legacy Code, RubyConf 2015
 
Keeping Code Agile
Keeping Code AgileKeeping Code Agile
Keeping Code Agile
 
Méthode Mikado
Méthode MikadoMéthode Mikado
Méthode Mikado
 
Mockist vs. Classicists TDD
Mockist vs. Classicists TDDMockist vs. Classicists TDD
Mockist vs. Classicists TDD
 
Baby Steps TDD Approaches
Baby Steps TDD ApproachesBaby Steps TDD Approaches
Baby Steps TDD Approaches
 

Ähnlich wie Working Effectively With Legacy Tdiary Code Using Cucumber And Rspec

R57shell
R57shellR57shell
R57shellady36
 
TurboGears2 Pluggable Applications
TurboGears2 Pluggable ApplicationsTurboGears2 Pluggable Applications
TurboGears2 Pluggable ApplicationsAlessandro Molina
 
And the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack SupportAnd the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack SupportBen Scofield
 
お題でGroovyプログラミング: Part A
お題でGroovyプログラミング: Part Aお題でGroovyプログラミング: Part A
お題でGroovyプログラミング: Part AKazuchika Sekiya
 
the next web now
the next web nowthe next web now
the next web nowzulin Gu
 
Rails Antipatterns | Open Session with Chad Pytel
Rails Antipatterns | Open Session with Chad Pytel Rails Antipatterns | Open Session with Chad Pytel
Rails Antipatterns | Open Session with Chad Pytel Engine Yard
 
Codeigniter : Two Step View - Concept Implementation
Codeigniter : Two Step View - Concept ImplementationCodeigniter : Two Step View - Concept Implementation
Codeigniter : Two Step View - Concept ImplementationAbdul Malik Ikhsan
 
All I Need to Know I Learned by Writing My Own Web Framework
All I Need to Know I Learned by Writing My Own Web FrameworkAll I Need to Know I Learned by Writing My Own Web Framework
All I Need to Know I Learned by Writing My Own Web FrameworkBen Scofield
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Tsuyoshi Yamamoto
 
Php tutorial handout
Php tutorial handoutPhp tutorial handout
Php tutorial handoutSBalan Balan
 
Middleman Usecase / TokyouMiddlemanMeetup#1
Middleman Usecase / TokyouMiddlemanMeetup#1Middleman Usecase / TokyouMiddlemanMeetup#1
Middleman Usecase / TokyouMiddlemanMeetup#1Kotaro Kawashima
 
You're Doing It Wrong
You're Doing It WrongYou're Doing It Wrong
You're Doing It Wrongbostonrb
 
You're Doing It Wrong
You're Doing It WrongYou're Doing It Wrong
You're Doing It Wrongbostonrb
 
The Zen of Lithium
The Zen of LithiumThe Zen of Lithium
The Zen of LithiumNate Abele
 
Building a Dynamic Website Using Django
Building a Dynamic Website Using DjangoBuilding a Dynamic Website Using Django
Building a Dynamic Website Using DjangoNathan Eror
 
DBIx::Skinnyと仲間たち
DBIx::Skinnyと仲間たちDBIx::Skinnyと仲間たち
DBIx::Skinnyと仲間たちRyo Miyake
 

Ähnlich wie Working Effectively With Legacy Tdiary Code Using Cucumber And Rspec (20)

Little Big Ruby
Little Big RubyLittle Big Ruby
Little Big Ruby
 
R57shell
R57shellR57shell
R57shell
 
TurboGears2 Pluggable Applications
TurboGears2 Pluggable ApplicationsTurboGears2 Pluggable Applications
TurboGears2 Pluggable Applications
 
And the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack SupportAnd the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack Support
 
お題でGroovyプログラミング: Part A
お題でGroovyプログラミング: Part Aお題でGroovyプログラミング: Part A
お題でGroovyプログラミング: Part A
 
Blog Hacks 2011
Blog Hacks 2011Blog Hacks 2011
Blog Hacks 2011
 
the next web now
the next web nowthe next web now
the next web now
 
Rails Antipatterns | Open Session with Chad Pytel
Rails Antipatterns | Open Session with Chad Pytel Rails Antipatterns | Open Session with Chad Pytel
Rails Antipatterns | Open Session with Chad Pytel
 
Codeigniter : Two Step View - Concept Implementation
Codeigniter : Two Step View - Concept ImplementationCodeigniter : Two Step View - Concept Implementation
Codeigniter : Two Step View - Concept Implementation
 
All I Need to Know I Learned by Writing My Own Web Framework
All I Need to Know I Learned by Writing My Own Web FrameworkAll I Need to Know I Learned by Writing My Own Web Framework
All I Need to Know I Learned by Writing My Own Web Framework
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
 
About Data::ObjectDriver
About Data::ObjectDriverAbout Data::ObjectDriver
About Data::ObjectDriver
 
Php tutorial handout
Php tutorial handoutPhp tutorial handout
Php tutorial handout
 
Middleman Usecase / TokyouMiddlemanMeetup#1
Middleman Usecase / TokyouMiddlemanMeetup#1Middleman Usecase / TokyouMiddlemanMeetup#1
Middleman Usecase / TokyouMiddlemanMeetup#1
 
You're Doing It Wrong
You're Doing It WrongYou're Doing It Wrong
You're Doing It Wrong
 
You're Doing It Wrong
You're Doing It WrongYou're Doing It Wrong
You're Doing It Wrong
 
The Zen of Lithium
The Zen of LithiumThe Zen of Lithium
The Zen of Lithium
 
Building a Dynamic Website Using Django
Building a Dynamic Website Using DjangoBuilding a Dynamic Website Using Django
Building a Dynamic Website Using Django
 
Separation of concerns - DPC12
Separation of concerns - DPC12Separation of concerns - DPC12
Separation of concerns - DPC12
 
DBIx::Skinnyと仲間たち
DBIx::Skinnyと仲間たちDBIx::Skinnyと仲間たち
DBIx::Skinnyと仲間たち
 

Mehr von Shintaro Kakutani

Postface from agilesamurai_supervisor
Postface from agilesamurai_supervisorPostface from agilesamurai_supervisor
Postface from agilesamurai_supervisorShintaro Kakutani
 
Testing Environment of Ruby on Rails
Testing Environment of Ruby on RailsTesting Environment of Ruby on Rails
Testing Environment of Ruby on RailsShintaro Kakutani
 
Agile Estimating and Planning on JFPUG
Agile Estimating and Planning on JFPUGAgile Estimating and Planning on JFPUG
Agile Estimating and Planning on JFPUGShintaro Kakutani
 
There Is No Spoon: Revisited
There Is No Spoon: RevisitedThere Is No Spoon: Revisited
There Is No Spoon: RevisitedShintaro Kakutani
 
"Ordinary" System Development
"Ordinary" System Development"Ordinary" System Development
"Ordinary" System DevelopmentShintaro Kakutani
 
what does "we speak Ruby" really mean?
what does "we speak Ruby" really mean?what does "we speak Ruby" really mean?
what does "we speak Ruby" really mean?Shintaro Kakutani
 
All About Nihon Ruby-no-Kai in Developers Summit 2010
All About Nihon Ruby-no-Kai in Developers Summit 2010All About Nihon Ruby-no-Kai in Developers Summit 2010
All About Nihon Ruby-no-Kai in Developers Summit 2010Shintaro Kakutani
 
Welcome To The Desert Of The Real
Welcome To The Desert Of The RealWelcome To The Desert Of The Real
Welcome To The Desert Of The RealShintaro Kakutani
 
You Should Attend Rubykaigi2010
You Should Attend Rubykaigi2010You Should Attend Rubykaigi2010
You Should Attend Rubykaigi2010Shintaro Kakutani
 
Allabout Nihon Ruby-no-kai Season6(KansaiRubyKaigi02 Edit)
Allabout Nihon Ruby-no-kai Season6(KansaiRubyKaigi02 Edit)Allabout Nihon Ruby-no-kai Season6(KansaiRubyKaigi02 Edit)
Allabout Nihon Ruby-no-kai Season6(KansaiRubyKaigi02 Edit)Shintaro Kakutani
 
Making Software Development Agile With Ruby
Making Software Development Agile With RubyMaking Software Development Agile With Ruby
Making Software Development Agile With RubyShintaro Kakutani
 
All about Nihon Ruby-no-kai:Season6 (NagoyaRubyKaigi01 Edit.)
All about Nihon Ruby-no-kai:Season6 (NagoyaRubyKaigi01 Edit.)All about Nihon Ruby-no-kai:Season6 (NagoyaRubyKaigi01 Edit.)
All about Nihon Ruby-no-kai:Season6 (NagoyaRubyKaigi01 Edit.)Shintaro Kakutani
 
Project Report:Regional Rubykaigi
Project Report:Regional RubykaigiProject Report:Regional Rubykaigi
Project Report:Regional RubykaigiShintaro Kakutani
 
From Iron Triangle To Iron Line
From Iron Triangle To Iron LineFrom Iron Triangle To Iron Line
From Iron Triangle To Iron LineShintaro Kakutani
 
Practices Of An Agile Developer
Practices Of An Agile DeveloperPractices Of An Agile Developer
Practices Of An Agile DeveloperShintaro Kakutani
 

Mehr von Shintaro Kakutani (20)

Postface from agilesamurai_supervisor
Postface from agilesamurai_supervisorPostface from agilesamurai_supervisor
Postface from agilesamurai_supervisor
 
Welcome to the Real World
Welcome to the Real WorldWelcome to the Real World
Welcome to the Real World
 
The gate
The gateThe gate
The gate
 
Testing Environment of Ruby on Rails
Testing Environment of Ruby on RailsTesting Environment of Ruby on Rails
Testing Environment of Ruby on Rails
 
Agile Estimating and Planning on JFPUG
Agile Estimating and Planning on JFPUGAgile Estimating and Planning on JFPUG
Agile Estimating and Planning on JFPUG
 
There Is No Spoon: Revisited
There Is No Spoon: RevisitedThere Is No Spoon: Revisited
There Is No Spoon: Revisited
 
Agile in 30mins
Agile in 30minsAgile in 30mins
Agile in 30mins
 
"Ordinary" System Development
"Ordinary" System Development"Ordinary" System Development
"Ordinary" System Development
 
There is no_spoon
There is no_spoonThere is no_spoon
There is no_spoon
 
what does "we speak Ruby" really mean?
what does "we speak Ruby" really mean?what does "we speak Ruby" really mean?
what does "we speak Ruby" really mean?
 
All About Nihon Ruby-no-Kai in Developers Summit 2010
All About Nihon Ruby-no-Kai in Developers Summit 2010All About Nihon Ruby-no-Kai in Developers Summit 2010
All About Nihon Ruby-no-Kai in Developers Summit 2010
 
Welcome To The Desert Of The Real
Welcome To The Desert Of The RealWelcome To The Desert Of The Real
Welcome To The Desert Of The Real
 
You Should Attend Rubykaigi2010
You Should Attend Rubykaigi2010You Should Attend Rubykaigi2010
You Should Attend Rubykaigi2010
 
Allabout Nihon Ruby-no-kai Season6(KansaiRubyKaigi02 Edit)
Allabout Nihon Ruby-no-kai Season6(KansaiRubyKaigi02 Edit)Allabout Nihon Ruby-no-kai Season6(KansaiRubyKaigi02 Edit)
Allabout Nihon Ruby-no-kai Season6(KansaiRubyKaigi02 Edit)
 
Making Software Development Agile With Ruby
Making Software Development Agile With RubyMaking Software Development Agile With Ruby
Making Software Development Agile With Ruby
 
All about Nihon Ruby-no-kai:Season6 (NagoyaRubyKaigi01 Edit.)
All about Nihon Ruby-no-kai:Season6 (NagoyaRubyKaigi01 Edit.)All about Nihon Ruby-no-kai:Season6 (NagoyaRubyKaigi01 Edit.)
All about Nihon Ruby-no-kai:Season6 (NagoyaRubyKaigi01 Edit.)
 
Do You See The Light
Do You See The LightDo You See The Light
Do You See The Light
 
Project Report:Regional Rubykaigi
Project Report:Regional RubykaigiProject Report:Regional Rubykaigi
Project Report:Regional Rubykaigi
 
From Iron Triangle To Iron Line
From Iron Triangle To Iron LineFrom Iron Triangle To Iron Line
From Iron Triangle To Iron Line
 
Practices Of An Agile Developer
Practices Of An Agile DeveloperPractices Of An Agile Developer
Practices Of An Agile Developer
 

Working Effectively With Legacy Tdiary Code Using Cucumber And Rspec

  • 1. Working Effectively with Legacy tDiary Code using Cucumber and RSpec KAKUTANI Shintaro; Eiwa System Management,Inc.; Nihon Ruby-no-kai
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7. Working Effectively with Legacy tDiary code using Cucumber and Rspec
  • 8. Working Effectively with Legacy tDiary code using Cucumber and Rspec
  • 9. Working Effectively with Legacy tDiary code using Cucumber and Rspec
  • 10. Working Effectively with Legacy tDiary code using Cucumber and Rspec
  • 11. Working Effectively with Legacy tDiary code using Cucumber and Rspec
  • 15.
  • 16.
  • 18.
  • 19. ✓ ‣ ‣ ‣ ✓ ‣ ‣
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30. 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|
  • 31. 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|
  • 33. Photo by rla579: http://flickr.com/photos/rla579/2482286520/
  • 35.
  • 36.
  • 38. 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|
  • 39.
  • 40. To me, legacy code is simply code without tests. Michael Feathers,“Workin Effectively with Legacy Code”
  • 41.
  • 42.
  • 43.
  • 44.
  • 46.
  • 47.
  • 48. Photo by pfish: http://flickr.com/photos/pfish/16518187/
  • 50.
  • 51. 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
  • 52.
  • 53.
  • 54.
  • 55. 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
  • 58.
  • 59.
  • 60.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 69.
  • 71. “Programming is the art of doing one thing at a time.” Michael Feathers,“Workin Effectively with Legacy Code”
  • 72. Refactoring GREEN E D R
  • 73.
  • 74.
  • 75.
  • 79.
  • 82.
  • 83.
  • 84.
  • 87. #!/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
  • 89. 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 ...
  • 90.
  • 92.
  • 93.
  • 94.
  • 95.
  • 98. Photo by whiteoakart: http://www.flickr.com/photos/whiteoakart/249859836/
  • 100.
  • 101.
  • 105.
  • 106. 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 ...
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 113. # -*- 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 ...
  • 114. 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
  • 115. 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
  • 116. stub(TDiary::Config).tdiary_config_file_path { path } TDiary::Config.stub!(:tdiary_config_file_path). and_return { path }
  • 117. Photo by thomasvignaud: http://www.flickr.com/photos/thomasvignaud/2160847847/
  • 119.
  • 122.
  • 124.
  • 126.
  • 128. 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 ...
  • 129.
  • 130. 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
  • 131. ... 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
  • 132. ... module Rack app = ShowExceptions.new( CommonLogger.new( Lint.new( TDiaryApp.index))) Handler::CGI.run app end
  • 133.
  • 136.
  • 137. Let’s face it, working in legacy code is surgery, and doctors never operate alone. Michael Feathers,“Workin Effectively with Legacy Code”