manveru-innate 2009.02.06
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +1409 -0
- data/COPYING +18 -0
- data/MANIFEST +100 -0
- data/README.md +485 -0
- data/Rakefile +139 -0
- data/example/app/retro_games.rb +57 -0
- data/example/app/whywiki_erb/layout/wiki.html.erb +15 -0
- data/example/app/whywiki_erb/spec/wiki.rb +19 -0
- data/example/app/whywiki_erb/start.rb +45 -0
- data/example/app/whywiki_erb/view/edit.html.erb +6 -0
- data/example/app/whywiki_erb/view/index.html.erb +10 -0
- data/example/custom_middleware.rb +43 -0
- data/example/error_handling.rb +31 -0
- data/example/hello.rb +12 -0
- data/example/howto_spec.rb +60 -0
- data/example/link.rb +35 -0
- data/example/providing_hash.rb +46 -0
- data/example/session.rb +42 -0
- data/innate.gemspec +118 -0
- data/lib/innate.rb +191 -0
- data/lib/innate/action.rb +156 -0
- data/lib/innate/adapter.rb +89 -0
- data/lib/innate/cache.rb +117 -0
- data/lib/innate/cache/api.rb +106 -0
- data/lib/innate/cache/drb.rb +58 -0
- data/lib/innate/cache/file_based.rb +39 -0
- data/lib/innate/cache/marshal.rb +17 -0
- data/lib/innate/cache/memory.rb +22 -0
- data/lib/innate/cache/yaml.rb +17 -0
- data/lib/innate/core_compatibility/basic_object.rb +9 -0
- data/lib/innate/core_compatibility/string.rb +3 -0
- data/lib/innate/current.rb +37 -0
- data/lib/innate/dynamap.rb +81 -0
- data/lib/innate/helper.rb +195 -0
- data/lib/innate/helper/aspect.rb +62 -0
- data/lib/innate/helper/cgi.rb +39 -0
- data/lib/innate/helper/flash.rb +36 -0
- data/lib/innate/helper/link.rb +55 -0
- data/lib/innate/helper/partial.rb +90 -0
- data/lib/innate/helper/redirect.rb +85 -0
- data/lib/innate/helper/send_file.rb +18 -0
- data/lib/innate/log.rb +23 -0
- data/lib/innate/log/color_formatter.rb +43 -0
- data/lib/innate/log/hub.rb +72 -0
- data/lib/innate/mock.rb +49 -0
- data/lib/innate/node.rb +471 -0
- data/lib/innate/options.rb +91 -0
- data/lib/innate/options/dsl.rb +155 -0
- data/lib/innate/request.rb +165 -0
- data/lib/innate/response.rb +18 -0
- data/lib/innate/route.rb +109 -0
- data/lib/innate/session.rb +104 -0
- data/lib/innate/session/flash.rb +94 -0
- data/lib/innate/setup.rb +23 -0
- data/lib/innate/spec.rb +42 -0
- data/lib/innate/state.rb +22 -0
- data/lib/innate/state/accessor.rb +130 -0
- data/lib/innate/state/fiber.rb +68 -0
- data/lib/innate/state/thread.rb +39 -0
- data/lib/innate/traited.rb +20 -0
- data/lib/innate/trinity.rb +22 -0
- data/lib/innate/version.rb +3 -0
- data/lib/innate/view.rb +67 -0
- data/lib/innate/view/erb.rb +17 -0
- data/lib/innate/view/none.rb +9 -0
- data/lib/rack/middleware_compiler.rb +62 -0
- data/lib/rack/reloader.rb +192 -0
- data/spec/example/hello.rb +14 -0
- data/spec/example/link.rb +29 -0
- data/spec/helper.rb +2 -0
- data/spec/innate/cache/common.rb +45 -0
- data/spec/innate/cache/marshal.rb +5 -0
- data/spec/innate/cache/memory.rb +5 -0
- data/spec/innate/cache/yaml.rb +5 -0
- data/spec/innate/dynamap.rb +22 -0
- data/spec/innate/helper.rb +66 -0
- data/spec/innate/helper/aspect.rb +80 -0
- data/spec/innate/helper/cgi.rb +37 -0
- data/spec/innate/helper/flash.rb +148 -0
- data/spec/innate/helper/link.rb +82 -0
- data/spec/innate/helper/partial.rb +66 -0
- data/spec/innate/helper/redirect.rb +148 -0
- data/spec/innate/helper/send_file.rb +21 -0
- data/spec/innate/helper/view/aspect_hello.erb +1 -0
- data/spec/innate/helper/view/locals.erb +1 -0
- data/spec/innate/helper/view/loop.erb +4 -0
- data/spec/innate/helper/view/num.erb +1 -0
- data/spec/innate/helper/view/partial.erb +1 -0
- data/spec/innate/helper/view/recursive.erb +8 -0
- data/spec/innate/mock.rb +84 -0
- data/spec/innate/node.rb +180 -0
- data/spec/innate/node/bar.html +1 -0
- data/spec/innate/node/foo.html.erb +1 -0
- data/spec/innate/node/with_layout.erb +3 -0
- data/spec/innate/options.rb +90 -0
- data/spec/innate/parameter.rb +154 -0
- data/spec/innate/request.rb +73 -0
- data/spec/innate/route.rb +129 -0
- data/spec/innate/session.rb +59 -0
- data/spec/innate/traited.rb +55 -0
- metadata +160 -0
@@ -0,0 +1,89 @@
|
|
1
|
+
module Rack
|
2
|
+
module Handler
|
3
|
+
autoload :Thin, 'rack/handler/thin'
|
4
|
+
autoload :Ebb, 'ebb'
|
5
|
+
autoload :SwiftipliedMongrel, 'rack/handler/swiftiplied_mongrel'
|
6
|
+
|
7
|
+
register 'thin', 'Rack::Handler::Thin'
|
8
|
+
register 'ebb', 'Rack::Handler::Ebb'
|
9
|
+
register 'smongrel', 'Rack::Handler::SwiftipliedMongrel'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Innate
|
14
|
+
|
15
|
+
# Lightweight wrapper around Rack::Handler, will apply our options in a
|
16
|
+
# unified manner and deal with adapters that don't like to do what we want or
|
17
|
+
# where Rack doesn't want to take a stand.
|
18
|
+
|
19
|
+
module Adapter
|
20
|
+
class << self
|
21
|
+
|
22
|
+
# Pass given app to the Handler, handler is chosen based on
|
23
|
+
# config.adapter option.
|
24
|
+
# If there is a method named start_name_of_adapter it will be run instead
|
25
|
+
# of the default run method of the handler, this makes it easy to define
|
26
|
+
# custom startup of handlers for your server of choice
|
27
|
+
def start(app, options = Innate.options)
|
28
|
+
adapter_name = options[:adapter].to_s.downcase
|
29
|
+
config = { :Host => options[:host], :Port => options[:port] }
|
30
|
+
Log.debug "Innate uses #{adapter_name}"
|
31
|
+
|
32
|
+
if respond_to?(method = "start_#{adapter_name}")
|
33
|
+
send(method, app, config)
|
34
|
+
else
|
35
|
+
Rack::Handler.get(adapter_name).run(app, config)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Due to buggy autoload on Ruby 1.8 we have to require 'ebb' manually.
|
40
|
+
# This most likely happens because autoload doesn't respect the require
|
41
|
+
# of rubygems and uses the C require directly.
|
42
|
+
def start_ebb(app, config)
|
43
|
+
require 'ebb'
|
44
|
+
Rack::Handler.get('ebb').run(app, config)
|
45
|
+
end
|
46
|
+
|
47
|
+
# We want webrick to use our logger.
|
48
|
+
|
49
|
+
def start_webrick(app, config)
|
50
|
+
handler = Rack::Handler.get('webrick')
|
51
|
+
config = {
|
52
|
+
:BindAddress => config[:Host],
|
53
|
+
:Port => config[:Port],
|
54
|
+
:Logger => Log,
|
55
|
+
:AccessLog => [
|
56
|
+
[Log, ::WEBrick::AccessLog::COMMON_LOG_FORMAT],
|
57
|
+
[Log, ::WEBrick::AccessLog::REFERER_LOG_FORMAT]]
|
58
|
+
}
|
59
|
+
|
60
|
+
handler.run(app, config)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Thin shouldn't give excessive output, especially not to $stdout
|
64
|
+
|
65
|
+
def start_thin(app, config)
|
66
|
+
require 'thin'
|
67
|
+
handler = Rack::Handler.get('thin')
|
68
|
+
::Thin::Logging.silent = true
|
69
|
+
handler.run(app, config)
|
70
|
+
end
|
71
|
+
|
72
|
+
# swiftcore has its own handler outside of rack
|
73
|
+
|
74
|
+
def start_emongrel(app, config)
|
75
|
+
require 'swiftcore/evented_mongrel'
|
76
|
+
handler = Rack::Handler.get('emongrel')
|
77
|
+
handler.run(app, config)
|
78
|
+
end
|
79
|
+
|
80
|
+
# swiftcore has its own handler outside of rack
|
81
|
+
|
82
|
+
def start_smongrel(app, config)
|
83
|
+
require 'swiftcore/swiftiplied_mongrel'
|
84
|
+
handler = Rack::Handler.get('smongrel')
|
85
|
+
handler.run(app, config)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/innate/cache.rb
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
module Innate
|
2
|
+
# Cache manager and wrapper.
|
3
|
+
#
|
4
|
+
# Provides a convenient wrapper around caches to keep method name confusion
|
5
|
+
# at a minimum while still having short and meaningful method names for every
|
6
|
+
# cache instance.
|
7
|
+
#
|
8
|
+
# The default caching is specified in lib/innate.rb in the config section.
|
9
|
+
# At the time of writing it defaults to Innate::Cache::Memory but can be
|
10
|
+
# changed easily.
|
11
|
+
#
|
12
|
+
# Configuration has to be done before Innate::setup_dependencies is being
|
13
|
+
# called.
|
14
|
+
#
|
15
|
+
# Configuration:
|
16
|
+
#
|
17
|
+
# Innate.options.cache do |cache|
|
18
|
+
# cache.names = [:session, :user]
|
19
|
+
# cache.session = Innate::Cache::Marshal
|
20
|
+
# cache.user = Innate::Cache::YAML
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# Usage for storing:
|
24
|
+
#
|
25
|
+
# # Storing with a time to live (10 seconds)
|
26
|
+
# Innate::Cache.user.store(:manveru, "Michael Fellinger", :ttl => 10)
|
27
|
+
#
|
28
|
+
# # Storing indefinitely
|
29
|
+
# Innate::Cache.user[:Pistos] = "unknown"
|
30
|
+
# # or without :ttl argument
|
31
|
+
# Innate::Cache.user.store(:Pistos, "unknown")
|
32
|
+
#
|
33
|
+
# Usage for retrieving:
|
34
|
+
#
|
35
|
+
# # we stored this one for 10 seconds
|
36
|
+
# Innate::Cache.user.fetch(:manveru, 'not here anymore')
|
37
|
+
# # => "Michael Fellinger"
|
38
|
+
# sleep 11
|
39
|
+
# Innate::Cache.user.fetch(:manveru, 'not here anymore')
|
40
|
+
# # => "not here anymore"
|
41
|
+
#
|
42
|
+
# Innate::Cache.user[:Pistos]
|
43
|
+
# # => "unknown"
|
44
|
+
# Innate::Cache.user.fetch(:Pistos)
|
45
|
+
# # => "unknown"
|
46
|
+
#
|
47
|
+
#
|
48
|
+
# For more details and to find out how to implement your own cache please
|
49
|
+
# read the documentation of Innate::Cache::API
|
50
|
+
#
|
51
|
+
# NOTE:
|
52
|
+
# * Some caches might expose their contents for everyone else on the same
|
53
|
+
# system, or even on connected systems. The rule as usual is, not to
|
54
|
+
# cache sensitive information.
|
55
|
+
|
56
|
+
class Cache
|
57
|
+
autoload :API, 'innate/cache/api'
|
58
|
+
autoload :DRb, 'innate/cache/drb'
|
59
|
+
autoload :YAML, 'innate/cache/yaml'
|
60
|
+
autoload :Memory, 'innate/cache/memory'
|
61
|
+
autoload :Marshal, 'innate/cache/marshal'
|
62
|
+
autoload :FileBased, 'innate/cache/file_based'
|
63
|
+
|
64
|
+
attr_reader :name, :instance
|
65
|
+
|
66
|
+
def initialize(name, klass = nil)
|
67
|
+
@name = name.to_s.dup.freeze
|
68
|
+
|
69
|
+
options = Innate.options
|
70
|
+
|
71
|
+
klass ||= options[:cache, @name.to_sym]
|
72
|
+
@instance = klass.new
|
73
|
+
|
74
|
+
@instance.cache_setup(
|
75
|
+
options.env.host,
|
76
|
+
options.env.user,
|
77
|
+
options.app.name,
|
78
|
+
@name
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.setup
|
83
|
+
Innate.options.cache.names.each{|name| add(name) }
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.register(cache)
|
87
|
+
key = cache.name
|
88
|
+
source = "def self.%s() @%s; end
|
89
|
+
def self.%s=(o) @%s = o; end" % [key, key, key, key]
|
90
|
+
self.class_eval(source, __FILE__, __LINE__)
|
91
|
+
|
92
|
+
self.send("#{key}=", cache)
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.add(name)
|
96
|
+
register(new(name))
|
97
|
+
end
|
98
|
+
|
99
|
+
def clear
|
100
|
+
instance.cache_clear
|
101
|
+
end
|
102
|
+
|
103
|
+
def delete(*keys)
|
104
|
+
instance.cache_delete(*keys)
|
105
|
+
end
|
106
|
+
|
107
|
+
def fetch(key, default = nil)
|
108
|
+
instance.cache_fetch(key, default)
|
109
|
+
end
|
110
|
+
alias [] fetch
|
111
|
+
|
112
|
+
def store(key, value, options = {})
|
113
|
+
instance.cache_store(key, value, options)
|
114
|
+
end
|
115
|
+
alias []= store
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module Innate
|
2
|
+
class Cache
|
3
|
+
|
4
|
+
# This is the API every Cache has to conform to.
|
5
|
+
#
|
6
|
+
# The default behaviour is tailored for the Memory cache, override any
|
7
|
+
# behaviour as you need.
|
8
|
+
#
|
9
|
+
# +key+ may be a String or Symbol
|
10
|
+
# +value+ is a Hash of serializable (as according to Marshal) objects
|
11
|
+
#
|
12
|
+
# Every cache instance has to respond to:
|
13
|
+
#
|
14
|
+
# ::new()
|
15
|
+
# #cache_setup(hostname, username, appname, cachename)
|
16
|
+
# #cache_clear()
|
17
|
+
# #cache_delete(*keys)
|
18
|
+
# #cache_fetch(key, default = nil)
|
19
|
+
# #cache_store(key, value, options = {})
|
20
|
+
#
|
21
|
+
# We are prefixing cache_ to make the intent clear and implementation
|
22
|
+
# easier, as there may be existing behaviour associated with the
|
23
|
+
# non-prefixed version.
|
24
|
+
#
|
25
|
+
# Also note that we create one instance per cache name-space.
|
26
|
+
module API
|
27
|
+
# Executed after #initialize and before any other method.
|
28
|
+
#
|
29
|
+
# Some parameters identifying the current process will be passed so
|
30
|
+
# caches that act in one global name-space can use them as a prefix.
|
31
|
+
#
|
32
|
+
# Treat all arguments as Strings.
|
33
|
+
#
|
34
|
+
# +hostname+ the hostname of the machine.
|
35
|
+
# +username+ user executing this process.
|
36
|
+
# +appname+ identifier for the application being executed.
|
37
|
+
# +cachename+ name-space of the cache, like 'session' or 'action'
|
38
|
+
def cache_setup(hostname, username, appname, cachename)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Remove all key/value pairs from the cache.
|
42
|
+
# Should behave as if #delete had been called with all +keys+ as argument.
|
43
|
+
def cache_clear
|
44
|
+
clear
|
45
|
+
end
|
46
|
+
|
47
|
+
# Remove the corresponding key/value pair for each key passed.
|
48
|
+
# If removing is not an option it should set the corresponding value to nil.
|
49
|
+
#
|
50
|
+
# If only one key was deleted, answer with the corresponding value.
|
51
|
+
# If multiple keys were deleted, answer with an Array containing the values.
|
52
|
+
def cache_delete(key, *keys)
|
53
|
+
if keys.empty?
|
54
|
+
if value = yield(key)
|
55
|
+
value[:value]
|
56
|
+
end
|
57
|
+
else
|
58
|
+
[key, *keys].map{|k| cache_delete(k) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Answer with the value associated with the +key+, +nil+ if not found or
|
63
|
+
# expired.
|
64
|
+
def cache_fetch(key, default = nil)
|
65
|
+
value = default
|
66
|
+
|
67
|
+
if entry = yield(key)
|
68
|
+
if expires = entry[:expires]
|
69
|
+
if expires > Time.now
|
70
|
+
value = entry[:value]
|
71
|
+
else
|
72
|
+
cache_delete(key)
|
73
|
+
end
|
74
|
+
else
|
75
|
+
value = entry[:value]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
return value
|
80
|
+
end
|
81
|
+
|
82
|
+
# Set +key+ to +value+.
|
83
|
+
#
|
84
|
+
# +options+ may be one of:
|
85
|
+
# :ttl => time to live in seconds if given in Numeric
|
86
|
+
# infinite or maximum if not given
|
87
|
+
#
|
88
|
+
# Usage:
|
89
|
+
# Cache.value.store(:num, 3, :ttl => 20)
|
90
|
+
# Cache.value.fetch(:num) # => 3
|
91
|
+
# sleep 21
|
92
|
+
# Cache.value.fetch(:num) # => nil
|
93
|
+
#
|
94
|
+
def cache_store(key, value, options = {})
|
95
|
+
ttl = options[:ttl]
|
96
|
+
|
97
|
+
value_hash = {:value => value}
|
98
|
+
value_hash[:expires] = Time.now + ttl if ttl
|
99
|
+
|
100
|
+
yield(key, value_hash)
|
101
|
+
|
102
|
+
return value
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'drb'
|
2
|
+
|
3
|
+
module Innate
|
4
|
+
module Cache
|
5
|
+
|
6
|
+
# Cache utilizing a DRb server.
|
7
|
+
#
|
8
|
+
# You will need to run a corresponding DRb server to use this cache. The
|
9
|
+
# example below is using a normal Hash, but it is recommended to use a
|
10
|
+
# thread-safe alternative like SyncHash.
|
11
|
+
#
|
12
|
+
# Example for DRb server:
|
13
|
+
#
|
14
|
+
# require 'drb'
|
15
|
+
#
|
16
|
+
# URI = "druby://127.0.0.1:9069"
|
17
|
+
# CACHE = {}
|
18
|
+
#
|
19
|
+
# $SAFE = 1 # disable eval and friends
|
20
|
+
#
|
21
|
+
# DRb.start_service(URI, CACHE)
|
22
|
+
# DRb.thread.join
|
23
|
+
#
|
24
|
+
# Usage for all caches:
|
25
|
+
#
|
26
|
+
# Innate.options.cache.default = Innate::Cache::DRb
|
27
|
+
#
|
28
|
+
# Usage for sessions only:
|
29
|
+
#
|
30
|
+
# Innate.options.cache.session = Innate::Cache::DRb
|
31
|
+
class DRb
|
32
|
+
include Cache::API
|
33
|
+
|
34
|
+
OPTIONS = {:address => '127.0.0.1', :port => 9069}
|
35
|
+
|
36
|
+
def cache_setup(*args)
|
37
|
+
address, port = OPTIONS.values_at(:address, :port)
|
38
|
+
@store = DRbObject.new(nil, "druby://#{address}:#{port}")
|
39
|
+
end
|
40
|
+
|
41
|
+
def cache_clear
|
42
|
+
@store.clear
|
43
|
+
end
|
44
|
+
|
45
|
+
def cache_store(*args)
|
46
|
+
super{|key, value| @store[key] = value }
|
47
|
+
end
|
48
|
+
|
49
|
+
def cache_fetch(*args)
|
50
|
+
super{|key| @store[key] }
|
51
|
+
end
|
52
|
+
|
53
|
+
def cache_delete(*args)
|
54
|
+
super{|key| @store.delete(key) }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Innate
|
2
|
+
class Cache
|
3
|
+
|
4
|
+
# Used by caches that serialize their contents to the filesystem.
|
5
|
+
module FileBased
|
6
|
+
def cache_setup(*args)
|
7
|
+
@prefix = args.compact.join('-')
|
8
|
+
|
9
|
+
@dir = File.join(Dir.tmpdir, self.class::DIR)
|
10
|
+
FileUtils.mkdir_p(@dir)
|
11
|
+
|
12
|
+
@filename = File.join(@dir, @prefix + self.class::EXT)
|
13
|
+
@store = self.class::STORE.new(@filename)
|
14
|
+
end
|
15
|
+
|
16
|
+
def cache_clear
|
17
|
+
FileUtils.mkdir_p(@dir)
|
18
|
+
FileUtils.rm_f(@filename)
|
19
|
+
@store = self.class::STORE.new(@filename)
|
20
|
+
end
|
21
|
+
|
22
|
+
def cache_store(*args)
|
23
|
+
super{|key, value| transaction{|store| store[key] = value } }
|
24
|
+
end
|
25
|
+
|
26
|
+
def cache_fetch(*args)
|
27
|
+
super{|key| transaction{|store| store[key] } }
|
28
|
+
end
|
29
|
+
|
30
|
+
def cache_delete(*args)
|
31
|
+
super{|key| transaction{|store| store.delete(key) } }
|
32
|
+
end
|
33
|
+
|
34
|
+
def transaction(&block)
|
35
|
+
Innate.sync{ @store.transaction(&block) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'pstore'
|
2
|
+
|
3
|
+
module Innate
|
4
|
+
class Cache
|
5
|
+
# Keeps every cache in a separate file like this:
|
6
|
+
#
|
7
|
+
# /tmp/innate-cache-marshal/delta-manveru-session.marshal
|
8
|
+
class Marshal
|
9
|
+
include Cache::API
|
10
|
+
include Cache::FileBased
|
11
|
+
|
12
|
+
STORE = ::PStore
|
13
|
+
DIR = 'innate-cache-marshal'
|
14
|
+
EXT = '.marshal'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Innate
|
2
|
+
class Cache
|
3
|
+
# Memory cache is simply a Hash with the Cache::API, it's the reference
|
4
|
+
# implementation for every other cache.
|
5
|
+
|
6
|
+
class Memory < Hash
|
7
|
+
include Cache::API
|
8
|
+
|
9
|
+
def cache_store(*args)
|
10
|
+
super{|key, value| self[key] = value }
|
11
|
+
end
|
12
|
+
|
13
|
+
def cache_fetch(*args)
|
14
|
+
super{|key| self[key] }
|
15
|
+
end
|
16
|
+
|
17
|
+
def cache_delete(*args)
|
18
|
+
super{|key| delete(key) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|