Class: Gopher::Application

Inherits:
Object
  • Object
show all
Defined in:
lib/gopher2000/base.rb

Overview

main application class for a gopher server. holds all the methods/data required to interact with clients.

Constant Summary collapse

ACCESS_LOG_PATTERN =

The output pattern we will use to generate access logs

"%d\t%m\n"
@@access_log =
nil
@@debug_log =
nil

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#configObject

Returns the value of attribute config.



20
21
22
# File 'lib/gopher2000/base.rb', line 20

def config
  @config
end

#last_reloadObject

Returns the value of attribute last_reload.



20
21
22
# File 'lib/gopher2000/base.rb', line 20

def last_reload
  @last_reload
end

Returns the value of attribute menus.



20
21
22
# File 'lib/gopher2000/base.rb', line 20

def menus
  @menus
end

#paramsObject

Returns the value of attribute params.



20
21
22
# File 'lib/gopher2000/base.rb', line 20

def params
  @params
end

#requestObject

Returns the value of attribute request.



20
21
22
# File 'lib/gopher2000/base.rb', line 20

def request
  @request
end

#routesObject

Returns the value of attribute routes.



20
21
22
# File 'lib/gopher2000/base.rb', line 20

def routes
  @routes
end

#scriptsObject

Returns the value of attribute scripts.



20
21
22
# File 'lib/gopher2000/base.rb', line 20

def scripts
  @scripts
end

#text_templatesObject

Returns the value of attribute text_templates.



20
21
22
# File 'lib/gopher2000/base.rb', line 20

def text_templates
  @text_templates
end

Class Method Details

.generate_method(method_name) { ... } ⇒ Object

generate a method which we will use to run routes. this is based on #generate_method as used by sinatra.

Parameters:

  • method_name (String)

    name to use for the method

Yields:

  • block to use for the method

See Also:



469
470
471
472
473
474
# File 'lib/gopher2000/base.rb', line 469

def generate_method(method_name, &block)
  define_method(method_name, &block)
  method = instance_method method_name
  remove_method method_name
  method
end

.sanitize_selector(raw) ⇒ Object

Sanitizes a gopher selector



455
456
457
458
459
460
461
# File 'lib/gopher2000/base.rb', line 455

