gopher2000 0.2.2 → 0.5.5
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 +7 -0
- data/.ruby-version +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +1 -16
- data/README.markdown +3 -1
- data/bin/gopher2000 +1 -1
- data/examples/default_route.rb +0 -0
- data/examples/figlet.rb +28 -0
- data/examples/nyan.rb +0 -0
- data/examples/simple.rb +2 -2
- data/examples/twitter.rb +0 -0
- data/examples/weather.rb +0 -0
- data/gopher2000.gemspec +5 -5
- data/lib/gopher2000.rb +1 -1
- data/lib/gopher2000/base.rb +83 -37
- data/lib/gopher2000/dispatcher.rb +13 -4
- data/lib/gopher2000/dsl.rb +1 -1
- data/lib/gopher2000/handlers/directory_handler.rb +5 -7
- data/lib/gopher2000/rendering/base.rb +42 -1
- data/lib/gopher2000/rendering/menu.rb +104 -15
- data/lib/gopher2000/request.rb +13 -2
- data/lib/gopher2000/server.rb +11 -4
- data/lib/gopher2000/version.rb +1 -1
- data/spec/application_spec.rb +12 -12
- data/spec/dispatching_spec.rb +38 -17
- data/spec/dsl_spec.rb +76 -67
- data/spec/handlers/directory_handler_spec.rb +35 -35
- data/spec/helpers_spec.rb +1 -1
- data/spec/rendering/base_spec.rb +16 -9
- data/spec/rendering/menu_spec.rb +40 -18
- data/spec/rendering_spec.rb +7 -7
- data/spec/request_spec.rb +21 -10
- data/spec/response_spec.rb +5 -5
- data/spec/routing_spec.rb +21 -21
- data/spec/server_spec.rb +23 -7
- metadata +117 -65
- data/.rvmrc +0 -1
    
        data/lib/gopher2000/dsl.rb
    CHANGED
    
    
| @@ -27,7 +27,7 @@ module Gopher | |
| 27 27 |  | 
| 28 28 | 
             
                  #
         | 
| 29 29 | 
             
                  # strip slashes, extra dots, etc, from an incoming selector and turn it into a 'normalized' path
         | 
| 30 | 
            -
                  # @param [String] path
         | 
| 30 | 
            +
                  # @param [String] p path to check
         | 
| 31 31 | 
             
                  # @return clean path string
         | 
| 32 32 | 
             
                  #
         | 
| 33 33 | 
             
                  def sanitize(p)
         | 
| @@ -65,12 +65,10 @@ module Gopher | |
| 65 65 | 
             
                  #
         | 
| 66 66 | 
             
                  # handle a request
         | 
| 67 67 | 
             
                  #
         | 
| 68 | 
            -
                  # @param [Hash] the params as parsed during the dispatching process - the main thing here should be :splat, which will basically be the path requested.
         | 
| 69 | 
            -
                  # @param [Request]  | 
| 68 | 
            +
                  # @param [Hash] params the params as parsed during the dispatching process - the main thing here should be :splat, which will basically be the path requested.
         | 
| 69 | 
            +
                  # @param [Request] request the Request object for this session -- not currently used?
         | 
| 70 70 | 
             
                  #
         | 
| 71 71 | 
             
                  def call(params = {}, request = nil)
         | 
| 72 | 
            -
            #        debug_log "DirectoryHandler: call #{params.inspect}, #{request.inspect}"
         | 
| 73 | 
            -
             | 
| 74 72 | 
             
                    lookup = request_path(params)
         | 
| 75 73 |  | 
| 76 74 | 
             
                    raise Gopher::InvalidRequest if ! contained?(lookup)
         | 
| @@ -86,7 +84,7 @@ module Gopher | |
| 86 84 |  | 
| 87 85 | 
             
                  #
         | 
| 88 86 | 
             
                  # generate a directory listing
         | 
| 89 | 
            -
                  # @param [String] path to directory
         | 
| 87 | 
            +
                  # @param [String] dir path to directory
         | 
| 90 88 | 
             
                  # @return rendered directory output for a response
         | 
| 91 89 | 
             
                  #
         | 
| 92 90 | 
             
                  def directory(dir)
         | 
| @@ -98,7 +96,7 @@ module Gopher | |
| 98 96 | 
             
                    # iterate through the contents of this directory.
         | 
| 99 97 | 
             
                    # NOTE: we don't filter this, so we will ALWAYS list subdirectories of a mounted folder
         | 
| 100 98 | 
             
                    #
         | 
