herbert 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+