def sanitize_selector(raw)
 "/#{raw}".dup.
   strip. # Strip whitespace
   sub(/\/$/, ''). # Strip last rslash
   sub(/^\/*/, '/'). # Strip extra lslashes
   gsub(/\.+/, '.') # Don't want consecutive dots!
end

Instance Method Details

#compile(path) ⇒ Object

turn a path string with optional keys (/foo/:bar/:boo) into a regexp which will be used when searching for a route

Parameters:

  • path (String)

    the path to compile



434
435
436
437
438
439
440
441
442
443
444
445
446
447
# File 'lib/gopher2000/base.rb', line 434

def compile(path)
  keys = []
  pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) { |c| encoded(c) }
  pattern.gsub!(/((:\w+)|\*)/) do |match|
    if match == "*"
      keys << 'splat'
      "(.*?)"
    else
      keys << $2[1..-1]
      "([^/?#]+)"
    end
  end
  [/^#{pattern}$/, keys]
end

#compile!(path, &block) ⇒ Object

compile a route



420
421
422
423
424
425
426
# File 'lib/gopher2000/base.rb', line 420

def compile!(path, &block)
  method_name = path
  route_method = Application.generate_method(method_name, &block)
  pattern, keys = compile path

  [ pattern, keys, route_method ]
end

#debug_log(x) ⇒ Object

output a debugging message



480
481
482
483
# File 'lib/gopher2000/base.rb', line 480

def debug_log(x)
  @@debug_logger ||= ::Logging.logger(STDERR)
  @@debug_logger.debug x
end

#debug_mode?Boolean

are we in debugging mode?

Returns:

  • (Boolean)


67
68
69
# File 'lib/gopher2000/base.rb', line 67

def debug_mode?
  config[:debug] == true
end

#default_route { ... } ⇒ Object

specify a default route to handle requests if no other route exists

Examples:

render a template

default_route do
  render :template
end

Yields:

  • a block to handle the default route



161
162
163
# File 'lib/gopher2000/base.rb', line 161

def default_route(&block)
  @default_route = Application.generate_method("DEFAULT_ROUTE", &block)
end

#dispatch(req) ⇒ Object

find and run the first route which matches the incoming request

Parameters:

  • req (Request)

    Gopher::Request object



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/gopher2000/base.rb', line 201

def dispatch(req)
  debug_log(req)

  response = Response.new
  @request = req

  if ! @request.valid?
    response.body = handle_invalid_request
    response.code = :error
  elsif @request.url?
    response.body = handle_url(@request)
    response.code = :success
  else
    begin
      debug_log("do lookup for #{@request.selector}")
      @params, block = lookup(@request.selector)

      #
      # call the block that handles this lookup
      #
      response.body = block.bind(self).call
      response.code = :success
    rescue Gopher::NotFoundError => e
      debug_log("#{@request.selector} -- not found")
      response.body = handle_not_found
      response.code = :missing
    rescue Exception => e
      debug_log("#{@request.selector} -- error")
      debug_log(e.inspect)
      debug_log(e.backtrace)

      response.body = handle_error(e)
      response.code = :error
    end
  end

  access_log(req, response)
  response
end

#envObject

return the application environment



60
61
62
# File 'lib/gopher2000/base.rb', line 60

def env
  config[:env] ||= 'development'
end

#error_templateObject

get the id of the template that will be used when rendering an error

Returns:

  • name of error template



355
356
357
# File 'lib/gopher2000/base.rb', line 355

def error_template
  menus.include?(:error) ? :error : :'internal/error'
end

#find_template(t) ⇒ Object

find a template

Parameters:

  • t (String/Symbol)

    name of the template

Returns:

  • template block and the class context it should use



310
311
312
313
314
315
316
317
318
319
# File 'lib/gopher2000/base.rb', line 310

def find_template(t)
  x = menus[t]
  if x
    return x, Gopher::Rendering::Menu
  end
  x = text_templates[t]
  if x
    return x, Gopher::Rendering::Text
  end
end

#globify(p) ⇒ Object

add a glob to the end of this string, if there's not one already



413
414
415
# File 'lib/gopher2000/base.rb', line 413

def globify(p)
  p =~ /\*/ ? p : "#{p}/?*".gsub("//", "/")
end

#helpers(target = Gopher::Application, &block) ⇒ Object

Add helpers to the Base renedering class, which allows them to be called when outputting the results of an action. Here's the code in Sinatra for reference:

Makes the methods defined in the block and in the Modules given in `extensions` available to the handlers and templates

def helpers(*extensions, &block)
  class_eval(&block)   if block_given?
  include(*extensions) if extensions.any?
end

target - What class should receive the helpers – defaults to Gopher::Rendering::Base, which will make it available when rendering block – a block which declares the helpers you want. for example:

helpers do

def foo; "FOO"; end

end



395
396
397
# File 'lib/gopher2000/base.rb', line 395

def helpers(target = Gopher::Application, &block)
  target.class_eval(&block)
end

#hostObject

return the host we will use when outputting gopher menus



46
47
48
# File 'lib/gopher2000/base.rb', line 46

def host
  config[:host] ||= '0.0.0.0'
end

#invalid_request_templateObject

get the id of the template that will be used when rendering an invalid request

Returns:

  • name of invalid_request template



373
374
375
# File 'lib/gopher2000/base.rb', line 373

def invalid_request_template
  menus.include?(:invalid_request) ? :invalid_request : :'internal/invalid_request'
end

#lookup(selector) ⇒ Object

lookup an incoming path

Parameters:

  • selector (String)

    the selector path of the incoming request

Raises:



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/gopher2000/base.rb', line 170

def lookup(selector)
  unless routes.nil?
  routes.each do |pattern, keys, block|
      if match = pattern.match(selector)
        match = match.to_a
        url = match.shift
        
        params = to_params_hash(keys, match)

        #
        # @todo think about this
        #
        @params = params

        return params, block
      end
    end
  end

  unless @default_route.nil?
    return {}, @default_route
  end

  raise Gopher::NotFoundError
end

define a template which will be used to render a gopher-style menu.

Examples:

a simple menu:

menu :index do
  # output a text entry in the menu
  text 'simple gopher example'

  # use br(x) to add x space between lines
  br(2)

  # link somewhere
  link 'current time', '/time'
  br

  # another link
  link 'about', '/about'
  br

  # ask for some input
  input 'Hey, what is your name?', '/hello'
  br

  # mount some files
  menu 'filez', '/files'
end

Parameters:

  • name (String/Symbol)

    the name of the template. This is what identifies the template when making a call to render

Yields:

  • a block which will output the menu. This block is executed within an instance of Gopher::Rendering::Menu and will have access to all of its methods.



275
276
277
# File 'lib/gopher2000/base.rb', line 275

def menu(name, &block)
  menus[name.to_sym] = block
end

#mount(path, opts = {}, klass = Gopher::Handlers::DirectoryHandler) ⇒ Object

Examples:

mount the directory '/home/user/foo' at the gopher path '/files', and only show JPG files:

mount '/files' => '/home/user/foo', :filter => '*.jpg'


108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/gopher2000/base.rb', line 108

def mount(path, opts = {}, klass = Gopher::Handlers::DirectoryHandler)
  debug_log "MOUNT #{path} #{opts.inspect}"
  opts[:mount_point] = path

  handler = klass.new(opts)
  handler.application = self

  #
  # add a route for the mounted class
  #
  route(globify(path)) do
    # when we call, pass the params and request object for this
    # particular request
    handler.call(params, request)
  end
end

#non_blocking?Boolean

should we use non-blocking operations? for now, defaults to false if in debug mode, true if we're not in debug mode (presumably, in some sort of production state. HAH! Gopher servers in production)

Returns:

  • (Boolean)


405
406
407
# File 'lib/gopher2000/base.rb', line 405

def non_blocking?
  config.key?(:non_blocking) ? config[:non_blocking] : ! debug_mode?
end

#not_found(&block) ⇒ Object

specify a template to be used for missing requests



301
302
303
# File 'lib/gopher2000/base.rb', line 301

def not_found(&block)
  menu :not_found, &block
end

#not_found_templateObject

get the id of the template that will be used when rendering a not found error

Returns:

  • name of not_found template



347
348
349
# File 'lib/gopher2000/base.rb', line 347

def not_found_template
  menus.include?(:not_found) ? :not_found : :'internal/not_found'
end

#portObject

return the port we will use when outputting gopher menus



53
54
55
# File 'lib/gopher2000/base.rb', line 53

def port
  config[:port] ||= 70
end

#reload_staleObject

reload scripts if needed



84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/gopher2000/base.rb', line 84

def reload_stale
  reload_check = should_reload?
  self.last_reload = Time.now

  return if ! reload_check
  reset!

  self.scripts.each do |f|
    debug_log "reload #{f}"
    load f
  end
end

#render(template, *arguments) ⇒ Object

Find the desired template and call it within the proper context

Parameters:

  • template (String/Symbol)

    name of the template to render

  • arguments (Array)

    optional arguments to be passed to template

Returns:

  • result of rendering

Raises:



327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/gopher2000/base.rb', line 327

def render(template, *arguments)
  #
  # find the right renderer we need
  #
  block, handler = find_template(template)

  raise TemplateNotFound if block.nil?

  ctx = handler.new(self)
  ctx.params = @params
  ctx.request = @request

  ctx.instance_exec(*arguments, &block)
end

#reset!Object

reset the app. clear out any routes, templates, config values, etc. this is used during the load process



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/gopher2000/base.rb', line 27

def reset!
  self.routes = []
  self.menus = {}
  self.text_templates = {}
  self.scripts ||= []
  self.config ||= {
    :debug => false,
    :host => "0.0.0.0",
    :port => 70
  }

  register_defaults

  self
end

#route(path) { ... } ⇒ Object

define a route.

Examples:

respond with a simple string

route '/path' do
  "hi, welcome to /path"
end

respond by rendering a template

route '/render' do
  render :template
end

Parameters:

  • path (String)

    the path your route will answer to. This is basically a URI path

Yields:

  • a block that handles your route



141
142
143
144
145
146
147
148
149
# File 'lib/gopher2000/base.rb', line 141

def route(path, &block)
  selector = Gopher::Application.sanitize_selector(path)
  sig = compile!(selector, &block)

  debug_log("Add route for #{selector}")

  self.routes ||= []
  self.routes << sig
end

#should_reload?Boolean

check if our script has been updated since the last reload

Returns:

  • (Boolean)


75
76
77
78
79
# File 'lib/gopher2000/base.rb', line 75

def should_reload?
  ! last_reload.nil? && self.scripts.any? do |f|
    File.mtime(f) > last_reload
  end
end

#text(name) { ... } ⇒ Object

Define a template which will be used for outputting text. This is not strictly required for outputting text, but it gives you access to the methods defined in Gopher::Rendering::Text for wrapping strings, adding simple headers, etc.

Examples:

simple example

text :hello do
  big_header "Hello There!"
  block "Really long text... ... the end"
  br
end

Parameters:

  • name (String/Symbol)

    the name of the template. This is what identifies the template when making a call to render

Yields:

  • a block which will output the menu. This block is executed within an instance of Gopher::Rendering::Text and will have access to all of its methods.



294
295
296
# File 'lib/gopher2000/base.rb', line 294

def text(name, &block)
  text_templates[name.to_sym] = block
end

#url_templateObject

get the id of the template that will be used when rendering an html page

Returns:

  • name of error template



364
365
366
# File 'lib/gopher2000/base.rb', line 364

def url_template
  menus.include?(:html) ? :html : :'internal/url'
end