Permalink
Browse files

Specs passing with newly modularized structure and config system

  • Loading branch information...
1 parent 8acc488 commit c5c775e38d9e145d580cf4f7cebacf605bd7ffea @winton committed Dec 6, 2009
View
@@ -0,0 +1,50 @@
+require 'builder'
+require 'net/http'
+require 'rack'
+
+lib = File.dirname(__FILE__)
+require "#{lib}/lilypad/config"
+require "#{lib}/lilypad/config/request"
+require "#{lib}/lilypad/log"
+require "#{lib}/lilypad/hoptoad/deploy"
+require "#{lib}/lilypad/hoptoad/notify"
+require "#{lib}/lilypad/hoptoad/xml"
+require "#{lib}/rack/lilypad"
+
+class Lilypad
+ class <<self
+
+ def active?
+ Config.api_key
+ end
+
+ def config(api_key=nil, &block)
+ if api_key
+ Config.api_key api_key
+ end
+ if block_given?
+ Config.class_eval &block
+ end
+ end
+
+ def deploy(options)
+ if active? && production?
+ Hoptoad::Deploy.new options
+ end
+ end
+
+ def notify(exception, env=nil)
+ if active? && production?
+ Hoptoad::Notify.new env, exception
+ end
+ end
+
+ def production?
+ Config.environments.include? ENV['RACK_ENV']
+ end
+ end
+end
+
+def Lilypad(api_key=nil, &block)
+ Lilypad.config api_key, &block
+end
@@ -0,0 +1,23 @@
+class Lilypad
+ module Rails
+
+ def self.included(base)
+ ENV['RACK_ENV'] = ENV['RAILS_ENV']
+ base.send(:include, LilypadMethods) if Lilypad.production?
+ end
+
+ module LilypadMethods
+
+ private
+
+ def rescue_action_without_handler(exception)
+ super
+ Config::Request.action params[:action]
+ Config::Request.component params[:controller]
+ raise exception
+ end
+ end
+ end
+end
+
+ActionController::Base.send(:include, Lilypad::Rails)
@@ -0,0 +1,10 @@
+class Lilypad
+ module Sinatra
+
+ def self.included(base)
+ base.set(:raise_errors, true) if Lilypad.production?
+ end
+ end
+end
+
+Sinatra::Base.send(:include, Lilypad::Sinatra)
View
@@ -0,0 +1,63 @@
+class Lilypad
+ class Config
+ class <<self
+
+ def api_key(api_key=nil, &block)
+ @api_key = api_key unless api_key.nil?
+ @api_key_block = block if block_given?
+ @api_key || @api_key_block
+ end
+
+ def deploy_url(url=nil)
+ @deploy_url = url unless url.nil?
+ @deploy_url || "http://hoptoadapp.com/deploys.txt"
+ end
+
+ def environments(environments=nil)
+ @environments = environments unless environments.nil?
+ @environments || %w(production staging)
+ end
+
+ def filters(filters=nil)
+ @filters = filters unless filters.nil?
+ @filters
+ end
+
+ def log(log=nil)
+ @log = log unless log.nil?
+ @log
+ end
+
+ def notify_url(url=nil)
+ @notify_url = url unless url.nil?
+ @url || "http://hoptoadapp.com:80/notify_url/v2/notices"
+ end
+
+ def rails
+ require "#{File.dirname(__FILE__)}/adapters/rails"
+ end
+
+ def reset!
+ self.instance_variables.each do |name|
+ eval "#{name} = nil"
+ end
+ end
+
+ def sinatra
+ require "#{File.dirname(__FILE__)}/adapters/sinatra"
+ end
+
+ end
+
+ module Methods
+
+ def api_key(env=nil, exception=nil)
+ if Config.api_key.respond_to?(:call)
+ Config.api_key.call env, exception
+ else
+ Config.api_key
+ end
+ end
+ end
+ end
+end
@@ -0,0 +1,24 @@
+class Lilypad
+ class Config
+ class Request
+ class <<self
+
+ def action(action=nil)
+ @action = action unless action.nil?
+ @action
+ end
+
+ def component(component=nil)
+ @component = component unless component.nil?
+ @component
+ end
+
+ def reset!
+ self.instance_variables.each do |name|
+ eval "#{name} = nil"
+ end
+ end
+ end
+ end
+ end
+end
@@ -0,0 +1,42 @@
+class Lilypad
+ class Hoptoad
+ class Deploy
+
+ include Config::Methods
+ include Log::Methods
+
+ def initialize(options)
+ @options = options
+
+ begin
+ post
+ rescue Exception => e
+ end
+
+ log :debug, @response
+ success?
+ end
+
+ private
+
+ def params
+ {
+ 'api_key' => api_key,
+ 'deploy[local_username]' => @options[:username],
+ 'deploy[rails_env]' => @options[:environment],
+ 'deploy[scm_revision]' => @options[:revision],
+ 'deploy[scm_repository]' => @options[:repository]
+ }
+ end
+
+ def post
+ url = URI.parse Config.deploy_url
+ @response = Net::HTTP.post_form url, params
+ end
+
+ def success?
+ @response.class.superclass == Net::HTTPSuccess
+ end
+ end
+ end
+end
@@ -0,0 +1,83 @@
+class Lilypad
+ class Hoptoad
+ class Notify
+
+ include Log::Methods
+
+ def initialize(env, exception)
+ @exception = exception
+ @env = env
+
+ http_start do |http|
+ begin
+ xml = XML.build *parse
+ http.post @uri.path, xml, headers
+ rescue Exception => e
+ end
+ end
+
+ if env && success?
+ env['hoptoad.notified'] = true
+ end
+
+ Config::Request.reset!
+ log :notify, @response
+ success?
+ end
+
+ private
+
+ def backtrace
+ regex = %r{^([^:]+):(\d+)(?::in `([^']+)')?$}
+ @exception.backtrace.map do |line|
+ _, file, number, method = line.match(regex).to_a
+ Backtrace.new file, number, method
+ end
+ end
+
+ def filter(hash)
+ return unless Config.filters
+ hash.inject({}) do |acc, (key, val)|
+ match = Config.filters.any? { |f| key.to_s =~ Regexp.new(f) }
+ acc[key] = match ? "[FILTERED]" : val
+ acc
+ end
+ end
+
+ def headers
+ {
+ 'Content-type' => 'text/xml',
+ 'Accept' => 'text/xml, application/xml'
+ }
+ end
+
+ def http_start(&block)
+ @uri = URI.parse Config.notify_url
+ Net::HTTP.start @uri.host, @uri.port do |http|
+ http.read_timeout = 5 # seconds
+ http.open_timeout = 2 # seconds
+ @response = yield http
+ end
+ end
+
+ def parse
+ env = filter ENV.to_hash.merge(@env || {})
+ if @env
+ request = Rack::Request.new @env
+ request_path = request.script_name + request.path_info
+ else
+ request = nil
+ request_path = 'Internal'
+ end
+ [ backtrace, env, @exception, request, request_path ]
+ end
+
+ def success?
+ @response.class.superclass == Net::HTTPSuccess
+ end
+
+ class Backtrace < Struct.new(:file, :number, :method)
+ end
+ end
+ end
+end
View
@@ -0,0 +1,61 @@
+class Lilypad
+ class Hoptoad
+ class XML
+ class <<self
+
+ include Config::Methods
+
+ def build(backtrace, env, exception, request, request_path)
+ @@last_request = nil
+ xml = ::Builder::XmlMarkup.new
+ xml.instruct! :xml, :version => "1.0", :encoding => "UTF-8"
+ xml.notice :version => '2.0.0' do |n|
+ n.tag! 'api-key', api_key
+ n.notifier do |n|
+ n.name 'Lilypad'
+ n.url 'http://github.com/winton/lilypad'
+ n.version '0.2.4'
+ end
+ n.error do |e|
+ e.tag! 'class', exception.class.name
+ e.message exception.message
+ e.backtrace do |b|
+ backtrace.each do |line|
+ b.line :method => line.method, :file => line.file, :number => line.number
+ end
+ end
+ end
+ n.request do |r|
+ r.action Config::Request.action
+ r.component Config::Request.component || request_path
+ r.url request_path
+ if request && request.params.any?
+ r.params do |p|
+ request.params.each do |key, value|
+ p.var value.to_s, :key => key
+ end
+ end
+ end
+ if env.any?
+ r.tag! 'cgi-data' do |c|
+ env.each do |key, value|
+ c.var value.to_s, :key => key
+ end
+ end
+ end
+ end
+ n.tag! 'server-environment' do |s|
+ s.tag! 'project-root', Dir.pwd
+ s.tag! 'environment-name', ENV['RACK_ENV'] || 'development'
+ end
+ end
+ @@last_request = xml.target!
+ end
+
+ def last_request
+ @@last_request
+ end
+ end
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit c5c775e

Please sign in to comment.