Permalink
Browse files

Initial pass at doc'ing the public API

  • Loading branch information...
1 parent fdfe9fc commit 73044bbfbcd11aa1b575fec1bc812a577efd377a @dj2 dj2 committed Feb 17, 2011
View
@@ -7,3 +7,6 @@ pkg/
examples/log
*.swp
Gemfile.lock
+.yardoc
+.livereload
+*.watchr
View
@@ -0,0 +1,2 @@
+--main README.md
+--no-private
View
@@ -1,7 +1,7 @@
require 'bundler'
Bundler::GemHelper.install_tasks
-require 'rake/rdoctask'
+require 'yard'
require 'rspec/core/rake_task'
task :default => [:spec]
@@ -11,11 +11,8 @@ RSpec::Core::RakeTask.new('spec') do |t|
t.pattern = 'spec/**/*_spec.rb'
end
-desc 'Generate RDoc documentation'
-Rake::RDocTask.new(:rdoc) do |task|
- task.rdoc_dir = 'doc'
- task.title = 'Goliath'
- task.options = %w(--title Goliath --main README.md --line-numbers)
- task.rdoc_files.include(['lib/**/*.rb'])
- task.rdoc_files.include(['README.md', 'LICENSE'])
+desc 'Generate documentation'
+YARD::Rake::YardocTask.new do |t|
+ t.files = ['lib/**/*.rb', '-', 'LICENSE']
+ t.options = ['--main', 'README.md', '--no-private']
end
View
@@ -30,6 +30,9 @@ Gem::Specification.new do |s|
s.add_development_dependency 'em-http-request'
s.add_development_dependency 'yajl-ruby'
+ s.add_development_dependency 'yard'
+ s.add_development_dependency 'bluecloth'
+
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- spec/*`.split("\n")
s.require_paths = ['lib']
View
@@ -2,35 +2,95 @@
require 'goliath/request'
module Goliath
+ # All Goliath APIs subclass Goliath::API. All subclasses _must_ override the
+ # {#response} method.
+ #
+ # @example
+ # require 'goliath'
+ #
+ # class HelloWorld < Goliath::API
+ # def response(env)
+ # [200, {}, "hello world"]
+ # end
+ # end
+ #
class API
-
GOLIATH_ENV = 'goliath.env'
class << self
+ # Retrieves the middlewares defined by this API server
+ #
+ # @return [Array] array contains [middleware class, args, block]
def middlewares
@middlewares ||= [[::Rack::ContentLength, nil, nil]]
end
+ # Specify a middleware to be used by the API
+ #
+ # @example
+ # use Goliath::Rack::Validation::RequiredParam, {:key => 'echo'}
+ #
+ # use ::Rack::Rewrite do
+ # rewrite %r{^(.*?)\??gziped=(.*)$}, lambda { |match, env| "#{match[1]}?echo=#{match[2]}" }
+ # end
+ #
+ # @param name [Class] The middleware class to use
+ # @param args Any arguments to pass to the middeware
+ # @param block A block to pass to the middleware
def use(name, args = nil, &block)
middlewares.push([name, args, block])
end
+ # Returns the plugins configured for this API
+ #
+ # @return [Array] array contains [plugin name, args]
def plugins
@plugins ||= []
end
+ # Specify a plugin to be used by the API
+ #
+ # @example
+ # plugin Goliath::Plugin::Latency
+ #
+ # @param name [Class] The plugin class to use
+ # @param args The arguments to the plugin
def plugin(name, *args)
plugins.push([name, args])
end
end
+ # Default stub method to add options into the option parser.
+ #
+ # @example
+ # def options_parser(opts, options)
+ # options[:test] = 0
+ # opts.on('-t', '--test NUM', "The test number") { |val| options[:test] = val.to_i }
+ # end
+ #
+ # @param opts [OptionParser] The options parser
+ # @param options [Hash] The hash to insert the parsed options into
def options_parser(opts, options)
end
+ # Accessor for the current env object
+ #
+ # @note This will not work in a streaming server. You must pass around the env object.
+ #
+ # @return [Goliath::Env] The current environment data for the request
def env
Thread.current[GOLIATH_ENV]
end
+ # The API will proxy missing calls to the env object if possible.
+ #
+ # The two entries in this example are equivalent as long as you are not
+ # in a streaming server.
+ #
+ # @example
+ # logger.info "Hello"
+ # env.logger.info "Hello"
+
def method_missing(name, *args, &blk)
name = name.to_s
if env.respond_to?(name)
@@ -40,6 +100,13 @@ def method_missing(name, *args, &blk)
end
end
+ # {#call} is executed automatically by the middleware chain and will setup
+ # the environment for the {#response} method to execute. This includes setting
+ # up a new Fiber, handing any execptions thrown from the API and executing
+ # the appropriate callback method for the API.
+ #
+ # @param env [Goliath::Env] The request environment
+ # @return [Goliath::Connection::AsyncResponse] An async response.
def call(env)
Fiber.new {
begin
@@ -63,6 +130,14 @@ def call(env)
Goliath::Connection::AsyncResponse
end
+ # Response is the main implementation method for Goliath APIs. All APIs
+ # should override this method in order to do any actual work.
+ #
+ # The response method will be executed in a new Fiber and wrapped in a
+ # begin rescue block to handle an thrown API errors.
+ #
+ # @param env [Goliath::Env] The request environment
+ # @return [Array] Array contains [Status code, Headers Hash, Body]
def response(env)
env.logger.error('You need to implement response')
[400, {}, {:error => 'No response implemented'}]
@@ -1,6 +1,11 @@
-# Most of this stuff is straight out of sinatra.
module Goliath
+ # The main execution class for Goliath. This will execute in the at_exit
+ # handler to run the server.
+ #
+ # @private
class Application
+ # Most of this stuff is straight out of sinatra.
+
CALLERS_TO_IGNORE = [ # :nodoc:
/\/goliath(\/(application))?\.rb$/, # all goliath code
/rubygems\/custom_require\.rb$/, # rubygems require hacks
@@ -2,6 +2,7 @@
require 'goliath/response'
module Goliath
+ # @private
class Connection < EM::Connection
attr_accessor :app, :request, :response
attr_reader :logger, :status, :config, :options
View
@@ -1,38 +1,90 @@
module Goliath
+ # Holds information for the current request.
+ #
+ # Goliath::Env also provides access to the logger, configuration information
+ # and anything else set into the config data during initialization.
class Env < Hash
+ # Create a new Goliath::Env object
+ #
+ # @return [Goliath::Env] The Goliath::Env object
def initialize
self[:start_time] = Time.now.to_f
self[:time] = Time.now.to_f
self[:trace] = []
end
+ # Add a trace timer with the given name into the environment. The tracer will
+ # provide information on the amount of time since the previous call to {#trace}
+ # or since the Goliath::Env object was initialized.
+ #
+ # @example
+ # trace("initialize hash")
+ # ....
+ # trace("Do something else")
+ #
+ # @param name [String] The name of the trace to add
def trace(name)
self[:trace].push([name, "%.2f" % ((Time.now.to_f - self[:time]) * 1000)])
self[:time] = Time.now.to_f
end
+ # Retrieve the tracer stats for this request environment. This can then be
+ # returned in the headers hash to in development to provide some simple
+ # timing information for the various API components.
+ #
+ # @example
+ # [200, {}, {:meta => {:trace => env.trace_stats}}, {}]
+ #
+ # @return [Array] Array of [name, time] pairs with a Total entry added.d
def trace_stats
self[:trace] + [['total', self[:trace].collect { |s| s[1].to_f }.inject(:+).to_s]]
end
+ # The on_close block will be executed when the connection to the client is closed.
+ # This is useful in streaming servers where we may have setup an EM::Channel or some
+ # timer that we need to be able to cancel.
+ #
+ # @example
+ # env.on_close do
+ # env.logger.info "Connection closed."
+ # end
+ #
+ # @param blk The block to execute.
def on_close(&blk)
self[Goliath::Request::ASYNC_CLOSE].callback &blk
end
+ # If the API is a streaming API this will send the provided data to the client.
+ # There will be no processing done on the data when this is called so it's the
+ # APIs responsibility to have the data formatted as needed.
+ #
+ # @param data [String] The data to send to the client.
def stream_send(data)
self[Goliath::Request::STREAM_SEND].call(data)
end
+ # If the API is a streaming API this will be executed by the API to signal that
+ # the stream is complete. This will close the connection with the client.
def stream_close
self[Goliath::Request::STREAM_CLOSE].call
end
+ # @param name [Symbol] The method to check if we respond to it.
+ # @return [Boolean] True if the Env responds to the method, false otherwise
def respond_to?(name)
return true if has_key?(name.to_s)
return true if self['config'] && self['config'].has_key?(name.to_s)
super
end
+ # The Goliath::Env will provide any of it's keys as a method. It will also provide
+ # any of the keys in the config object as methods. The methods will return
+ # the value of the key. If the key doesn't exist in either hash this will
+ # fall back to the standard method_missing implementation.
+ #
+ # @param name [Symbol] The method to look for
+ # @param args The arguments
+ # @param blk A block
def method_missing(name, *args, &blk)
return self[name.to_s] if has_key?(name.to_s)
return self['config'][name.to_s] if self['config'] && self['config'].has_key?(name.to_s)
@@ -1,12 +1,19 @@
+# The Goliath Framework
module Goliath
module_function
@env = 'development'
+ # Retrieves the current goliath environment
+ #
+ # @return [String] the current environment
def env
@env
end
+ # Sets the current goliath environment
+ #
+ # @param [String] env the environment string of [dev|prod|test]
def env=(env)
case(env)
when 'dev' then @env = 'development'
@@ -15,14 +22,23 @@ def env=(env)
end
end
+ # Determines if we are in the production environment
+ #
+ # @return [Boolean] true if current environemnt is production, false otherwise
def prod?
@env == 'production'
end
+ # Determines if we are in the development environment
+ #
+ # @return [Boolean] true if current environemnt is development, false otherwise
def dev?
@env == 'development'
end
+ # Determines if we are in the test environment
+ #
+ # @return [Boolean] true if current environemnt is test, false otherwise
def test?
@env == 'test'
end
@@ -1,4 +1,5 @@
module Goliath
+ # @private
class Headers
HEADER_FORMAT = "%s: %s\r\n".freeze
ALLOWED_DUPLICATES = %w(Set-Cookie Set-Cookie2 Warning WWW-Authenticate).freeze
Oops, something went wrong.

3 comments on commit 73044bb

@igrigorik
Member

Great stuff.

@mattetti
Member

Agreed, it's looking great, thanks for working on that Dan.

@dj2
Member
dj2 commented on 73044bb Feb 17, 2011

You're welcome. I like inline docs, just hadn't had time to write any before.

Please sign in to comment.