richard_iii 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/.travis.yml +11 -0
 - data/README.md +37 -26
 - data/Rakefile +15 -0
 - data/lib/richard_iii/curl_reply.rb +72 -0
 - data/lib/richard_iii/internal/curl.rb +68 -0
 - data/lib/richard_iii/internal/text_line.rb +29 -0
 - data/lib/richard_iii/internal/xml_format.rb +17 -0
 - data/lib/richard_iii/request_and_response.rb +33 -0
 - data/lib/richard_iii/version.rb +1 -1
 - data/lib/richard_iii.rb +46 -3
 - data/richard_iii.gemspec +3 -1
 - data/test/acceptance.tests/an_example.rb +34 -0
 - data/test/helper.rb +9 -0
 - data/test/integration.tests/adapters/internet.rb +19 -0
 - data/test/support/extensions.rb +13 -0
 - data/test/support/spy_internet.rb +18 -0
 - data/test/unit.tests/can_conduct_conversations.rb +46 -0
 - data/test/unit.tests/can_find_out_why_a_match_failed.rb +69 -0
 - data/test/unit.tests/can_match_pretty_formatted_xml_bodies.rb +36 -0
 - data/test/unit.tests/can_partial_match_on_replies.rb +75 -0
 - data/test/unit.tests/can_pattern_match_replies.rb +37 -0
 - data/test/unit.tests/earls_may_be_relative_or_absolute.rb +15 -0
 - data/test/unit.tests/the_basics.rb +59 -0
 - metadata +62 -6
 - data/test/the_basics.rb +0 -74
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 434e46a127df3a3f2b582f0f756fa532af1dd7ec
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: a001d83f06e97f59587e9d2bb9986f288c53cde1
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 17c790c383ba196b630152af5e31cd9e1a242f72225352715f9f98b3ad98395e2f3dea20f0576c551ef17f4d879f3c5bca9a816d94a0e636255d20024c3b8775
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: cd5010064c3b9b4e3e26c3bf23c44a3c253aa29a5ab10d702ce8787ebaa5bd02f1b3699f703c1d9268cf2c6076f60487dc98ea61fc8fd9701d8cf331eb14b95b
         
     | 
    
        data/.travis.yml
    ADDED
    
    
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,31 +1,42 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            #  
     | 
