herbert 0.0.1

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.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ nbproject
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in herbert.gemspec
4
+ gemspec
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "herbert/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "herbert"
7
+ s.version = Herbert::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Pavel Kalvoda"]
10
+ s.email = ["me@pavelkalvoda.com","pavel@drinkwithabraham.com"]
11
+ #s.homepage = ""
12
+ s.summary = %q{Sinatra-based toolset for creating JSON API servers backed by Mongo & Memcached}
13
+ s.description = <<-desc
14
+ Herbert makes development of JSON REST API servers ridiculously simple.
15
+ It provides a bunch of useful helpers and conventions to speed up development.
16
+ Input validation, logs and advanced AJAX support are baked in.
17
+ Herbert is very lightweight and transparent, making it easy to use & modify.
18
+ desc
19
+
20
+ s.add_dependency("sinatra","= 1.2.6")
21
+ s.add_dependency("memcache-client")
22
+ s.add_dependency("mongo")
23
+ s.add_dependency("syslogger")
24
+ s.add_dependency("kwalify","= 0.7.2")
25
+ s.add_dependency("activesupport")
26
+ s.add_dependency("bson_ext",">= 1.3.1")
27
+
28
+ s.files = `git ls-files`.split("\n")
29
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
30
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
31
+ s.require_paths = ["lib"]
32
+ end
@@ -0,0 +1,2 @@
1
+ puts "file got loaded"
2
+
@@ -0,0 +1,3 @@
1
+ require 'herbert/loader'
2
+
3
+
@@ -0,0 +1,50 @@
1
+ module Herbert
2
+ module Ajaxify
3
+ Headers = {
4
+ 'Access-Control-Allow-Methods' => %w{POST GET PUT DELETE OPTIONS},
5
+ 'Access-Control-Allow-Headers' => %w{Content-Type X-Requested-With},
6
+ 'Access-Control-Allow-Origin' => %w{*},
7
+ 'Access-Control-Expose-Header' => %w{Content-Type Content-Length X-Build},
8
+ 'X-Build' => [Herbert::Utils.version]
9
+ }
10
+
11
+ def self.registered(app)
12
+ # Heeeaderzz!!! Gimme heaaaderzzz!!!
13
+ path = File.join(app.settings.root, 'config','headers.rb')
14
+ if File.exists?(path) then
15
+ log.h_debug("Loading additional headers from #{path}")
16
+ custom = eval(File.open(path).read)
17
+ custom.each {|name, value|
18
+ value = [value] unless value.is_a?(Array)
19
+ Headers[name] = (Headers[name] || []) | value
20
+ }
21
+ else
22
+ log.h_info("File #{path} doesn't exists; no addition headers loaded")
23
+ end
24
+
25
+ app.before do
26
+ # Add the headers to the response
27
+ Headers.each {|name, value|
28
+ value = [value] unless value.is_a?(Array)
29
+ value.map! {|val|
30
+ (val.is_a?(Proc) ? val.call : val).to_s
31
+ }
32
+ response[name] = value.join(', ')
33
+ }
34
+ end
35
+
36
+ # Proxy for not CORS enables services such as
37
+ # Google Maps
38
+ # /proxy/<url to fetch>
39
+ if app.get '/proxy/:url' do
40
+ url = URI.parse(URI.decode(params[:url]))
41
+ res = Net::HTTP.start(url.host, 80) {|http|
42
+ http.get(url.path + (url.query ? '?' + url.query : ''))
43
+ }
44
+ response['content-type'] = res['content-type']
45
+ res.body
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,90 @@
1
+ module Herbert
2
+ # Full-fledged request & response logger with
3
+ # several storage providers (console, mongo, cache)
4
+ class AppLogger
5
+ def self.provider
6
+ @@provider
7
+ end
8
+
9
+ def self.provider=(prov)
10
+ @@provider = prov
11
+ end
12
+
13
+ def self.log(request, response)
14
+ log = {
15
+ "request"=> {
16
+ "path"=> request.path,
17
+ "method"=> request.request_method,
18
+ "xhr" => request.xhr?,
19
+ "postData"=> request.POST,
20
+ "query"=> request.GET,
21
+ "headers"=> {},
22
+ "body"=> {
23
+ "isJson"=> request.json?,
24
+ "value"=> request.json? ? request.body : request.body_raw
25
+ },
26
+ "client"=> {
27
+ "ip"=> request.ip,
28
+ "hostname"=> request.host,
29
+ "referer"=> request.referer
30
+ }
31
+ },
32
+ "response"=> {
33
+ "code"=> response.status,
34
+ "headers"=> response.headers,
35
+ "body"=> {
36
+ "isJson"=> response.json?,
37
+ "value"=> response.body
38
+ },
39
+ },
40
+ "meta"=> {
41
+ "dateTime"=> Time.new,
42
+ "processingTime"=> response.app.timer_elapsed.round(3),
43
+ "port" => request.port
44
+ }
45
+ }
46
+
47
+ # Extract tha headerz
48
+ request.env.keys.each do |key|
49
+ if key =~ /^HTTP_/ then
50
+ log['request']['headers'][key.gsub(/^HTTP_/,'')] = request.env[key]
51
+ end
52
+ end
53
+ # do not log bodies from GET/DELETE/HEAD
54
+ log["request"].delete("body") if %{GET DELETE HEAD}.include?(log["request"]["method"])
55
+ # If an error occured, add it
56
+ log["response"]["error"] = request.env['sinatra.error'].to_hash if request.env['sinatra.error']
57
+ id = @@provider.save(log)
58
+ response['X-RequestId'] = id.to_s if @@provider.respond_to?(:id) && response.app.settings.append_log_id
59
+ end
60
+ end
61
+
62
+ module LoggingProviders
63
+ class StdoutProvider
64
+ def initialize
65
+ require 'pp'
66
+ end
67
+
68
+ def save(log)
69
+ pp log
70
+ end
71
+ end
72
+
73
+ class MongoProvider
74
+
75
+ Collection = 'logs'
76
+
77
+ def initialize(db)
78
+ @db = db
79
+ end
80
+
81
+ def save(log)
82
+ @db[Collection].save(log)
83
+ end
84
+
85
+ def id
86
+ true
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,61 @@
1
+ module Herbert
2
+
3
+ # Provides centralized handling of exceptions in an
4
+ # application context
5
+ module Error
6
+ # Error relevant in application context
7
+ class ApplicationError < StandardError
8
+ attr_reader :code, :message, :http_code, :errors
9
+
10
+ # Code to text translation
11
+ Translation = {
12
+ "1000" => ["Malformated JSON", 400],
13
+ "1001" => ["Non-unicode encoding",400],
14
+ "1002" => ["Non-acceptable Accept header", 406],
15
+ "1003" => ["Not found", 404],
16
+ "1010" => ["Missing request body", 400],
17
+ "1011" => ["Missign required parameter", 400],
18
+ "1012" => ["Invalid request body", 400],
19
+ "1020" => ["Unspecified error occured", 500]
20
+ }
21
+
22
+ def initialize(errno, http_code = nil, errors = [])
23
+ raise ArgumentError, "Unknown error code: #{errno}" unless Translation.has_key?(errno.to_s)
24
+ @code = errno
25
+ @message = Translation[@code.to_s][0]
26
+ @http_code = (http_code || Translation[@code.to_s][1])
27
+ @errors = errors.to_a
28
+ end
29
+
30
+ def to_hash
31
+ { :code => @code, :stackTrace => backtrace, :validationTrace => @errors}
32
+ end
33
+
34
+ # Add an error
35
+ def self.push(code, error)
36
+ Translation[code.to_s] = error.to_a
37
+ end
38
+
39
+ # Add a hash of errors
40
+ def self.merge(errors)
41
+ if errors.is_a? Hash then
42
+ Translation.merge!(errors)
43
+ else
44
+ raise ArgumentError("Expected a hash of codes and descriptions")
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+ end
51
+
52
+ module Sinatra
53
+ class NotFound
54
+ def to_hash
55
+ {
56
+ :code => 1003,
57
+ :message => "Not found"
58
+ }
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,49 @@
1
+ module Herbert
2
+ module Configurator
3
+ module Prepatch
4
+ def self.registered(app)
5
+ # Enable envs such as development;debug, where debug is herberts debug flag
6
+ env = ENV['RACK_ENV'].split(';')
7
+ ENV['RACK_ENV'], ENV['HERBERT_DEBUG'] = (env[0].empty? ? 'development' : env[0]), (env[1] == 'debug' ? 1:0).to_s
8
+ app.set :environment, ENV['RACK_ENV'].downcase.to_sym
9
+ end
10
+ end
11
+
12
+ module Helpers
13
+ def staging?
14
+ ENV['RACK_ENV'] == 'staging'
15
+ end
16
+
17
+ def development?
18
+ ENV['RACK_ENV'] == 'development' || (ENV['RACK_ENV'].empty?)
19
+ end
20
+
21
+ def debug?
22
+ ENV['HERBERT_DEBUG'] == '1'
23
+ end
24
+ end
25
+
26
+ def self.registered(app)
27
+ app.enable :logging if app.development?
28
+ #Assume loading by rackup...
29
+ app.settings.root ||= File.join(Dir.getwd, 'lib')
30
+ path = File.join(app.settings.root, 'config')
31
+ # Load and evaluate common.rb and appropriate settings
32
+ ['common.rb', app.environment.to_s + '.rb'].each do |file|
33
+ cpath = File.join(path, file)
34
+ if File.exists?(cpath) then
35
+ # Ummm, I'm sorry?
36
+ app.instance_eval(IO.read(cpath))
37
+ log.h_debug("Applying #{cpath} onto the application")
38
+ else
39
+ log.h_warn("Configuration file #{cpath} not found")
40
+ end
41
+ end
42
+ # So, we have all our settings... Please note that configure
43
+ # block inside an App can overwrite our settings, but Herbert's
44
+ # services are being created right now, so they only take in account
45
+ # previous declarations
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,60 @@
1
+ # coding: utf-8
2
+
3
+ require File.dirname(__FILE__) + '/ApplicationError.rb'
4
+
5
+ module Herbert
6
+ module Error
7
+ # Inclusion hook
8
+ def self.registered(app)
9
+ # Disable HTML errors and preliminary reporting
10
+ log.h_warn("Herbert is running in debugging mode - exceptions will be visualized") if app.debug?
11
+ app.set :raise_errors, false
12
+ app.set :show_exceptions, false
13
+ app.set :dump_errors, app.debug?
14
+ # Add a new error state handler which produces
15
+ # compact JSON error reports (handled by #Sinatra::Jsonify)
16
+ app.error do
17
+ err = request.env['sinatra.error']
18
+ if err.class == ApplicationError then
19
+ log.h_debug("Caught manageable error")
20
+ response.status = err.http_code
21
+ body = {
22
+ :error => {
23
+ :code => err.code,
24
+ :message => err.message
25
+ }
26
+ }
27
+ # Add backtrace, Kwalify validation report and other info if
28
+ # running in development mode
29
+ if settings.development? then
30
+ log.h_debug("Adding stacktrace and report to the error")
31
+ body[:error][:stacktrace] = err.backtrace.join("\n")
32
+ body[:error][:info] = (err.errors || [])
33
+ end
34
+ response.body = body
35
+ else
36
+ # If the exception is not manageable, bust it
37
+ log.h_error("A non-managed error occured! Backtrace: #{err.backtrace.join("\n")}")
38
+ response.status = 500
39
+ response.body = settings.development? ? err.to_s : nil
40
+ end
41
+ end
42
+
43
+ #Ummm, nasty.... FIXME
44
+ app.not_found do
45
+ content_type 'application/json', :charset => 'utf-8'
46
+ {:error => {
47
+ :code => 1003,
48
+ :message => "Not found"
49
+ }}
50
+ end
51
+ end
52
+
53
+ module Helpers
54
+ # Request-context helper of error states
55
+ def error(code = 1020, http_code = nil, errors = nil)
56
+ raise Herbert::Error::ApplicationError.new(code, http_code, errors)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,79 @@
1
+
2
+ class True
3
+ def to_json
4
+ return 1
5
+ end
6
+ end
7
+
8
+ module Sinatra
9
+
10
+ # Makes JSON the default DDL of HTTP communication
11
+ module Jsonify
12
+ # Sinatra inclusion hook
13
+ def self.registered(app)
14
+ app.before do
15
+ log.h_debug("Adding proper content-type and charset")
16
+ content_type 'application/json', :charset => 'utf-8'
17
+ end
18
+ end
19
+
20
+ class Sinatra::Request
21
+
22
+ @is_json = false
23
+ # Encapsulates #Rack::Request.body in order to remove #IO.String
24
+ # and therefore to enable repeated reads
25
+ def body_raw
26
+ @body_raw ||= body(true).read
27
+ @body_raw
28
+ end
29
+
30
+ def ensure_encoded(strict = true)
31
+ if !@is_json then
32
+ begin
33
+ @body_decoded ||= ActiveSupport::JSON.decode(body_raw)
34
+ @is_json = true;
35
+ rescue StandardError
36
+ @is_json = false;
37
+ raise ::Herbert::Error::ApplicationError.new(1000) if strict
38
+ end
39
+ end
40
+ end
41
+
42
+ # Overrides #Rack::Request.body, returns native #Hash
43
+ # Preserves access to underlying @env['rack.input'] #IO.String
44
+ def body(rack = false)
45
+ if rack then
46
+ super()
47
+ else
48
+ ensure_encoded
49
+ @body_decoded
50
+ end
51
+ end
52
+
53
+ def json?
54
+ ensure_encoded(false)
55
+ @is_json
56
+ end
57
+ end
58
+
59
+ class Sinatra::Response
60
+ # Reference to application instance that created this response
61
+ attr_accessor :app
62
+
63
+ # Automatically encode body to JSON, but only as long as
64
+ # the content-type remained set to app/json
65
+ def finish
66
+ @app.log_request
67
+ if json?
68
+ log.h_debug("Serializing response into JSON")
69
+ @body = [ActiveSupport::JSON.encode(@body)]
70
+ end
71
+ super
72
+ end
73
+
74
+ def json?
75
+ @header['Content-type'] === 'application/json;charset=utf-8'
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,30 @@
1
+ module Sinatra
2
+ module Log
3
+ def log_request
4
+ ::Herbert::AppLogger.log(request, response) if settings.log_requests
5
+ end
6
+
7
+ def timer_elapsed
8
+ return (@timer_stop.to_f - @timer_start.to_f)*100
9
+ end
10
+
11
+ module Extension
12
+ def self.registered(app)
13
+ case app.log_requests
14
+ when :db
15
+ provider = Herbert::LoggingProviders::MongoProvider.new(app.db)
16
+ when :stdout
17
+ provider = Herbert::LoggingProviders::StdoutProvider.new
18
+ else
19
+ app.log_requests.respond_to?(:save) ? provider = app.log_requests : log.h_fatal("Unknown logs storage provider.")
20
+ end
21
+ Herbert::AppLogger.provider = provider
22
+ # Make the app automatically inject refernce to iteself into the response,
23
+ # so Sinatra::Response::finish can manipulate it
24
+ app.before { response.app = self; @timer_start = Time.new }
25
+ app.after { @timer_stop = Time.new}
26
+ #app.before { log_request }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,49 @@
1
+ module Herbert
2
+ # This class allows you to organize code by REST resources.
3
+ # Any class that subclasses Herbert::Resource is automatically "merged"
4
+ # into the application. Resource name will be derived from the class name.
5
+ #
6
+ # For instance,
7
+ # class Messages < Herbert::Resource
8
+ # get '/' do
9
+ # "here's a message for you!"
10
+ # end
11
+ # end
12
+ # will respond to
13
+ # GET /messages/
14
+ #
15
+
16
+ class Resource
17
+ def self.new
18
+ raise StandardError.new('You are not allowed to instantize this class directly')
19
+ end
20
+
21
+ # Translates Sintra DSL calls
22
+ def self.inherited(subclass)
23
+ %w{get post put delete}.each do |verb|
24
+ subclass.define_singleton_method verb.to_sym do |route, &block|
25
+ app.send verb.to_sym, "/#{subclass.to_s.downcase}#{route}", &block
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ # Loads all Herbert resources
32
+ module ResourceLoader
33
+ def self.registered(app)
34
+ # Inject refence to the app into Resource
35
+ Resource.class_eval do
36
+ define_singleton_method :app do
37
+ app
38
+ end
39
+ end
40
+
41
+ # And load all resource definitions
42
+ path = File.join(app.settings.root, 'Resources')
43
+ Dir.new(path).each do |file|
44
+ next if %{. ..}.include? file
45
+ require File.join(path,file)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,90 @@
1
+ module Sinatra
2
+ module Database
3
+ def self.registered(app)
4
+ app.set :mongo_connection, Mongo::Connection.new(app.settings.db_settings[:host],
5
+ app.settings.db_settings[:porty],
6
+ app.settings.db_settings[:options])
7
+ log.h_debug("Connected to MongoDB #{app.settings.mongo_connection}")
8
+ app.set :mongo_db, app.settings.mongo_connection.db(app.settings.db_settings[:db_name])
9
+ end
10
+
11
+ def db
12
+ settings.mongo_db
13
+ end
14
+
15
+ end
16
+
17
+ module Cache
18
+ def self.registered(app)
19
+ servers = []
20
+ app.settings.cache[:servers].each {|c|
21
+ servers << (c[:host] + ':' + (c[:port] || 11211).to_s)
22
+ }
23
+ app.set :cache, MemCache.new(app.settings.cache[:options])
24
+ app.settings.cache.servers = servers
25
+ log.h_debug("Connected to Memcached #{app.settings.cache.inspect}")
26
+ end
27
+
28
+ def mc
29
+ settings.cache
30
+ end
31
+ end
32
+
33
+
34
+ module Validation
35
+ module Extension
36
+ # Um, dragons... Fucking swarm... But I'll try to explain this anyway.
37
+ # We'll scan the defined settings.validation[:path] dir for dirs. Those found dirs
38
+ # will denote <resource>s. Then, we will scan the "resource" dirs for files.
39
+ # These files will represent one http <verb>.yaml each. And then, we create hierchy of
40
+ # validation schemas following this pattern:
41
+ # ::setting.validation[:module]::<resource>::<verb_schema>
42
+ # where the <verb_schema> equals <verb>.capitalize and contains parsed contents of <verb>.yaml file.
43
+ # Please note that I haven't used a single (.*_)eval even though I was terribly tempted.
44
+ # And I also documented this method. I'm so awesome, considerate and drunk, am I not?
45
+ # Uh, yea, and notice the nice cascade of 'end's on the end
46
+ def self.registered(app)
47
+ # Define the ::<schema_root> module
48
+ validation_module = Kernel.const_set(app.settings.validation[:module], Module.new)
49
+ schema_root = Dir.new(File.join(app.settings.root, app.settings.validation[:path]))
50
+ log.h_debug("Loading validation schemas from #{schema_root.path}");
51
+ # For each resource
52
+ schema_root.each do |resource_dir|
53
+ next if %w{.. .}.include? resource_dir
54
+ resource_name = resource_dir
55
+ resource_dir = File.join(schema_root, resource_dir)
56
+ # Create <schema_root>::<resource> module
57
+ validation_module.const_set(resource_name, Module.new {})
58
+ if File.directory?(resource_dir) then
59
+ Dir.new(resource_dir).each do |verb|
60
+ next if %w{.. .}.include? verb
61
+ # And create the <schema_root>::<resource>::<verb_schema> constant
62
+ validation_module.const_get(resource_name).const_set(/(\w+).yaml/.match(verb)[1].capitalize, YAML.load_file(File.join(resource_dir, verb)))
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ module Helpers
70
+ # Only a few dragons here. This method validates body of the request
71
+ # against a schema. If no schema was passed to the method, it will
72
+ # try to find it automagically
73
+ def validate!(schema = nil)
74
+ schema ||= Kernel.const_get(
75
+ settings.validation[:module]
76
+ ).const_get(
77
+ /^\/(.*)\//.match(request.path)[1].capitalize
78
+ ).const_get(
79
+ request.env['REQUEST_METHOD'].downcase.capitalize
80
+ )
81
+ res = Kwalify::Validator.new(schema).validate(request.body)
82
+ res.map! { |error|
83
+ error.to_s
84
+ }
85
+ error(1012, nil, res) unless res == []
86
+ end
87
+ end
88
+ end
89
+
90
+ end
@@ -0,0 +1,10 @@
1
+ module Herbert
2
+ module Utils
3
+ # Assert v<major>.<minor>.<etc> tags
4
+ def self.version
5
+ version = `git describe --long`.strip
6
+ version[0] = '' if version[0] == 'v'
7
+ version
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,63 @@
1
+ require 'logger'
2
+ require 'mongo'
3
+ require 'memcache'
4
+ require 'sinatra/base'
5
+ require 'kwalify'
6
+ require 'active_support'
7
+
8
+ module Herbert
9
+ ::Logger.class_eval do
10
+ # prefix all Herbert's log with [Herbert]
11
+ [:fatal, :error, :warn, :info, :debug].each do |type|
12
+ name = "h_" + type.to_s
13
+ define_method name do |message|
14
+ send(type, "[Herbert] " + message)
15
+ end
16
+ end
17
+ end
18
+
19
+ # Plug it in, the monkey style :]
20
+ module ::Kernel
21
+ @@logger = Logger.new(STDOUT)
22
+ def log
23
+ @@logger
24
+ end
25
+ end
26
+
27
+ module Loader
28
+ $HERBERT_PATH = File.dirname(__FILE__)
29
+ log.h_info("Here comes Herbert. He's a berserker!")
30
+ # because order matters
31
+ %w{Utils Jsonify Configurator Error Services Ajaxify AppLogger Log Resource}.each {|file|
32
+ require $HERBERT_PATH + "/#{file}.rb"
33
+ }
34
+
35
+ def self.registered(app)
36
+ # Set some default
37
+ # TODO to external file?
38
+ app.set :log_requests, :db
39
+ app.enable :append_log_id # If logs go to Mongo, IDs will be appended to responses
40
+ ## register the ;debug flag patch first to enable proper logging
41
+ app.register Herbert::Configurator::Prepatch
42
+ # the logger
43
+ log.level = app.development? ? Logger::DEBUG : Logger::INFO
44
+ # the extensions
45
+ app.register Herbert::Configurator
46
+ app.register Herbert::Configurator::Helpers
47
+ app.helpers Herbert::Configurator::Helpers
48
+ app.register Herbert::Error
49
+ app.helpers Herbert::Error::Helpers
50
+ app.register Sinatra::Jsonify
51
+ app.register Sinatra::Database
52
+ app.helpers Sinatra::Database
53
+ app.register Sinatra::Cache
54
+ app.helpers Sinatra::Cache
55
+ app.register Sinatra::Validation::Extension
56
+ app.helpers Sinatra::Validation::Helpers
57
+ app.register Herbert::Ajaxify
58
+ app.helpers Sinatra::Log
59
+ app.register Sinatra::Log::Extension
60
+ app.register Herbert::ResourceLoader
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,3 @@
1
+ module Herbert
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,184 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: herbert
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Pavel Kalvoda
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-06-18 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: sinatra
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - "="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 1
30
+ - 2
31
+ - 6
32
+ version: 1.2.6
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: memcache-client
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: mongo
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ type: :runtime
60
+ version_requirements: *id003
61
+ - !ruby/object:Gem::Dependency
62
+ name: syslogger
63
+ prerelease: false
64
+ requirement: &id004 !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ type: :runtime
73
+ version_requirements: *id004
74
+ - !ruby/object:Gem::Dependency
75
+ name: kwalify
76
+ prerelease: false
77
+ requirement: &id005 !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - "="
81
+ - !ruby/object:Gem::Version
82
+ segments:
83
+ - 0
84
+ - 7
85
+ - 2
86
+ version: 0.7.2
87
+ type: :runtime
88
+ version_requirements: *id005
89
+ - !ruby/object:Gem::Dependency
90
+ name: activesupport
91
+ prerelease: false
92
+ requirement: &id006 !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ segments:
98
+ - 0
99
+ version: "0"
100
+ type: :runtime
101
+ version_requirements: *id006
102
+ - !ruby/object:Gem::Dependency
103
+ name: bson_ext
104
+ prerelease: false
105
+ requirement: &id007 !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ segments:
111
+ - 1
112
+ - 3
113
+ - 1
114
+ version: 1.3.1
115
+ type: :runtime
116
+ version_requirements: *id007
117
+ description: |
118
+ Herbert makes development of JSON REST API servers ridiculously simple.
119
+ It provides a bunch of useful helpers and conventions to speed up development.
120
+ Input validation, logs and advanced AJAX support are baked in.
121
+ Herbert is very lightweight and transparent, making it easy to use & modify.
122
+
123
+ email:
124
+ - me@pavelkalvoda.com
125
+ - pavel@drinkwithabraham.com
126
+ executables: []
127
+
128
+ extensions: []
129
+
130
+ extra_rdoc_files: []
131
+
132
+ files:
133
+ - .gitignore
134
+ - Gemfile
135
+ - Rakefile
136
+ - herbert.gemspec
137
+ - lib/file.rb
138
+ - lib/herbert.rb
139
+ - lib/herbert/Ajaxify.rb
140
+ - lib/herbert/AppLogger.rb
141
+ - lib/herbert/ApplicationError.rb
142
+ - lib/herbert/Configurator.rb
143
+ - lib/herbert/Error.rb
144
+ - lib/herbert/Jsonify.rb
145
+ - lib/herbert/Log.rb
146
+ - lib/herbert/Resource.rb
147
+ - lib/herbert/Services.rb
148
+ - lib/herbert/Utils.rb
149
+ - lib/herbert/loader.rb
150
+ - lib/herbert/version.rb
151
+ has_rdoc: true
152
+ homepage:
153
+ licenses: []
154
+
155
+ post_install_message:
156
+ rdoc_options: []
157
+
158
+ require_paths:
159
+ - lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ segments:
166
+ - 0
167
+ version: "0"
168
+ required_rubygems_version: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ segments:
174
+ - 0
175
+ version: "0"
176
+ requirements: []
177
+
178
+ rubyforge_project:
179
+ rubygems_version: 1.3.7
180
+ signing_key:
181
+ specification_version: 3
182
+ summary: Sinatra-based toolset for creating JSON API servers backed by Mongo & Memcached
183
+ test_files: []
184
+