| 101 | 
            -
                    Dir.glob("#{dir} | 
| 99 | 
            +
                    Dir.glob("#{dir}/*").each do |x|
         | 
| 102 100 | 
             
                      # if this is a directory, then generate a directory link for it
         | 
| 103 101 | 
             
                      if File.directory?(x)
         | 
| 104 102 | 
             
                        m.directory File.basename(x), to_selector(x), @application.host, @application.port
         | 
| @@ -5,10 +5,14 @@ module Gopher | |
| 5 5 | 
             
              #
         | 
| 6 6 | 
             
              module Rendering
         | 
| 7 7 |  | 
| 8 | 
            +
                require 'artii'
         | 
| 9 | 
            +
             | 
| 8 10 | 
             
                # "A CR LF denotes the end of the item." RFC 1436
         | 
| 9 11 | 
             
                # @see http://www.faqs.org/rfcs/rfc1436.html
         | 
| 10 12 | 
             
                LINE_ENDING = "\r\n"
         | 
| 11 13 |  | 
| 14 | 
            +
                DEFAULT_ENCODING = 'UTF-8'
         | 
| 15 | 
            +
                
         | 
| 12 16 | 
             
                #
         | 
| 13 17 | 
             
                # base class for rendering output. this class provides methods
         | 
| 14 18 | 
             
                # that can be used when rendering both text and gopher menus
         | 
| @@ -40,7 +44,7 @@ module Gopher | |
| 40 44 | 
             
                  #   then adds any required spacing
         | 
| 41 45 | 
             
                  #
         | 
| 42 46 | 
             
                  def text(text)
         | 
| 43 | 
            -
                    self << text
         | 
| 47 | 
            +
                    self << text.force_encoding(DEFAULT_ENCODING)
         | 
| 44 48 | 
             
                    add_spacing
         | 
| 45 49 | 
             
                  end
         | 
| 46 50 |  | 
| @@ -94,6 +98,32 @@ module Gopher | |
| 94 98 | 
             
                    self.to_s
         | 
| 95 99 | 
             
                  end
         | 
| 96 100 |  | 
| 101 | 
            +
                  #
         | 
| 102 | 
            +
                  # output a figlet, which is a big ASCII art header like this:
         | 
| 103 | 
            +
                  #    _    _      _ _       _
         | 
| 104 | 
            +
                  #   | |  | |    | | |     | |
         | 
| 105 | 
            +
                  #   | |__| | ___| | | ___ | |
         | 
| 106 | 
            +
                  #   |  __  |/ _ \ | |/ _ \| |
         | 
| 107 | 
            +
                  #   | |  | |  __/ | | (_) |_|
         | 
| 108 | 
            +
                  #   |_|  |_|\___|_|_|\___/(_)
         | 
| 109 | 
            +
                  #
         | 
| 110 | 
            +
                  # This method doesn't do any width checks, so you should be
         | 
| 111 | 
            +
                  # careful with it.
         | 
| 112 | 
            +
                  # You can get a list of fonts from the artii source code or
         | 
| 113 | 
            +
                  # http://www.figlet.org/examples.html
         | 
| 114 | 
            +
                  # https://github.com/miketierney/artii/tree/master/lib/figlet/fonts
         | 
| 115 | 
            +
                  
         | 
| 116 | 
            +
                  # @param [String] str the text you want to use for your figlet
         | 
| 117 | 
            +
                  # @param [String] font name of the font. Defaults to 'big'.
         | 
| 118 | 
            +
                  #
         | 
| 119 | 
            +
                  def figlet(str, font = 'big')
         | 
| 120 | 
            +
                    a = Artii::Base.new(:font => font)
         | 
| 121 | 
            +
                    a.asciify(str).split("\n").each do |l|
         | 
| 122 | 
            +
                      text l
         | 
| 123 | 
            +
                    end
         | 
| 124 | 
            +
                    self.to_s
         | 
| 125 | 
            +
                  end
         | 
| 126 | 
            +
             | 
| 97 127 | 
             
                  #
         | 
| 98 128 | 
             
                  # output a centered string with a nice underline below it,
         | 
| 99 129 | 
             
                  # centered on the current output width
         | 
| @@ -119,6 +149,17 @@ module Gopher | |
| 119 149 | 
             
                    underline(@width, under)
         | 
| 120 150 | 
             
                  end
         | 
| 121 151 |  | 
| 152 | 
            +
                  #
         | 
| 153 | 
            +
                  # output a 'small' header, just the text with an underline
         | 
| 154 | 
            +
                  # @param [String] str - the string to output
         | 
| 155 | 
            +
                  # @param [String] under - the desired underline character
         | 
| 156 | 
            +
                  #
         | 
| 157 | 
            +
                  def small_header(str, under = '=')
         | 
| 158 | 
            +
                    str = " " + str + " "
         | 
| 159 | 
            +
                    text(str)
         | 
| 160 | 
            +
                    underline(str.length, under)
         | 
| 161 | 
            +
                  end
         | 
| 162 | 
            +
             | 
| 122 163 | 
             
                  #
         | 
| 123 164 | 
             
                  # output a centered string in a box
         | 
| 124 165 | 
             
                  # @param [String] str the string to output
         | 
| @@ -1,5 +1,7 @@ | |
| 1 1 | 
             
            module Gopher
         | 
| 2 2 | 
             
              module Rendering
         | 
| 3 | 
            +
                require 'mimemagic'
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
                #
         | 
| 4 6 | 
             
                # The MenuContext is for rendering gopher menus in the "pseudo
         | 
| 5 7 | 
             
                # file-system hierarchy" defined by RFC1436
         | 
| @@ -7,7 +9,6 @@ module Gopher | |
| 7 9 | 
             
                # @see http://www.ietf.org/rfc/rfc1436.txt
         | 
| 8 10 | 
             
                #
         | 
| 9 11 | 
             
                class Menu < Base
         | 
| 10 | 
            -
             | 
| 11 12 | 
             
                  # default host value when rendering a line with no selector
         | 
| 12 13 | 
             
                  NO_HOST = '(FALSE)'
         | 
| 13 14 |  | 
| @@ -15,7 +16,7 @@ module Gopher | |
| 15 16 | 
             
                  NO_PORT = 0
         | 
| 16 17 |  | 
| 17 18 | 
             
                  # Sanitizes text for use in gopher menus
         | 
| 18 | 
            -
                  # @param [String]  | 
| 19 | 
            +
                  # @param [String] raw text to cleanup
         | 
| 19 20 | 
             
                  # @return string that can be used in a gopher menu
         | 
| 20 21 | 
             
                  def sanitize_text(raw)
         | 
| 21 22 | 
             
                    raw.
         | 
| @@ -92,11 +93,51 @@ module Gopher | |
| 92 93 | 
             
                  #   method instead
         | 
| 93 94 | 
             
                  # @param [String] host for link, defaults to current host
         | 
| 94 95 | 
             
                  # @param [String] port for link, defaults to current port
         | 
| 95 | 
            -
                   | 
| 96 | 
            -
             | 
| 96 | 
            +
                  # @param [String] real filepath of the link
         | 
| 97 | 
            +
                  # @param [String] selector type. if not specified, we will guess
         | 
| 98 | 
            +
                  def link(text, selector, host=nil, port=nil, filepath=nil, type=nil)
         | 
| 99 | 
            +
                    if !type
         | 
| 100 | 
            +
                      if filepath
         | 
| 101 | 
            +
                        type = determine_type(filepath)
         | 
| 102 | 
            +
                      else
         | 
| 103 | 
            +
                        type = determine_type(selector)
         | 
| 104 | 
            +
                      end
         | 
| 105 | 
            +
                    end
         | 
| 97 106 | 
             
                    line type, text, selector, host, port
         | 
| 98 107 | 
             
                  end
         | 
| 99 108 |  | 
| 109 | 
            +
                  #
         | 
| 110 | 
            +
                  # output a link to text output
         | 
| 111 | 
            +
                  #
         | 
| 112 | 
            +
                  # @param [String] text the text of the link
         | 
| 113 | 
            +
                  # @param [String] selector the path of the link. the extension of this path will be used to
         | 
| 114 | 
            +
                  #   detemine the type of link -- image, archive, etc. If you want
         | 
| 115 | 
            +
                  #   to specify a specific link-type, you should use the text
         | 
| 116 | 
            +
                  #   method instead
         | 
| 117 | 
            +
                  # @param [String] host for link, defaults to current host
         | 
| 118 | 
            +
                  # @param [String] port for link, defaults to current port
         | 
| 119 | 
            +
                  # @param [String] real filepath of the link
         | 
| 120 | 
            +
                  def text_link(text, selector, host=nil, port=nil, filepath=nil)
         | 
| 121 | 
            +
                    link(text, selector, host, port, filepath, '0')
         | 
| 122 | 
            +
                  end
         | 
| 123 | 
            +
                  
         | 
| 124 | 
            +
                  # Create an HTTP link entry. This is how this works (via wikipedia)
         | 
| 125 | 
            +
                  #
         | 
| 126 | 
            +
                  # For example, to create a link to http://gopher.quux.org/, the
         | 
| 127 | 
            +
                  # item type is "h", the display string is the title of the link,
         | 
| 128 | 
            +
                  # the item selector is "URL:http://gopher.quux.org/", and the
         | 
| 129 | 
            +
                  # domain and port are that of the originating Gopher server (so
         | 
| 130 | 
            +
                  # that clients that do not support URL links will query the
         | 
| 131 | 
            +
                  # server and receive an HTML redirection page).
         | 
| 132 | 
            +
                  #
         | 
| 133 | 
            +
                  # @param [String] text the text of the link
         | 
| 134 | 
            +
                  # @param [String] URL of the link
         | 
| 135 | 
            +
                  # @param [String] host for link, defaults to current host
         | 
| 136 | 
            +
                  # @param [String] port for link, defaults to current port
         | 
| 137 | 
            +
                  def http(text, url, host=nil, port=nil)
         | 
| 138 | 
            +
                    line "h", text, "URL:#{url}", host, port
         | 
| 139 | 
            +
                  end
         | 
| 140 | 
            +
                  
         | 
| 100 141 | 
             
                  #
         | 
| 101 142 | 
             
                  # output a search entry
         | 
| 102 143 | 
             
                  # @param [String] text the text of the link
         | 
| @@ -109,19 +150,67 @@ module Gopher | |
| 109 150 |  | 
| 110 151 | 
             
                  #
         | 
| 111 152 | 
             
                  # Determines the gopher type for +selector+ based on the
         | 
| 112 | 
            -
                  #  | 
| 113 | 
            -
                  #  | 
| 114 | 
            -
                  # @param [String] selector, presumably a link to a file name with an extension
         | 
| 153 | 
            +
                  # information stored in the shared mime database.
         | 
| 154 | 
            +
                  # @param [String] filepath The full path to the file (should also exist, if possible)
         | 
| 115 155 | 
             
                  # @return gopher selector type
         | 
| 116 156 | 
             
                  #
         | 
| 117 | 
            -
                  def determine_type( | 
| 118 | 
            -
                     | 
| 119 | 
            -
                     | 
| 120 | 
            -
             | 
| 121 | 
            -
                     | 
| 122 | 
            -
                     | 
| 123 | 
            -
             | 
| 124 | 
            -
             | 
| 157 | 
            +
                  def determine_type(filepath)
         | 
| 158 | 
            +
                    # Determine MIME type by path
         | 
| 159 | 
            +
                    mimetype = MimeMagic.by_path(filepath)
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                    # Determine MIME type by contents
         | 
| 162 | 
            +
                    if !mimetype
         | 
| 163 | 
            +
                      begin
         | 
| 164 | 
            +
                        # Open file
         | 
| 165 | 
            +
                        file = File.open(filepath)
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                        # Try to detect MIME type using by recognition of typical characters
         | 
| 168 | 
            +
                        mimetype = MimeMagic.by_magic(file)
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                        if !mimetype
         | 
| 171 | 
            +
                          file.rewind
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                          # Read up to 1k of file data and look for a "\0\0" sequence (typical for binary files)
         | 
| 174 | 
            +
                          if file.read(1000).include?("\0\0")
         | 
| 175 | 
            +
                            mimetype = MimeMagic.new("application/octet-stream")
         | 
| 176 | 
            +
                          else
         | 
| 177 | 
            +
                            mimetype = MimeMagic.new("text/plain")
         | 
| 178 | 
            +
                          end
         | 
| 179 | 
            +
                          
         | 
| 180 | 
            +
                          file.close
         | 
| 181 | 
            +
                        end
         | 
| 182 | 
            +
                      rescue SystemCallError,IOError
         | 
| 183 | 
            +
                        nil
         | 
| 184 | 
            +
                      end
         | 
| 185 | 
            +
                    end
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                    if !mimetype
         | 
| 188 | 
            +
                      ext = File.extname(filepath).split(".").last
         | 
| 189 | 
            +
                      mimetype = MimeMagic.by_extension(ext)
         | 
| 190 | 
            +
                    end
         | 
| 191 | 
            +
                    
         | 
| 192 | 
            +
                    if !mimetype
         | 
| 193 | 
            +
                      return '9' # Binary file
         | 
| 194 | 
            +
                    elsif mimetype.child_of?('application/gzip') || mimetype.child_of?('application/x-bzip') || mimetype.child_of?('application/zip')
         | 
| 195 | 
            +
                      return '5' # archive
         | 
| 196 | 
            +
                    elsif mimetype.child_of?('image/gif')
         | 
| 197 | 
            +
                      return 'g' # GIF image
         | 
| 198 | 
            +
                    elsif mimetype.child_of?('text/x-uuencode')
         | 
| 199 | 
            +
                      return '6' # UUEncode encoded file
         | 
| 200 | 
            +
                    elsif mimetype.child_of?('application/mac-binhex40')
         | 
| 201 | 
            +
                      return '4' # BinHex encoded file
         | 
| 202 | 
            +
                    elsif mimetype.child_of?('text/html') || mimetype.child_of?('application/xhtml+xml')
         | 
| 203 | 
            +
                      return 'h' # HTML file
         | 
| 204 | 
            +
                    elsif mimetype.mediatype == 'text'    || mimetype.child_of?('text/plain')
         | 
| 205 | 
            +
                      return '0' # General text file
         | 
| 206 | 
            +
                    elsif mimetype.mediatype == 'image'
         | 
| 207 | 
            +
                      return 'I' # General image file
         | 
| 208 | 
            +
                    elsif mimetype.mediatype == 'audio'
         | 
| 209 | 
            +
                      return 's' # General audio file
         | 
| 210 | 
            +
                    elsif mimetype.mediatype == 'video'
         | 
| 211 | 
            +
                      return 'v' # General video file
         | 
| 212 | 
            +
                    else
         | 
| 213 | 
            +
                      return '9' # Binary file
         | 
| 125 214 | 
             
                    end
         | 
| 126 215 | 
             
                  end
         | 
| 127 216 | 
             
                end
         | 
    
        data/lib/gopher2000/request.rb
    CHANGED
    
    | @@ -7,10 +7,21 @@ module Gopher | |
| 7 7 | 
             
                attr_accessor :selector, :input, :ip_address
         | 
| 8 8 |  | 
| 9 9 | 
             
                def initialize(raw, ip_addr=nil)
         | 
| 10 | 
            -
                  @ | 
| 11 | 
            -
                  @ | 
| 10 | 
            +
                  @raw = raw
         | 
| 11 | 
            +
                  @selector, @input = @raw.chomp.split("\t")
         | 
| 12 | 
            +
                  
         | 
| 13 | 
            +
            	    @selector = Gopher::Application.sanitize_selector(@selector)
         | 
| 14 | 
            +
            	    @ip_address = ip_addr
         | 
| 12 15 | 
             
                end
         | 
| 13 16 |  | 
| 17 | 
            +
                def url?
         | 
| 18 | 
            +
                  @raw =~ /^URL\:/
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def url
         | 
| 22 | 
            +
                  @raw.chomp.split("\t").first.gsub(/^URL\:/, '')
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
                
         | 
| 14 25 | 
             
                # confirm that this is actually a valid gopher request
         | 
| 15 26 | 
             
                # @return [Boolean] true if the request is valid, false otherwise
         | 
| 16 27 | 
             
                def valid?
         | 
    
        data/lib/gopher2000/server.rb
    CHANGED
    
    | @@ -8,12 +8,12 @@ module Gopher | |
| 8 8 |  | 
| 9 9 | 
             
                #
         | 
| 10 10 | 
             
                # constructor
         | 
| 11 | 
            -
                # @param [Application] instance of Gopher::Application we want to run
         | 
| 11 | 
            +
                # @param [Application] a instance of Gopher::Application we want to run
         | 
| 12 12 | 
             
                #
         | 
| 13 13 | 
             
                def initialize(a)
         | 
| 14 14 | 
             
                  @app = a
         | 
| 15 15 | 
             
                end
         | 
| 16 | 
            -
             | 
| 16 | 
            +
             
         | 
| 17 17 | 
             
                #
         | 
| 18 18 | 
             
                # @return [String] name of the host specified in our config
         | 
| 19 19 | 
             
                #
         | 
| @@ -28,6 +28,13 @@ module Gopher | |
| 28 28 | 
             
                  @app.config[:port] ||= 70
         | 
| 29 29 | 
             
                end
         | 
| 30 30 |  | 
| 31 | 
            +
                #
         | 
| 32 | 
            +
                # @return [String] environment specified in config
         | 
| 33 | 
            +
                #
         | 
| 34 | 
            +
                def env
         | 
| 35 | 
            +
                  @app.config[:env] || 'development'
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
                
         | 
| 31 38 | 
             
                #
         | 
| 32 39 | 
             
                # main app loop. called via at_exit block defined in DSL
         | 
| 33 40 | 
             
                #
         | 
| @@ -77,8 +84,8 @@ module Gopher | |
| 77 84 | 
             
                  require 'optparse'
         | 
| 78 85 | 
             
                  OptionParser.new { |op|
         | 
| 79 86 | 
             
                    op.on('-p port',   'set the port (default is 70)')                { |val| set :port, Integer(val) }
         | 
| 80 | 
            -
                    op.on('-o addr',   'set the host (default is 0.0.0.0)')             { |val| set : | 
| 81 | 
            -
                    op.on('-e env',    'set the environment (default is development)')  { |val| set : | 
| 87 | 
            +
                    op.on('-o addr',   'set the host (default is 0.0.0.0)')             { |val| set :host, val }
         | 
| 88 | 
            +
                    op.on('-e env',    'set the environment (default is development)')  { |val| set :env, val.to_sym }
         | 
| 82 89 | 
             
                  }.parse!(ARGV.dup)
         | 
| 83 90 | 
             
                end
         | 
| 84 91 | 
             
              end
         | 
    
        data/lib/gopher2000/version.rb
    CHANGED
    
    
    
        data/spec/application_spec.rb
    CHANGED
    
    | @@ -9,46 +9,46 @@ describe Gopher::Application do | |
| 9 9 | 
             
              end
         | 
| 10 10 |  | 
| 11 11 | 
             
              it 'should have default host/port' do
         | 
| 12 | 
            -
                @app.host. | 
| 13 | 
            -
                @app.port. | 
| 12 | 
            +
                expect(@app.host).to eq("0.0.0.0")
         | 
| 13 | 
            +
                expect(@app.port).to eq(70)
         | 
| 14 14 | 
             
              end
         | 
| 15 15 |  | 
| 16 16 | 
             
              describe "should_reload?" do
         | 
| 17 17 | 
             
                it "is false if no scripts" do
         | 
| 18 | 
            -
                  @app.should_reload | 
| 18 | 
            +
                  expect(@app.should_reload?).to eq(false)
         | 
| 19 19 | 
             
                end
         | 
| 20 20 |  | 
| 21 21 | 
             
                it "shouldn't do anything if last_reload not set" do
         | 
| 22 | 
            -
                  @app.last_reload. | 
| 22 | 
            +
                  expect(@app.last_reload).to be_nil
         | 
| 23 23 | 
             
                  @app.scripts << "foo.rb"
         | 
| 24 | 
            -
                  @app.should_reload | 
| 24 | 
            +
                  expect(@app.should_reload?).to eq(false)
         | 
| 25 25 | 
             
                end
         | 
| 26 26 |  | 
| 27 27 | 
             
                it "should check script date" do
         | 
| 28 28 | 
             
                  now = Time.now
         | 
| 29 | 
            -
                  Time. | 
| 29 | 
            +
                  allow(Time).to receive(:now).and_return(now)
         | 
| 30 30 |  | 
| 31 31 | 
             
                  @app.last_reload = Time.now - 1000
         | 
| 32 32 | 
             
                  @app.scripts << "foo.rb"
         | 
| 33 | 
            -
                  File. | 
| 33 | 
            +
                  expect(File).to receive(:mtime).with("foo.rb").and_return(now)
         | 
| 34 34 |  | 
| 35 | 
            -
                  @app.should_reload | 
| 35 | 
            +
                  expect(@app.should_reload?).to eq(true)
         | 
| 36 36 | 
             
                end
         | 
| 37 37 | 
             
              end
         | 
| 38 38 |  | 
| 39 39 | 
             
              describe "reload_stale" do
         | 
| 40 40 | 
             
                it "should load script and update last_reload" do
         | 
| 41 41 | 
             
                  now = Time.now
         | 
| 42 | 
            -
                  Time. | 
| 42 | 
            +
                  allow(Time).to receive(:now).and_return(now)
         | 
| 43 43 |  | 
| 44 | 
            -
                  @app. | 
| 44 | 
            +
                  expect(@app).to receive(:should_reload?).and_return(true)
         | 
| 45 45 |  | 
| 46 46 | 
             
                  @app.last_reload = Time.now - 1000
         | 
| 47 47 | 
             
                  @app.scripts << "foo.rb"
         | 
| 48 | 
            -
                  @app. | 
| 48 | 
            +
                  expect(@app).to receive(:load).with("foo.rb")
         | 
| 49 49 | 
             
                  @app.reload_stale
         | 
| 50 50 |  | 
| 51 | 
            -
                  @app.last_reload. | 
| 51 | 
            +
                  expect(@app.last_reload).to eq(now)
         | 
| 52 52 | 
             
                end
         | 
| 53 53 | 
             
              end
         | 
| 54 54 | 
             
            end
         | 
    
        data/spec/dispatching_spec.rb
    CHANGED
    
    | @@ -24,7 +24,7 @@ describe Gopher::Application do | |
| 24 24 | 
             
                  @request = Gopher::Request.new("/about")
         | 
| 25 25 |  | 
| 26 26 | 
             
                  keys, block = @server.lookup(@request.selector)
         | 
| 27 | 
            -
                  keys. | 
| 27 | 
            +
                  expect(keys).to eq({})
         | 
| 28 28 | 
             
                end
         | 
| 29 29 |  | 
| 30 30 | 
             
                it "should translate path" do
         | 
| @@ -32,7 +32,7 @@ describe Gopher::Application do | |
| 32 32 | 
             
                  @request = Gopher::Request.new("/about/x/y")
         | 
| 33 33 |  | 
| 34 34 | 
             
                  keys, block = @server.lookup(@request.selector)
         | 
| 35 | 
            -
                  keys. | 
| 35 | 
            +
                  expect(keys).to eq({:foo => 'x', :bar => 'y'})
         | 
| 36 36 | 
             
                end
         | 
| 37 37 |  | 
| 38 38 | 
             
                it "should return default route if no other route found, and default is defined" do
         | 
| @@ -41,8 +41,8 @@ describe Gopher::Application do | |
| 41 41 | 
             
                  end
         | 
| 42 42 | 
             
                  @request = Gopher::Request.new("/about/x/y")
         | 
| 43 43 | 
             
                  @response = @server.dispatch(@request)
         | 
| 44 | 
            -
                  @response.body. | 
| 45 | 
            -
                  @response.code. | 
| 44 | 
            +
                  expect(@response.body).to eq("DEFAULT ROUTE")
         | 
| 45 | 
            +
                  expect(@response.code).to eq(:success)
         | 
| 46 46 | 
             
                end
         | 
| 47 47 |  | 
| 48 48 | 
             
                it "should respond with error if no route found" do
         | 
| @@ -50,7 +50,8 @@ describe Gopher::Application do | |
| 50 50 | 
             
                  @request = Gopher::Request.new("/junk/x/y")
         | 
| 51 51 |  | 
| 52 52 | 
             
                  @response = @server.dispatch(@request)
         | 
| 53 | 
            -
                  @response.code. | 
| 53 | 
            +
                  expect(@response.code).to eq(:missing)
         | 
| 54 | 
            +
                  expect(@response.body).to contain_any_error
         | 
| 54 55 | 
             
                end
         | 
| 55 56 |  | 
| 56 57 | 
             
                it "should respond with error if invalid request" do
         | 
| @@ -58,7 +59,8 @@ describe Gopher::Application do | |
| 58 59 | 
             
                  @request = Gopher::Request.new("x" * 256)
         | 
| 59 60 |  | 
| 60 61 | 
             
                  @response = @server.dispatch(@request)
         | 
| 61 | 
            -
                  @response.code. | 
| 62 | 
            +
                  expect(@response.code).to eq(:error)
         | 
| 63 | 
            +
                  expect(@response.body).to contain_any_error
         | 
| 62 64 | 
             
                end
         | 
| 63 65 |  | 
| 64 66 | 
             
                it "should respond with error if there's an exception" do
         | 
| @@ -66,7 +68,8 @@ describe Gopher::Application do | |
| 66 68 | 
             
                  @request = Gopher::Request.new("/x")
         | 
| 67 69 |  | 
| 68 70 | 
             
                  @response = @server.dispatch(@request)
         | 
| 69 | 
            -
                  @response.code. | 
| 71 | 
            +
                  expect(@response.code).to eq(:error)
         | 
| 72 | 
            +
                  expect(@response.body).to contain_any_error
         | 
| 70 73 | 
             
                end
         | 
| 71 74 | 
             
              end
         | 
| 72 75 |  | 
| @@ -82,7 +85,7 @@ describe Gopher::Application do | |
| 82 85 |  | 
| 83 86 | 
             
                it "should run the block" do
         | 
| 84 87 | 
             
                  @response = @server.dispatch(@request)
         | 
| 85 | 
            -
                  @response.body. | 
| 88 | 
            +
                  expect(@response.body).to eq("GOPHERTRON")
         | 
| 86 89 | 
             
                end
         | 
| 87 90 | 
             
              end
         | 
| 88 91 |  | 
| @@ -97,37 +100,47 @@ describe Gopher::Application do | |
| 97 100 |  | 
| 98 101 | 
             
                it "should use incoming params" do
         | 
| 99 102 | 
             
                  @response = @server.dispatch(@request)
         | 
| 100 | 
            -
                  @response.body. | 
| 103 | 
            +
                  expect(@response.body).to eq("x/a/y/b")
         | 
| 101 104 | 
             
                end
         | 
| 102 105 | 
             
              end
         | 
| 103 106 |  | 
| 104 107 | 
             
              describe "dispatch to mount" do
         | 
| 105 108 | 
             
                before(:each) do
         | 
| 106 | 
            -
                  @h =  | 
| 107 | 
            -
                  @h. | 
| 108 | 
            -
                  Gopher::Handlers::DirectoryHandler. | 
| 109 | 
            +
                  @h = double(Gopher::Handlers::DirectoryHandler)
         | 
| 110 | 
            +
                  expect(@h).to receive(:application=).with(@server)
         | 
| 111 | 
            +
                  expect(Gopher::Handlers::DirectoryHandler).to receive(:new).with({:bar => :baz, :mount_point => "/foo"}).and_return(@h)
         | 
| 109 112 |  | 
| 110 113 | 
             
                  @server.mount "/foo", :bar => :baz
         | 
| 111 114 | 
             
                end
         | 
| 112 115 |  | 
| 113 116 | 
             
                it "should work for root path" do
         | 
| 114 117 | 
             
                  @request = Gopher::Request.new("/foo")
         | 
| 115 | 
            -
                  @h. | 
| 118 | 
            +
                  expect(@h).to receive(:call).with({:splat => ""}, @request)
         | 
| 116 119 |  | 
| 117 120 | 
             
                  @response = @server.dispatch(@request)
         | 
| 118 | 
            -
                  @response.code. | 
| 121 | 
            +
                  expect(@response.code).to eq(:success)
         | 
| 119 122 | 
             
                end
         | 
| 120 123 |  | 
| 121 124 | 
             
                it "should work for subdir" do
         | 
| 122 125 | 
             
                  @request = Gopher::Request.new("/foo/bar")
         | 
| 123 | 
            -
                  @h. | 
| 126 | 
            +
                  expect(@h).to receive(:call).with({:splat => "bar"}, @request)
         | 
| 124 127 |  | 
| 125 128 | 
             
                  @response = @server.dispatch(@request)
         | 
| 126 | 
            -
                  @response.code. | 
| 129 | 
            +
                  expect(@response.code).to eq(:success)
         | 
| 127 130 | 
             
                end
         | 
| 128 131 | 
             
              end
         | 
| 129 132 |  | 
| 133 | 
            +
              describe 'dispatch for URL requests' do
         | 
| 134 | 
            +
                let(:target) { 'URL:http://github.com/muffinista/gopher2000' }
         | 
| 135 | 
            +
                let(:request) { Gopher::Request.new(target) }
         | 
| 136 | 
            +
                let(:response) { @server.dispatch(request) }
         | 
| 130 137 |  | 
| 138 | 
            +
                it "should return web page" do
         | 
| 139 | 
            +
                  expect(response.body).to include('<meta http-equiv="refresh" content="5;URL=http://github.com/muffinista/gopher2000">')
         | 
| 140 | 
            +
                end
         | 
| 141 | 
            +
              end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
              
         | 
| 131 144 | 
             
              describe "globs" do
         | 
| 132 145 | 
             
                before(:each) do
         | 
| 133 146 | 
             
                  @server.route '/about/*' do
         | 
| @@ -138,7 +151,15 @@ describe Gopher::Application do | |
| 138 151 | 
             
                it "should put wildcard into param[:splat]" do
         | 
| 139 152 | 
             
                  @request = Gopher::Request.new("/about/a/b")
         | 
| 140 153 | 
             
                  @response = @server.dispatch(@request)
         | 
| 141 | 
            -
                  @response.body. | 
| 154 | 
            +
                  expect(@response.body).to eq("a/b")
         | 
| 155 | 
            +
                end
         | 
| 156 | 
            +
              end
         | 
| 157 | 
            +
            end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
            RSpec::Matchers.define :contain_any_error do
         | 
| 160 | 
            +
              match do |actual|
         | 
| 161 | 
            +
                actual.split(/\r?\n/).any? do |line| 
         | 
| 162 | 
            +
                  line.match(/^3.*?\tnull\t\(FALSE\)\t0$/)
         | 
| 142 163 | 
             
                end
         | 
| 143 164 | 
             
              end
         | 
| 144 165 | 
             
            end
         |