| 
      
 1 
     | 
    
         
            +
            # Richard III
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
      
 3 
     | 
    
         
            +
            [](http://travis-ci.org/ben-biddington/richard_iii)
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
      
 5 
     | 
    
         
            +
            Based on [HTTP/1.1](http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html)
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
      
 7 
     | 
    
         
            +
            Here's what we'd like to be able to write about any restful microservice:
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       10 
     | 
    
         
            -
            gem 'richard_iii'
         
     | 
| 
       11 
9 
     | 
    
         
             
            ```
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
      
 10 
     | 
    
         
            +
            describe "An end-to-end example" do
         
     | 
| 
      
 11 
     | 
    
         
            +
              it "lets me see raw text" do
         
     | 
| 
      
 12 
     | 
    
         
            +
                when_I_request <<-TEXT 
         
     | 
| 
      
 13 
     | 
    
         
            +
                  GET /1.1/statuses
         
     | 
| 
      
 14 
     | 
    
         
            +
                  Host: api.twitter.com
         
     | 
| 
      
 15 
     | 
    
         
            +
                  Accept: application/json
         
     | 
| 
      
 16 
     | 
    
         
            +
                TEXT
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
               then_I_get <<-REPLY 
         
     | 
| 
      
 19 
     | 
    
         
            +
                  HTTP/1.1 400 Bad Request
         
     | 
| 
      
 20 
     | 
    
         
            +
                  content-length: 24
         
     | 
| 
      
 21 
     | 
    
         
            +
                  content-type: text/plain;charset=utf-8
         
     | 
| 
      
 22 
     | 
    
         
            +
                  date: Sat, 30 Aug 2014 01:30:43 UTC
         
     | 
| 
      
 23 
     | 
    
         
            +
                  server: tsa_a
         
     | 
| 
      
 24 
     | 
    
         
            +
                  set-cookie: guest_id=v1%3A140936224322485447; Domain=.twitter.com; Path=/; Expires=Mon, 29-Aug-2016 01:30:43 UTC
         
     | 
| 
      
 25 
     | 
    
         
            +
                  strict-transport-security: max-age=631138519
         
     | 
| 
      
 26 
     | 
    
         
            +
                  x-connection-hash: 7710cb2762e0a47702d21bb7e10d3056
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  Bad Authentication data
         
     | 
| 
      
 29 
     | 
    
         
            +
                REPLY
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              private
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
              def when_I_request(text)
         
     | 
| 
      
 35 
     | 
    
         
            +
                fail "Not implemented"
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              def then_I_get(what)
         
     | 
| 
      
 39 
     | 
    
         
            +
                fail "Not implemented"
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
            end
         
     | 
| 
      
 42 
     | 
    
         
            +
            ```
         
     | 
    
        data/Rakefile
    CHANGED
    
    | 
         @@ -1,2 +1,17 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require "bundler/gem_tasks"
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            require 'rake/testtask'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            namespace :test do 
         
     | 
| 
      
 6 
     | 
    
         
            +
              %w{unit integration acceptance}.each do |name|
         
     | 
| 
      
 7 
     | 
    
         
            +
                Rake::TestTask.new do |t|
         
     | 
| 
      
 8 
     | 
    
         
            +
                  t.name = name		   
         
     | 
| 
      
 9 
     | 
    
         
            +
                  t.test_files = FileList["test/#{name}.tests/**/*.rb"]
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
              end	  
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              desc "Run all tests"
         
     | 
| 
      
 14 
     | 
    
         
            +
              task :all => ["test:unit", "test:integration", "test:acceptance"]
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            task :default => "test:unit"
         
     | 
| 
         @@ -0,0 +1,72 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Richard
         
     | 
| 
      
 2 
     | 
    
         
            +
              class CurlReply
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :missing, :surplus
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize(text)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @text = text
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @missing = @surplus = []
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @pretty = false
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def use_pretty_xml
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @pretty = true;
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def matches?(expected)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  actual_lines   = strip(@text)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  
         
     | 
| 
      
 18 
     | 
    
         
            +
                  expected_lines = parse(expected)
         
     | 
| 
      
 19 
     | 
    
         
            +
               
         
     | 
| 
      
 20 
     | 
    
         
            +
                  matches = actual_lines.select do |line|
         
     | 
| 
      
 21 
     | 
    
         
            +
                    expected_lines.any?{|it| it.matches?(line)}
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  expectations_that_did_not_match_anything = expected_lines.select do |expected|
         
     | 
| 
      
 25 
     | 
    
         
            +
                    actual_lines.none?{|line| expected.matches?(line)}
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  @missing = expectations_that_did_not_match_anything.map(&:text)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @surplus = actual_lines - matches
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  return matches.size.eql? expected_lines.size
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                def eql?(text)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  strip(@text).eql? strip(text)
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def equals?(text); self.eql? text; end
         
     | 
| 
      
 39 
     | 
    
         
            +
                def ==(text); self.eql? text; end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def to_s; @text; end
         
     | 
| 
      
 42 
     | 
    
         
            +
                def inspect; to_s; end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                private
         
     | 
| 
      
 45 
     | 
    
         
            +
               
         
     | 
| 
      
 46 
     | 
    
         
            +
                def parse(text)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  convert_all strip(text)
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                def strip(text)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  format(text).map(&:chomp).map(&:strip)
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
                
         
     | 
| 
      
 54 
     | 
    
         
            +
                def format(text)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  return text.lines unless @pretty
         
     | 
| 
      
 56 
     | 
    
         
            +
                 
         
     | 
| 
      
 57 
     | 
    
         
            +
                  lines = text.lines
         
     | 
| 
      
 58 
     | 
    
         
            +
                  
         
     | 
| 
      
 59 
     | 
    
         
            +
                  body = lines.delete_at(lines.size-1)
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  lines += Richard::Internal::XmlFormat.pretty(body)
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  lines
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                def convert_all(lines=[])
         
     | 
| 
      
 67 
     | 
    
         
            +
                  lines.map do |text| 
         
     | 
| 
      
 68 
     | 
    
         
            +
                    text.start_with?("/") ? Richard::Internal::PatternLine.new(text) : Richard::Internal::TextLine.new(text)
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
              end
         
     | 
| 
      
 72 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,68 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Richard
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Internal
         
     | 
| 
      
 3 
     | 
    
         
            +
                class BasicRequestLineParser
         
     | 
| 
      
 4 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 5 
     | 
    
         
            +
                    def from(text, headers)
         
     | 
| 
      
 6 
     | 
    
         
            +
                      lines = text.lines.map(&:chomp).map(&:strip)
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                      verb = lines.first.match(/^(\w+)/)[1]
         
     | 
| 
      
 9 
     | 
    
         
            +
                      path = lines.first.match(/(\S+)$/)[1]
         
     | 
| 
      
 10 
     | 
    
         
            +
                      host = headers["Host"]
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                      is_absolute = path.include?('://')
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                      fail "Missing host header. When you supply a relative earl you have to supply host header too." if !is_absolute && host.nil?
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                      earl = is_absolute ? path : "https://#{host}#{path}"
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                      RequestLine.new(verb, earl)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                class BasicHeaderParser
         
     | 
| 
      
 24 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 25 
     | 
    
         
            +
                    def from(text)
         
     | 
| 
      
 26 
     | 
    
         
            +
                      lines = text.lines.map(&:chomp).map(&:strip)
         
     | 
| 
      
 27 
     | 
    
         
            +
                      lines.drop(1).take_while{|line| !line.strip.empty?}.map do |line|
         
     | 
| 
      
 28 
     | 
    
         
            +
                        name,value = line.split(':')
         
     | 
| 
      
 29 
     | 
    
         
            +
                        RequestHeader.new name.strip, value.strip
         
     | 
| 
      
 30 
     | 
    
         
            +
                      end
         
     | 
| 
      
 31 
     | 
    
         
            +
                    end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                class BasicBodyParser
         
     | 
| 
      
 36 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 37 
     | 
    
         
            +
                    def from(text)
         
     | 
| 
      
 38 
     | 
    
         
            +
                      lines = text.lines.map(&:chomp).map(&:strip)
         
     | 
| 
      
 39 
     | 
    
         
            +
                      lines.drop_while{|line| !line.strip.empty?}.drop(1).first
         
     | 
| 
      
 40 
     | 
    
         
            +
                    end
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                class CurlFormat
         
     | 
| 
      
 45 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 46 
     | 
    
         
            +
                    def as_string(reply)
         
     | 
| 
      
 47 
     | 
    
         
            +
                      return '' if reply.nil?
         
     | 
| 
      
 48 
     | 
    
         
            +
                      CurlReply.new((
         
     | 
| 
      
 49 
     | 
    
         
            +
                        ["HTTP/1.1 #{reply.status}"] + 
         
     | 
| 
      
 50 
     | 
    
         
            +
                        headers_from(reply) + 
         
     | 
| 
      
 51 
     | 
    
         
            +
                        ["\n#{reply.body.strip}\n"]
         
     | 
| 
      
 52 
     | 
    
         
            +
                      ).join("\n"))
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                    private
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    def headers_from(reply)
         
     | 
| 
      
 58 
     | 
    
         
            +
                      reply.headers.map do |name, value|
         
     | 
| 
      
 59 
     | 
    
         
            +
                        "#{name}: #{value}"
         
     | 
| 
      
 60 
     | 
    
         
            +
                      end.to_a
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                RequestLine   = Struct.new 'RequestLine'  , :verb, :uri
         
     | 
| 
      
 66 
     | 
    
         
            +
                RequestHeader = Struct.new 'RequestHeader', :name, :value
         
     | 
| 
      
 67 
     | 
    
         
            +
              end
         
     | 
| 
      
 68 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Richard
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Internal
         
     | 
| 
      
 3 
     | 
    
         
            +
                class TextLine
         
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_reader :text
         
     | 
| 
      
 5 
     | 
    
         
            +
                  
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize(text)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    @text = text || fail("You need to supply some text even if it's empty")
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def matches?(text)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @text.eql?(text)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                class PatternLine
         
     | 
| 
      
 16 
     | 
    
         
            +
                  attr_reader :text
         
     | 
| 
      
 17 
     | 
    
         
            +
                  
         
     | 
| 
      
 18 
     | 
    
         
            +
                  def initialize(text)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    @text = text || fail("You need to supply some text even if it's empty")
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  # [!] expects lines to start and end with /
         
     | 
| 
      
 23 
     | 
    
         
            +
                  def matches?(text)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    regex = Regexp.new(@text.slice(1,(@text.size-2)))
         
     | 
| 
      
 25 
     | 
    
         
            +
                    false == regex.match(text).nil?
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Richard
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Internal
         
     | 
| 
      
 3 
     | 
    
         
            +
                class XmlFormat 
         
     | 
| 
      
 4 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 5 
     | 
    
         
            +
                    def pretty(text)
         
     | 
| 
      
 6 
     | 
    
         
            +
                      require 'nokogiri'
         
     | 
| 
      
 7 
     | 
    
         
            +
                
         
     | 
| 
      
 8 
     | 
    
         
            +
                      doc = Nokogiri.XML(text) do |config|
         
     | 
| 
      
 9 
     | 
    
         
            +
                        config.default_xml.noblanks
         
     | 
| 
      
 10 
     | 
    
         
            +
                      end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                      doc.to_xml(:indent => 2).lines.drop(1)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    end
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,33 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Richard
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Request
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :verb, :uri, :headers, :body
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize(opts={})
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @verb,@uri,@headers,@body = opts[:verb],opts[:uri],opts[:headers],opts[:body]
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def eql?(other)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  self.verb.eql?(other.verb) && self.uri.eql?(other.uri) && self.headers == other.headers && self.body.eql?(other.body)
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              class Response
         
     | 
| 
      
 15 
     | 
    
         
            +
                attr_reader :status, :headers, :body
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def initialize(opts={})
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @status,@headers,@body = opts[:status],opts[:headers],opts[:body]
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def eql?(other)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  self.status.eql?(other.status) && self.headers == other.headers && self.body.eql?(other.body)
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def each_header(&block)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  self.headers.each_pair{|k,v| block.call(k,v)}
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              Status = Struct.new 'Status', :code, :desc do 
         
     | 
| 
      
 31 
     | 
    
         
            +
                def to_s; "#{code} #{desc}"; end
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/richard_iii/version.rb
    CHANGED
    
    
    
        data/lib/richard_iii.rb
    CHANGED
    
    | 
         @@ -1,5 +1,48 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            module Richard; end
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
      
 3 
     | 
    
         
            +
            dir = File.join(File.dirname(__FILE__))
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            $LOAD_PATH.unshift File.join(dir, 'richard_iii')
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            Dir.glob(File.join(dir, "richard_iii", "**", "*.rb")).each {|f| require f}
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            module Richard
         
     | 
| 
      
 10 
     | 
    
         
            +
              class III
         
     | 
| 
      
 11 
     | 
    
         
            +
                def initialize(opts={})
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @internet = opts[:internet] || fail("You need to supply :internet")
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def exec(text)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  request_line = request_line_from text
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  reply = @internet.execute(
         
     | 
| 
      
 19 
     | 
    
         
            +
                    Richard::Request.new(
         
     | 
| 
      
 20 
     | 
    
         
            +
                      :verb     => request_line.verb, 
         
     | 
| 
      
 21 
     | 
    
         
            +
                      :uri      => request_line.uri,
         
     | 
| 
      
 22 
     | 
    
         
            +
                      :headers  => headers_from(text),
         
     | 
| 
      
 23 
     | 
    
         
            +
                      :body     => body_from(text)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    )
         
     | 
| 
      
 25 
     | 
    
         
            +
                  )
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  Internal::CurlFormat.as_string reply
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                private
         
     | 
| 
      
 31 
     | 
    
         
            +
                
         
     | 
| 
      
 32 
     | 
    
         
            +
                def request_line_from(text)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  Richard::Internal::BasicRequestLineParser.from text, headers_from(text)
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def headers_from(text)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  result = {}
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  Internal::BasicHeaderParser.from(text).each{|h| result[h.name] = h.value }
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  result
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def body_from(text)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  Internal::BasicBodyParser.from(text)
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
       5 
48 
     | 
    
         
             
            end
         
     | 
    
        data/richard_iii.gemspec
    CHANGED
    
    | 
         @@ -18,6 +18,8 @@ Gem::Specification.new do |spec| 
     | 
|
| 
       18 
18 
     | 
    
         
             
              spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
         
     | 
| 
       19 
19 
     | 
    
         
             
              spec.require_paths = ["lib"]
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
              spec. 
     | 
| 
      
 21 
     | 
    
         
            +
              spec.add_dependency "nokogiri"
         
     | 
| 
      
 22 
     | 
    
         
            +
              spec.add_development_dependency "bundler", "~> 1.6"
         
     | 
| 
       22 
23 
     | 
    
         
             
              spec.add_development_dependency "rake", "~> 10.0"
         
     | 
| 
      
 24 
     | 
    
         
            +
              spec.add_development_dependency "tinternet"
         
     | 
| 
       23 
25 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join '.', 'test', 'helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe "An end-to-end example" do
         
     | 
| 
      
 4 
     | 
    
         
            +
              it "lets me see raw text" do
         
     | 
| 
      
 5 
     | 
    
         
            +
                when_I_request <<-TEXT 
         
     | 
| 
      
 6 
     | 
    
         
            +
                  GET /1.1/statuses
         
     | 
| 
      
 7 
     | 
    
         
            +
                  Host: api.twitter.com
         
     | 
| 
      
 8 
     | 
    
         
            +
                  Accept: application/json
         
     | 
| 
      
 9 
     | 
    
         
            +
                TEXT
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
               then_I_get <<-REPLY 
         
     | 
| 
      
 12 
     | 
    
         
            +
                  HTTP/1.1 400 Bad Request
         
     | 
| 
      
 13 
     | 
    
         
            +
                  content-length: 24
         
     | 
| 
      
 14 
     | 
    
         
            +
                  content-type: text/plain;charset=utf-8
         
     | 
| 
      
 15 
     | 
    
         
            +
                  date: Sat, 30 Aug 2014 01:30:43 UTC
         
     | 
| 
      
 16 
     | 
    
         
            +
                  server: tsa_a
         
     | 
| 
      
 17 
     | 
    
         
            +
                  set-cookie: guest_id=v1%3A140936224322485447; Domain=.twitter.com; Path=/; Expires=Mon, 29-Aug-2016 01:30:43 UTC
         
     | 
| 
      
 18 
     | 
    
         
            +
                  strict-transport-security: max-age=631138519
         
     | 
| 
      
 19 
     | 
    
         
            +
                  x-connection-hash: 7710cb2762e0a47702d21bb7e10d3056
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  Bad Authentication data
         
     | 
| 
      
 22 
     | 
    
         
            +
                REPLY
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              private
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              def when_I_request(text)
         
     | 
| 
      
 28 
     | 
    
         
            +
                fail "Not implemented"
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              def then_I_get(what)
         
     | 
| 
      
 32 
     | 
    
         
            +
                fail "Not implemented"
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
    
        data/test/helper.rb
    ADDED
    
    
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join '.', 'test', 'helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class SystemInternet
         
     | 
| 
      
 4 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 5 
     | 
    
         
            +
                def execute(request)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  require 't'
         
     | 
| 
      
 7 
     | 
    
         
            +
                  reply = T::Internet.new.execute(T::Request.new(:verb => :get, :uri => "http://api.twitter.com/1.1/statuses"))
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  Richard::Response.new :status => reply.code
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            describe "the internet adapter" do
         
     | 
| 
      
 15 
     | 
    
         
            +
              it "can be asked to execute a basic get" do
         
     | 
| 
      
 16 
     | 
    
         
            +
                reply = SystemInternet.execute Request.new(:verb => "GET", :uri => "http://api.twitter.com/1.1/statuses")
         
     | 
| 
      
 17 
     | 
    
         
            +
                reply.status.must_equal 400
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'minitest/unit'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Richard::CurlReply.class_eval do
         
     | 
| 
      
 4 
     | 
    
         
            +
              include MiniTest::Assertions
         
     | 
| 
      
 5 
     | 
    
         
            +
              
         
     | 
| 
      
 6 
     | 
    
         
            +
              def must_match(expected)
         
     | 
| 
      
 7 
     | 
    
         
            +
                assert(self.matches?(expected), "Expected <#{self}> to match <#{expected}>")
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              def must_not_match(expected)
         
     | 
| 
      
 11 
     | 
    
         
            +
                assert(false === self.matches?(expected), "Expected <#{self}> not to match <#{expected}>")
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class SpyInternet
         
     | 
| 
      
 2 
     | 
    
         
            +
              def initialize
         
     | 
| 
      
 3 
     | 
    
         
            +
                @requests = []
         
     | 
| 
      
 4 
     | 
    
         
            +
              end
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              def execute(request)
         
     | 
| 
      
 7 
     | 
    
         
            +
                @requests << request
         
     | 
| 
      
 8 
     | 
    
         
            +
                @response
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              def must_have_been_asked_to_execute(what)
         
     | 
| 
      
 12 
     | 
    
         
            +
                @requests.any?{|it| it.eql? what}.must_be :==, true, "Unable to locate match for:\n\n#{what.inspect}\n\nin:\n\n#{@requests.inspect}"
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              def always_return(response)
         
     | 
| 
      
 16 
     | 
    
         
            +
                @response = response
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,46 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join '.', 'test', 'helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe "Full conversations" do 
         
     | 
| 
      
 4 
     | 
    
         
            +
              before do
         
     | 
| 
      
 5 
     | 
    
         
            +
                @internet = SpyInternet.new
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                @internet.always_return(
         
     | 
| 
      
 8 
     | 
    
         
            +
                  Response.new(
         
     | 
| 
      
 9 
     | 
    
         
            +
                    :status  => Status.new(400, 'Bad Request'),
         
     | 
| 
      
 10 
     | 
    
         
            +
                    :headers => {
         
     | 
| 
      
 11 
     | 
    
         
            +
                       'content-length'            => 24,
         
     | 
| 
      
 12 
     | 
    
         
            +
                       'content-type'              => 'text/plain;charset=utf-8',
         
     | 
| 
      
 13 
     | 
    
         
            +
                       'date'                      => 'Sat, 30 Aug 2014 01:30:43 UTC',
         
     | 
| 
      
 14 
     | 
    
         
            +
                       'server'                    => 'tsa_a',
         
     | 
| 
      
 15 
     | 
    
         
            +
                       'set-cookie'                => 'guest_id=v1%3A140936224322485447; Domain=.twitter.com; Path=/; Expires=Mon, 29-Aug-2016 01:30:43 UTC',
         
     | 
| 
      
 16 
     | 
    
         
            +
                       'strict-transport-security' => 'max-age=631138519',
         
     | 
| 
      
 17 
     | 
    
         
            +
                       'x-connection-hash'         => '7710cb2762e0a47702d21bb7e10d3056'
         
     | 
| 
      
 18 
     | 
    
         
            +
                    },
         
     | 
| 
      
 19 
     | 
    
         
            +
                    :body => 'Bad Authentication data'
         
     | 
| 
      
 20 
     | 
    
         
            +
                  )
         
     | 
| 
      
 21 
     | 
    
         
            +
                )
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                richard_iii = Richard::III.new :internet => @internet
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                @reply = richard_iii.exec <<-TEXT 
         
     | 
| 
      
 26 
     | 
    
         
            +
                  GET /1.1/statuses
         
     | 
| 
      
 27 
     | 
    
         
            +
                  Host: api.twitter.com
         
     | 
| 
      
 28 
     | 
    
         
            +
                  Accept: application/json
         
     | 
| 
      
 29 
     | 
    
         
            +
                TEXT
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              it "can be used to conduct a conversation" do
         
     | 
| 
      
 33 
     | 
    
         
            +
                assert_equal @reply, <<-REPLY 
         
     | 
| 
      
 34 
     | 
    
         
            +
                  HTTP/1.1 400 Bad Request
         
     | 
| 
      
 35 
     | 
    
         
            +
                  content-length: 24
         
     | 
| 
      
 36 
     | 
    
         
            +
                  content-type: text/plain;charset=utf-8
         
     | 
| 
      
 37 
     | 
    
         
            +
                  date: Sat, 30 Aug 2014 01:30:43 UTC
         
     | 
| 
      
 38 
     | 
    
         
            +
                  server: tsa_a
         
     | 
| 
      
 39 
     | 
    
         
            +
                  set-cookie: guest_id=v1%3A140936224322485447; Domain=.twitter.com; Path=/; Expires=Mon, 29-Aug-2016 01:30:43 UTC
         
     | 
| 
      
 40 
     | 
    
         
            +
                  strict-transport-security: max-age=631138519
         
     | 
| 
      
 41 
     | 
    
         
            +
                  x-connection-hash: 7710cb2762e0a47702d21bb7e10d3056
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  Bad Authentication data
         
     | 
| 
      
 44 
     | 
    
         
            +
                REPLY
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,69 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join '.', 'test', 'helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe "Finding out why a match failed" do 
         
     | 
| 
      
 4 
     | 
    
         
            +
              before do
         
     | 
| 
      
 5 
     | 
    
         
            +
                @internet = SpyInternet.new
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                @internet.always_return(
         
     | 
| 
      
 8 
     | 
    
         
            +
                  Response.new(
         
     | 
| 
      
 9 
     | 
    
         
            +
                    :status  => Status.new(400, 'Bad Request'),
         
     | 
| 
      
 10 
     | 
    
         
            +
                    :headers => {
         
     | 
| 
      
 11 
     | 
    
         
            +
                       'content-length'            => 24,
         
     | 
| 
      
 12 
     | 
    
         
            +
                       'content-type'              => 'text/plain;charset=utf-8',
         
     | 
| 
      
 13 
     | 
    
         
            +
                       'date'                      => 'Sat, 30 Aug 2014 01:30:43 UTC',
         
     | 
| 
      
 14 
     | 
    
         
            +
                       'server'                    => 'tsa_a',
         
     | 
| 
      
 15 
     | 
    
         
            +
                       'set-cookie'                => 'guest_id=v1%3A140936224322485447; Domain=.twitter.com; Path=/; Expires=Mon, 29-Aug-2016 01:30:43 UTC',
         
     | 
| 
      
 16 
     | 
    
         
            +
                       'strict-transport-security' => 'max-age=631138519',
         
     | 
| 
      
 17 
     | 
    
         
            +
                       'x-connection-hash'         => '7710cb2762e0a47702d21bb7e10d3056'
         
     | 
| 
      
 18 
     | 
    
         
            +
                    },
         
     | 
| 
      
 19 
     | 
    
         
            +
                    :body => 'Bad Authentication data'
         
     | 
| 
      
 20 
     | 
    
         
            +
                  )
         
     | 
| 
      
 21 
     | 
    
         
            +
                )
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                richard_iii = Richard::III.new :internet => @internet
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                @reply = richard_iii.exec <<-TEXT 
         
     | 
| 
      
 26 
     | 
    
         
            +
                  GET /1.1/statuses
         
     | 
| 
      
 27 
     | 
    
         
            +
                  Host: api.twitter.com
         
     | 
| 
      
 28 
     | 
    
         
            +
                  Accept: application/json
         
     | 
| 
      
 29 
     | 
    
         
            +
                TEXT
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              it "tells you which expected lines are missing" do
         
     | 
| 
      
 33 
     | 
    
         
            +
                expected = <<-REPLY 
         
     | 
| 
      
 34 
     | 
    
         
            +
                  HTTP/1.1 400 Bad Request
         
     | 
| 
      
 35 
     | 
    
         
            +
                  content-type: text/plain;charset=utf-8
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  xxx_body_differs_xxx
         
     | 
| 
      
 38 
     | 
    
         
            +
                REPLY
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                @reply.must_not_match expected
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                @reply.missing.must_equal ['xxx_body_differs_xxx']
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
              it "tells you which lines are present that were not expected, i.e., are surplus" do
         
     | 
| 
      
 46 
     | 
    
         
            +
                expected = <<-REPLY 
         
     | 
| 
      
 47 
     | 
    
         
            +
                  HTTP/1.1 400 Bad Request
         
     | 
| 
      
 48 
     | 
    
         
            +
                REPLY
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                @reply.matches? expected
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                @reply.surplus.must_equal [
         
     | 
| 
      
 53 
     | 
    
         
            +
                  "content-length: 24", 
         
     | 
| 
      
 54 
     | 
    
         
            +
                  "content-type: text/plain;charset=utf-8", 
         
     | 
| 
      
 55 
     | 
    
         
            +
                  "date: Sat, 30 Aug 2014 01:30:43 UTC",
         
     | 
| 
      
 56 
     | 
    
         
            +
                  "server: tsa_a", 
         
     | 
| 
      
 57 
     | 
    
         
            +
                  "set-cookie: guest_id=v1%3A140936224322485447; Domain=.twitter.com; Path=/; Expires=Mon, 29-Aug-2016 01:30:43 UTC", 
         
     | 
| 
      
 58 
     | 
    
         
            +
                  "strict-transport-security: max-age=631138519", 
         
     | 
| 
      
 59 
     | 
    
         
            +
                  "x-connection-hash: 7710cb2762e0a47702d21bb7e10d3056", 
         
     | 
| 
      
 60 
     | 
    
         
            +
                  "", 
         
     | 
| 
      
 61 
     | 
    
         
            +
                  "Bad Authentication data"
         
     | 
| 
      
 62 
     | 
    
         
            +
                ]
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
              it "both surplus and missing are empty before matching" do
         
     | 
| 
      
 66 
     | 
    
         
            +
                @reply.missing.must_equal []
         
     | 
| 
      
 67 
     | 
    
         
            +
                @reply.surplus.must_equal []
         
     | 
| 
      
 68 
     | 
    
         
            +
              end
         
     | 
| 
      
 69 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,36 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join '.', 'test', 'helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe "Matching pretty-formatted bodies" do
         
     | 
| 
      
 4 
     | 
    
         
            +
              before do
         
     | 
| 
      
 5 
     | 
    
         
            +
                @curl_reply = CurlReply.new <<-TEXT
         
     | 
| 
      
 6 
     | 
    
         
            +
                  HTTP/1.1 200 OK
         
     | 
| 
      
 7 
     | 
    
         
            +
                  Content-Type: text/xml; charset=utf-8
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  <?xml version="1.0" encoding="utf-8"?><current><city id="6058560" name="London" /><humidity value="76" unit="%"/><pressure value="1018" unit="hPa"/></current>
         
     | 
| 
      
 10 
     | 
    
         
            +
                TEXT
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              it "matches when you have neither pretty formatted" do
         
     | 
| 
      
 14 
     | 
    
         
            +
                @curl_reply.must_match <<-TEXT
         
     | 
| 
      
 15 
     | 
    
         
            +
                  HTTP/1.1 200 OK
         
     | 
| 
      
 16 
     | 
    
         
            +
                  Content-Type: text/xml; charset=utf-8
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  <?xml version="1.0" encoding="utf-8"?><current><city id="6058560" name="London" /><humidity value="76" unit="%"/><pressure value="1018" unit="hPa"/></current>
         
     | 
| 
      
 19 
     | 
    
         
            +
                TEXT
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              it "matches when you say yes to pretty bodies" do
         
     | 
| 
      
 23 
     | 
    
         
            +
                @curl_reply.use_pretty_xml
         
     | 
| 
      
 24 
     | 
    
         
            +
                @curl_reply.must_match <<-TEXT
         
     | 
| 
      
 25 
     | 
    
         
            +
                  HTTP/1.1 200 OK
         
     | 
| 
      
 26 
     | 
    
         
            +
                  Content-Type: text/xml; charset=utf-8
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  <?xml version="1.0" encoding="utf-8"?>
         
     | 
| 
      
 29 
     | 
    
         
            +
                  <current>
         
     | 
| 
      
 30 
     | 
    
         
            +
                    <city id="6058560" name="London"/>
         
     | 
| 
      
 31 
     | 
    
         
            +
                    <humidity value="76" unit="%"/>
         
     | 
| 
      
 32 
     | 
    
         
            +
                    <pressure value="1018" unit="hPa"/>
         
     | 
| 
      
 33 
     | 
    
         
            +
                  </current>
         
     | 
| 
      
 34 
     | 
    
         
            +
                TEXT
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,75 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join '.', 'test', 'helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe "Partial matching replies" do 
         
     | 
| 
      
 4 
     | 
    
         
            +
              before do
         
     | 
| 
      
 5 
     | 
    
         
            +
                @internet = SpyInternet.new
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                @internet.always_return(
         
     | 
| 
      
 8 
     | 
    
         
            +
                  Response.new(
         
     | 
| 
      
 9 
     | 
    
         
            +
                    :status  => Status.new(400, 'Bad Request'),
         
     | 
| 
      
 10 
     | 
    
         
            +
                    :headers => {
         
     | 
| 
      
 11 
     | 
    
         
            +
                       'content-length'            => 24,
         
     | 
| 
      
 12 
     | 
    
         
            +
                       'content-type'              => 'text/plain;charset=utf-8',
         
     | 
| 
      
 13 
     | 
    
         
            +
                       'date'                      => 'Sat, 30 Aug 2014 01:30:43 UTC',
         
     | 
| 
      
 14 
     | 
    
         
            +
                       'server'                    => 'tsa_a',
         
     | 
| 
      
 15 
     | 
    
         
            +
                       'set-cookie'                => 'guest_id=v1%3A140936224322485447; Domain=.twitter.com; Path=/; Expires=Mon, 29-Aug-2016 01:30:43 UTC',
         
     | 
| 
      
 16 
     | 
    
         
            +
                       'strict-transport-security' => 'max-age=631138519',
         
     | 
| 
      
 17 
     | 
    
         
            +
                       'x-connection-hash'         => '7710cb2762e0a47702d21bb7e10d3056'
         
     | 
| 
      
 18 
     | 
    
         
            +
                    },
         
     | 
| 
      
 19 
     | 
    
         
            +
                    :body => 'Bad Authentication data'
         
     | 
| 
      
 20 
     | 
    
         
            +
                  )
         
     | 
| 
      
 21 
     | 
    
         
            +
                )
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                richard_iii = Richard::III.new :internet => @internet
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                @reply = richard_iii.exec <<-TEXT 
         
     | 
| 
      
 26 
     | 
    
         
            +
                  GET /1.1/statuses
         
     | 
| 
      
 27 
     | 
    
         
            +
                  Host: api.twitter.com
         
     | 
| 
      
 28 
     | 
    
         
            +
                  Accept: application/json
         
     | 
| 
      
 29 
     | 
    
         
            +
                TEXT
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              it "can match partially, so that you can ignore headers like date" do
         
     | 
| 
      
 33 
     | 
    
         
            +
                expected = <<-REPLY 
         
     | 
| 
      
 34 
     | 
    
         
            +
                  HTTP/1.1 400 Bad Request
         
     | 
| 
      
 35 
     | 
    
         
            +
                  content-type: text/plain;charset=utf-8
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  Bad Authentication data
         
     | 
| 
      
 38 
     | 
    
         
            +
                REPLY
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                @reply.must_match expected
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
              it "fails when the response line does not match" do
         
     | 
| 
      
 44 
     | 
    
         
            +
                expected = <<-REPLY 
         
     | 
| 
      
 45 
     | 
    
         
            +
                  HTTP/1.1 XXX_BAD_RESPONSE_LINE
         
     | 
| 
      
 46 
     | 
    
         
            +
                  content-type: text/plain;charset=utf-8
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  Bad Authentication data
         
     | 
| 
      
 49 
     | 
    
         
            +
                REPLY
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                @reply.must_not_match expected
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
              it "fails, for example, when you expected a header that is not present" do
         
     | 
| 
      
 55 
     | 
    
         
            +
                expected = <<-REPLY 
         
     | 
| 
      
 56 
     | 
    
         
            +
                  HTTP/1.1 400 Bad Request
         
     | 
| 
      
 57 
     | 
    
         
            +
                  xxx: this_one_is_not_present
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  Bad Authentication data
         
     | 
| 
      
 60 
     | 
    
         
            +
                REPLY
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                @reply.must_not_match expected
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
              it "fails when the body does not match" do
         
     | 
| 
      
 66 
     | 
    
         
            +
                expected = <<-REPLY 
         
     | 
| 
      
 67 
     | 
    
         
            +
                  HTTP/1.1 400 Bad Request
         
     | 
| 
      
 68 
     | 
    
         
            +
                  content-type: text/plain;charset=utf-8
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                  Who says famine has to be depressing
         
     | 
| 
      
 71 
     | 
    
         
            +
                REPLY
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                @reply.must_not_match expected
         
     | 
| 
      
 74 
     | 
    
         
            +
              end
         
     | 
| 
      
 75 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join '.', 'test', 'helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe "Pattern matching on reply lines" do
         
     | 
| 
      
 4 
     | 
    
         
            +
              before do
         
     | 
| 
      
 5 
     | 
    
         
            +
                @curl_reply = CurlReply.new <<-TEXT
         
     | 
| 
      
 6 
     | 
    
         
            +
                  HTTP/1.1 400 Bad Request
         
     | 
| 
      
 7 
     | 
    
         
            +
                  
         
     | 
| 
      
 8 
     | 
    
         
            +
                  content-type: text/plain;charset=utf-8
         
     | 
| 
      
 9 
     | 
    
         
            +
                  date: Sat, 30 Aug 2014 01:30:43 UTC
         
     | 
| 
      
 10 
     | 
    
         
            +
                  server: tsa_a
         
     | 
| 
      
 11 
     | 
    
         
            +
                  set-cookie: guest_id=v1%3A140936224322485447; Domain=.twitter.com; Path=/; Expires=Mon, 29-Aug-2016 01:30:43 UTC
         
     | 
| 
      
 12 
     | 
    
         
            +
                  strict-transport-security: max-age=631138519
         
     | 
| 
      
 13 
     | 
    
         
            +
                  x-connection-hash: 7710cb2762e0a47702d21bb7e10d3056
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  Bad Authentication data
         
     | 
| 
      
 16 
     | 
    
         
            +
                TEXT
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              it "allows you to match part of a dynamic header, for example" do
         
     | 
| 
      
 20 
     | 
    
         
            +
                @curl_reply.must_match <<-REPLY 
         
     | 
| 
      
 21 
     | 
    
         
            +
                  HTTP/1.1 400 Bad Request
         
     | 
| 
      
 22 
     | 
    
         
            +
                  content-type: text/plain;charset=utf-8
         
     | 
| 
      
 23 
     | 
    
         
            +
                  /set-cookie: .+Domain=.twitter.com; Path=\/;/
         
     | 
| 
      
 24 
     | 
    
         
            +
                REPLY
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              it "another example might be /content-length: \d+/" do 
         
     | 
| 
      
 28 
     | 
    
         
            +
                skip("WIP: for some reason the backslash is being dropped")
         
     | 
| 
      
 29 
     | 
    
         
            +
                @curl_reply.must_match <<-REPLY 
         
     | 
| 
      
 30 
     | 
    
         
            +
                  HTTP/1.1 400 Bad Request
         
     | 
| 
      
 31 
     | 
    
         
            +
                  content-type: text/plain;charset=utf-8
         
     | 
| 
      
 32 
     | 
    
         
            +
                  /content-length: \d+/
         
     | 
| 
      
 33 
     | 
    
         
            +
                REPLY
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              it "how are we going to fail in a way that tells you a pattern match failed?"
         
     | 
| 
      
 37 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join '.', 'test', 'helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe "Request earls may be absolute or relative" do 
         
     | 
| 
      
 4 
     | 
    
         
            +
              it "must have host header when relative" do
         
     | 
| 
      
 5 
     | 
    
         
            +
                spy_internet = SpyInternet.new
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                richard_iii = Richard::III.new :internet => spy_internet
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                err = lambda { richard_iii.exec 'GET /1.1/statuses' }.must_raise RuntimeError
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                err.message.must_match /missing host header/i
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              it "ignores host header when earl is absolute -- that is it does not send it"
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,59 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join '.', 'test', 'helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe 'The basics of Richard III' do
         
     | 
| 
      
 4 
     | 
    
         
            +
              it "can issue a very simple GET request" do
         
     | 
| 
      
 5 
     | 
    
         
            +
                spy_internet = SpyInternet.new
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                richard_iii = Richard::III.new :internet => spy_internet
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                richard_iii.exec <<-TEXT 
         
     | 
| 
      
 10 
     | 
    
         
            +
                  GET /1.1/statuses
         
     | 
| 
      
 11 
     | 
    
         
            +
                  Host: api.twitter.com
         
     | 
| 
      
 12 
     | 
    
         
            +
                  Accept: application/json
         
     | 
| 
      
 13 
     | 
    
         
            +
                TEXT
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                spy_internet.must_have_been_asked_to_execute(
         
     | 
| 
      
 16 
     | 
    
         
            +
                  Request.new(
         
     | 
| 
      
 17 
     | 
    
         
            +
                    :verb    => 'GET', 
         
     | 
| 
      
 18 
     | 
    
         
            +
                    :uri     => 'https://api.twitter.com/1.1/statuses',
         
     | 
| 
      
 19 
     | 
    
         
            +
                    :headers => {
         
     | 
| 
      
 20 
     | 
    
         
            +
                       'Host'   => 'api.twitter.com',
         
     | 
| 
      
 21 
     | 
    
         
            +
                       'Accept' => 'application/json'
         
     | 
| 
      
 22 
     | 
    
         
            +
                    }
         
     | 
| 
      
 23 
     | 
    
         
            +
                  )
         
     | 
| 
      
 24 
     | 
    
         
            +
                )
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              it "can issue a very simple POST request" do
         
     | 
| 
      
 28 
     | 
    
         
            +
                spy_internet = SpyInternet.new
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                richard_iii = Richard::III.new :internet => spy_internet
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                richard_iii.exec <<-TEXT 
         
     | 
| 
      
 33 
     | 
    
         
            +
                  POST /1.1/statuses/update
         
     | 
| 
      
 34 
     | 
    
         
            +
                  Host: api.twitter.com
         
     | 
| 
      
 35 
     | 
    
         
            +
                  Accept: application/json
         
     | 
| 
      
 36 
     | 
    
         
            +
                  Content-type: application/x-www-form-urlencoded
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  status=Who%20says%20famine%20has%20to%20be%20depressing?
         
     | 
| 
      
 39 
     | 
    
         
            +
                TEXT
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                spy_internet.must_have_been_asked_to_execute(
         
     | 
| 
      
 42 
     | 
    
         
            +
                  Request.new(
         
     | 
| 
      
 43 
     | 
    
         
            +
                    :verb    => 'POST', 
         
     | 
| 
      
 44 
     | 
    
         
            +
                    :uri     => 'https://api.twitter.com/1.1/statuses/update',
         
     | 
| 
      
 45 
     | 
    
         
            +
                    :headers => {
         
     | 
| 
      
 46 
     | 
    
         
            +
                       'Host'         => 'api.twitter.com',
         
     | 
| 
      
 47 
     | 
    
         
            +
                       'Accept'       => 'application/json',
         
     | 
| 
      
 48 
     | 
    
         
            +
                       'Content-type' => 'application/x-www-form-urlencoded'
         
     | 
| 
      
 49 
     | 
    
         
            +
                    },
         
     | 
| 
      
 50 
     | 
    
         
            +
                    :body => 'status=Who%20says%20famine%20has%20to%20be%20depressing?'
         
     | 
| 
      
 51 
     | 
    
         
            +
                  )
         
     | 
| 
      
 52 
     | 
    
         
            +
                )
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
              # TEST: quotes are treated literally
         
     | 
| 
      
 56 
     | 
    
         
            +
              # TEST: whitespace does not matter
         
     | 
| 
      
 57 
     | 
    
         
            +
              # TEST: where does it read the protocol part (HTTP or HTTPS)
         
     | 
| 
      
 58 
     | 
    
         
            +
              # TEST: looks like you can either supply absolute uri, or relative AND Host header
         
     | 
| 
      
 59 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,29 +1,43 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: richard_iii
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0.0 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.1.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Ben Biddington
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2014- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2014-09-16 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
      
 13 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 14 
     | 
    
         
            +
              name: nokogiri
         
     | 
| 
      
 15 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 16 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 17 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 18 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 19 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 20 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 21 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 22 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 23 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 24 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 25 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 26 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
       13 
27 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
28 
     | 
    
         
             
              name: bundler
         
     | 
| 
       15 
29 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
       16 
30 
     | 
    
         
             
                requirements:
         
     | 
| 
       17 
31 
     | 
    
         
             
                - - "~>"
         
     | 
| 
       18 
32 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       19 
     | 
    
         
            -
                    version: '1. 
     | 
| 
      
 33 
     | 
    
         
            +
                    version: '1.6'
         
     | 
| 
       20 
34 
     | 
    
         
             
              type: :development
         
     | 
| 
       21 
35 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       22 
36 
     | 
    
         
             
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
       23 
37 
     | 
    
         
             
                requirements:
         
     | 
| 
       24 
38 
     | 
    
         
             
                - - "~>"
         
     | 
| 
       25 
39 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       26 
     | 
    
         
            -
                    version: '1. 
     | 
| 
      
 40 
     | 
    
         
            +
                    version: '1.6'
         
     | 
| 
       27 
41 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       28 
42 
     | 
    
         
             
              name: rake
         
     | 
| 
       29 
43 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -38,6 +52,20 @@ dependencies: 
     | 
|
| 
       38 
52 
     | 
    
         
             
                - - "~>"
         
     | 
| 
       39 
53 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       40 
54 
     | 
    
         
             
                    version: '10.0'
         
     | 
| 
      
 55 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 56 
     | 
    
         
            +
              name: tinternet
         
     | 
| 
      
 57 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 58 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 59 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 60 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 61 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 62 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 63 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 64 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 65 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 66 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 67 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 68 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
       41 
69 
     | 
    
         
             
            description: ''
         
     | 
| 
       42 
70 
     | 
    
         
             
            email:
         
     | 
| 
       43 
71 
     | 
    
         
             
            - ben.biddington@gmail.com
         
     | 
| 
         @@ -46,15 +74,32 @@ extensions: [] 
     | 
|
| 
       46 
74 
     | 
    
         
             
            extra_rdoc_files: []
         
     | 
| 
       47 
75 
     | 
    
         
             
            files:
         
     | 
| 
       48 
76 
     | 
    
         
             
            - ".gitignore"
         
     | 
| 
      
 77 
     | 
    
         
            +
            - ".travis.yml"
         
     | 
| 
       49 
78 
     | 
    
         
             
            - Gemfile
         
     | 
| 
       50 
79 
     | 
    
         
             
            - LICENSE
         
     | 
| 
       51 
80 
     | 
    
         
             
            - LICENSE.txt
         
     | 
| 
       52 
81 
     | 
    
         
             
            - README.md
         
     | 
| 
       53 
82 
     | 
    
         
             
            - Rakefile
         
     | 
| 
       54 
83 
     | 
    
         
             
            - lib/richard_iii.rb
         
     | 
| 
      
 84 
     | 
    
         
            +
            - lib/richard_iii/curl_reply.rb
         
     | 
| 
      
 85 
     | 
    
         
            +
            - lib/richard_iii/internal/curl.rb
         
     | 
| 
      
 86 
     | 
    
         
            +
            - lib/richard_iii/internal/text_line.rb
         
     | 
| 
      
 87 
     | 
    
         
            +
            - lib/richard_iii/internal/xml_format.rb
         
     | 
| 
      
 88 
     | 
    
         
            +
            - lib/richard_iii/request_and_response.rb
         
     | 
| 
       55 
89 
     | 
    
         
             
            - lib/richard_iii/version.rb
         
     | 
| 
       56 
90 
     | 
    
         
             
            - richard_iii.gemspec
         
     | 
| 
       57 
     | 
    
         
            -
            - test/ 
     | 
| 
      
 91 
     | 
    
         
            +
            - test/acceptance.tests/an_example.rb
         
     | 
| 
      
 92 
     | 
    
         
            +
            - test/helper.rb
         
     | 
| 
      
 93 
     | 
    
         
            +
            - test/integration.tests/adapters/internet.rb
         
     | 
| 
      
 94 
     | 
    
         
            +
            - test/support/extensions.rb
         
     | 
| 
      
 95 
     | 
    
         
            +
            - test/support/spy_internet.rb
         
     | 
| 
      
 96 
     | 
    
         
            +
            - test/unit.tests/can_conduct_conversations.rb
         
     | 
| 
      
 97 
     | 
    
         
            +
            - test/unit.tests/can_find_out_why_a_match_failed.rb
         
     | 
| 
      
 98 
     | 
    
         
            +
            - test/unit.tests/can_match_pretty_formatted_xml_bodies.rb
         
     | 
| 
      
 99 
     | 
    
         
            +
            - test/unit.tests/can_partial_match_on_replies.rb
         
     | 
| 
      
 100 
     | 
    
         
            +
            - test/unit.tests/can_pattern_match_replies.rb
         
     | 
| 
      
 101 
     | 
    
         
            +
            - test/unit.tests/earls_may_be_relative_or_absolute.rb
         
     | 
| 
      
 102 
     | 
    
         
            +
            - test/unit.tests/the_basics.rb
         
     | 
| 
       58 
103 
     | 
    
         
             
            homepage: ''
         
     | 
| 
       59 
104 
     | 
    
         
             
            licenses:
         
     | 
| 
       60 
105 
     | 
    
         
             
            - MIT
         
     | 
| 
         @@ -80,4 +125,15 @@ signing_key: 
     | 
|
| 
       80 
125 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       81 
126 
     | 
    
         
             
            summary: ''
         
     | 
| 
       82 
127 
     | 
    
         
             
            test_files:
         
     | 
| 
       83 
     | 
    
         
            -
            - test/ 
     | 
| 
      
 128 
     | 
    
         
            +
            - test/acceptance.tests/an_example.rb
         
     | 
| 
      
 129 
     | 
    
         
            +
            - test/helper.rb
         
     | 
| 
      
 130 
     | 
    
         
            +
            - test/integration.tests/adapters/internet.rb
         
     | 
| 
      
 131 
     | 
    
         
            +
            - test/support/extensions.rb
         
     | 
| 
      
 132 
     | 
    
         
            +
            - test/support/spy_internet.rb
         
     | 
| 
      
 133 
     | 
    
         
            +
            - test/unit.tests/can_conduct_conversations.rb
         
     | 
| 
      
 134 
     | 
    
         
            +
            - test/unit.tests/can_find_out_why_a_match_failed.rb
         
     | 
| 
      
 135 
     | 
    
         
            +
            - test/unit.tests/can_match_pretty_formatted_xml_bodies.rb
         
     | 
| 
      
 136 
     | 
    
         
            +
            - test/unit.tests/can_partial_match_on_replies.rb
         
     | 
| 
      
 137 
     | 
    
         
            +
            - test/unit.tests/can_pattern_match_replies.rb
         
     | 
| 
      
 138 
     | 
    
         
            +
            - test/unit.tests/earls_may_be_relative_or_absolute.rb
         
     | 
| 
      
 139 
     | 
    
         
            +
            - test/unit.tests/the_basics.rb
         
     | 
    
        data/test/the_basics.rb
    DELETED
    
    | 
         @@ -1,74 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'minitest/autorun'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'minitest/pride'
         
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
            module Richard
         
     | 
| 
       5 
     | 
    
         
            -
              module Internal
         
     | 
| 
       6 
     | 
    
         
            -
                class RequestLineParser
         
     | 
| 
       7 
     | 
    
         
            -
                  
         
     | 
| 
       8 
     | 
    
         
            -
                end
         
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
                class RequestLine
         
     | 
| 
       11 
     | 
    
         
            -
                  attr_reader :method, :uri
         
     | 
| 
       12 
     | 
    
         
            -
                  
         
     | 
| 
       13 
     | 
    
         
            -
                  def initialize(opts = {})
         
     | 
| 
       14 
     | 
    
         
            -
                    @method,@uri = opts[:method],opts[:uri]
         
     | 
| 
       15 
     | 
    
         
            -
                  end
         
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
                  def eql?(other)
         
     | 
| 
       18 
     | 
    
         
            -
                    return self.method == other.method && self.uri == other.uri
         
     | 
| 
       19 
     | 
    
         
            -
                  end
         
     | 
| 
       20 
     | 
    
         
            -
                end
         
     | 
| 
       21 
     | 
    
         
            -
              end
         
     | 
| 
       22 
     | 
    
         
            -
            end
         
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
            module Richard
         
     | 
| 
       25 
     | 
    
         
            -
              class III
         
     | 
| 
       26 
     | 
    
         
            -
                def initialize(opts={})
         
     | 
| 
       27 
     | 
    
         
            -
                  @internet = opts[:internet] || fail("You need to supply :internet")
         
     | 
| 
       28 
     | 
    
         
            -
                end
         
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
                def exec(text)
         
     | 
| 
       31 
     | 
    
         
            -
                  lines = text.lines.map(&:chomp)
         
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
                  verb = lines.first.match(/^(\w+)/)[1]
         
     | 
| 
       34 
     | 
    
         
            -
                  path = lines.first.match(/(\S+)$/)[1]
         
     | 
| 
       35 
     | 
    
         
            -
                  host = lines[1].match(/Host: (.+)$/)[1]
         
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
                  @internet.execute Request.new(:verb => verb, :uri => "https://#{host}#{path}")
         
     | 
| 
       38 
     | 
    
         
            -
                end
         
     | 
| 
       39 
     | 
    
         
            -
              end
         
     | 
| 
       40 
     | 
    
         
            -
            end
         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
            class Request
         
     | 
| 
       43 
     | 
    
         
            -
              attr_reader :verb, :uri
         
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
              def initialize(opts={})
         
     | 
| 
       46 
     | 
    
         
            -
                @verb,@uri = opts[:verb],opts[:uri]
         
     | 
| 
       47 
     | 
    
         
            -
              end
         
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
              def eql?(other)
         
     | 
| 
       50 
     | 
    
         
            -
                self.verb.eql?(other.verb) && self.uri.eql?(other.uri)
         
     | 
| 
       51 
     | 
    
         
            -
              end
         
     | 
| 
       52 
     | 
    
         
            -
            end
         
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
            describe 'The basics of Richard III' do
         
     | 
| 
       55 
     | 
    
         
            -
              it "can issue a very simple request" do
         
     | 
| 
       56 
     | 
    
         
            -
                spy_internet = MiniTest::Mock.new
         
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
                spy_internet.expect :execute, nil do |actual|
         
     | 
| 
       59 
     | 
    
         
            -
                  actual.first.eql? Request.new(:verb => 'GET', :uri => 'https://api.twitter.com/1.1/statuses')
         
     | 
| 
       60 
     | 
    
         
            -
                end
         
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
                richard_iii = Richard::III.new :internet => spy_internet
         
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
                richard_iii.exec <<-TEXT 
         
     | 
| 
       65 
     | 
    
         
            -
            GET /1.1/statuses
         
     | 
| 
       66 
     | 
    
         
            -
            Host: api.twitter.com
         
     | 
| 
       67 
     | 
    
         
            -
            Accept: application/json
         
     | 
| 
       68 
     | 
    
         
            -
                TEXT
         
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
                spy_internet.verify
         
     | 
| 
       71 
     | 
    
         
            -
              end
         
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
              # TEST: where does it read the protocol part (HTTP of HTTPS)
         
     | 
| 
       74 
     | 
    
         
            -
            end
         
     |