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,39 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Innate
|
4
|
+
module State
|
5
|
+
# In case fibers are not available we fall back to this wrapper.
|
6
|
+
#
|
7
|
+
# It will raise errors happening inside the wrapping Thread even if
|
8
|
+
# Thread::raise_on_exception is false.
|
9
|
+
#
|
10
|
+
# For things that require a mutex in a threaded environment, use
|
11
|
+
# STATE#sync, if Fiber is available no mutex will be used.
|
12
|
+
|
13
|
+
class Thread
|
14
|
+
SEMAPHORE = Mutex.new
|
15
|
+
|
16
|
+
def [](key)
|
17
|
+
::Thread.current[key]
|
18
|
+
end
|
19
|
+
|
20
|
+
def []=(key, value)
|
21
|
+
::Thread.current[key] = value
|
22
|
+
end
|
23
|
+
|
24
|
+
# Execute given block in a new Thread and rescue any exceptions before
|
25
|
+
# they reach Thread::new, so in case Thread::raise_on_exception is false
|
26
|
+
# we can still reraise the error outside of the Thread.
|
27
|
+
|
28
|
+
def wrap
|
29
|
+
value = ::Thread.new{ begin; yield; rescue Exception => ex; ex; end }.value
|
30
|
+
raise(value) if Exception === value
|
31
|
+
return value
|
32
|
+
end
|
33
|
+
|
34
|
+
def sync(&block)
|
35
|
+
SEMAPHORE.synchronize(&block)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Innate
|
2
|
+
module Traited
|
3
|
+
TRAITS = Hash.new{|h,k| h[k] = {}}
|
4
|
+
|
5
|
+
def self.included(into)
|
6
|
+
into.extend(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
def trait(hash = nil)
|
10
|
+
hash ? TRAITS[self].update(hash) : TRAITS[self]
|
11
|
+
end
|
12
|
+
|
13
|
+
def ancestral_trait
|
14
|
+
ancs = respond_to?(:ancestors) ? ancestors : self.class.ancestors
|
15
|
+
ancs.reverse.inject({}){|s,v|
|
16
|
+
v.respond_to?(:trait) ? s.update(v.trait) : s
|
17
|
+
}.merge(trait)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'innate/state/accessor'
|
2
|
+
require 'innate/request'
|
3
|
+
|
4
|
+
module Innate
|
5
|
+
# The module to be included into the Controller it basically just provides
|
6
|
+
# #request, #response and #session, each accessing Thread.current to
|
7
|
+
# retrieve the demanded object
|
8
|
+
|
9
|
+
module Trinity
|
10
|
+
extend StateAccessor
|
11
|
+
|
12
|
+
state_accessor :request, :response, :session, :actions
|
13
|
+
|
14
|
+
def action
|
15
|
+
actions.last
|
16
|
+
end
|
17
|
+
|
18
|
+
def action=(arg)
|
19
|
+
raise "You have to modify Current::actions or use Current::action.wrap"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/innate/view.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
module Innate
|
2
|
+
|
3
|
+
# This is a container module for wrappers of templating engines and handles
|
4
|
+
# lazy requiring of needed engines.
|
5
|
+
|
6
|
+
module View
|
7
|
+
ENGINE, TEMP = {}, {}
|
8
|
+
|
9
|
+
module_function
|
10
|
+
|
11
|
+
# Try to obtain given engine by its registered name.
|
12
|
+
|
13
|
+
def get(engine_or_ext)
|
14
|
+
return unless engine_or_ext
|
15
|
+
eoe = engine_or_ext.to_s
|
16
|
+
|
17
|
+
if klass = TEMP[eoe]
|
18
|
+
return klass
|
19
|
+
elsif klass = ENGINE[eoe]
|
20
|
+
TEMP[eoe] = obtain(klass)
|
21
|
+
else
|
22
|
+
TEMP[eoe] = const_get(eoe.capitalize)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# We need to put this in a Mutex because simultanous calls for the same
|
27
|
+
# class will cause race conditions and one call may return the wrong class
|
28
|
+
# on the first request (before TEMP is set).
|
29
|
+
# No mutex is used in Fiber environment, see Innate::State and subclasses.
|
30
|
+
|
31
|
+
def obtain(klass)
|
32
|
+
STATE.sync do
|
33
|
+
obj = Object
|
34
|
+
klass.split('::').each{|e| obj = obj.const_get(e) }
|
35
|
+
obj
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Register given templating engine wrapper and extensions for later usage.
|
40
|
+
#
|
41
|
+
# +name+ : the class name of the templating engine wrapper
|
42
|
+
# +exts+ : any number of arguments will be turned into strings via #to_s
|
43
|
+
# that indicate which filename-extensions the templates may have.
|
44
|
+
|
45
|
+
def register(klass, *exts)
|
46
|
+
exts.each do |ext|
|
47
|
+
ext = ext.to_s
|
48
|
+
if k = ENGINE[ext]
|
49
|
+
Log.warn "#{ext} is assigned to #{k} already"
|
50
|
+
else
|
51
|
+
ENGINE[ext] = klass
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Combine Kernel#autoload and Innate::View::register
|
57
|
+
|
58
|
+
def auto_register(name, *exts)
|
59
|
+
autoload(name, "innate/view/#{name}".downcase)
|
60
|
+
register("#{self}::#{name}", *exts)
|
61
|
+
end
|
62
|
+
|
63
|
+
auto_register :None, :css
|
64
|
+
auto_register :None, :html, :htm
|
65
|
+
auto_register :ERB, :erb
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Innate
|
4
|
+
module View
|
5
|
+
module ERB
|
6
|
+
def self.render(action, string = action.view)
|
7
|
+
return unless string.respond_to?(:to_str)
|
8
|
+
|
9
|
+
action.copy_variables
|
10
|
+
|
11
|
+
erb = ::ERB.new(string.to_str, nil, '%<>')
|
12
|
+
erb.filename = (action.view || action.method).to_s
|
13
|
+
erb.result(action.binding)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Rack
|
2
|
+
class MiddlewareCompiler
|
3
|
+
COMPILED = {}
|
4
|
+
|
5
|
+
def self.build(name, &block)
|
6
|
+
COMPILED[name] ||= new(name, &block)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.build!(name, &block)
|
10
|
+
COMPILED[name] = new(name, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :middlewares, :name
|
14
|
+
|
15
|
+
def initialize(name)
|
16
|
+
@name = name
|
17
|
+
@middlewares = []
|
18
|
+
@compiled = nil
|
19
|
+
yield(self) if block_given?
|
20
|
+
end
|
21
|
+
|
22
|
+
# FIXME: Should we use `|` or `+`?
|
23
|
+
def use(*mws)
|
24
|
+
@middlewares = mws | @middlewares
|
25
|
+
end
|
26
|
+
|
27
|
+
def run(app)
|
28
|
+
@app = app
|
29
|
+
end
|
30
|
+
|
31
|
+
def cascade(*apps)
|
32
|
+
@app = Rack::Cascade.new(apps)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Default application for Innate
|
36
|
+
def innate
|
37
|
+
cascade(
|
38
|
+
Rack::File.new('public'),
|
39
|
+
Innate::Current.new(Innate::Route.new, Innate::Rewrite.new))
|
40
|
+
end
|
41
|
+
|
42
|
+
def static(path)
|
43
|
+
require 'rack/contrib'
|
44
|
+
Rack::ConditionalGet.new(Rack::ETag.new(Rack::File.new(path)))
|
45
|
+
end
|
46
|
+
|
47
|
+
def call(env)
|
48
|
+
compile
|
49
|
+
@compiled.call(env)
|
50
|
+
end
|
51
|
+
|
52
|
+
def compiled?
|
53
|
+
@compiled
|
54
|
+
end
|
55
|
+
|
56
|
+
def compile
|
57
|
+
return self if compiled?
|
58
|
+
@compiled = @middlewares.inject(@app){|a,e| e.new(a) }
|
59
|
+
self
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
# Copyright (c) 2008 Michael Fellinger m.fellinger@gmail.com
|
2
|
+
# All files in this distribution are subject to the terms of the Ruby license.
|
3
|
+
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
module Rack
|
7
|
+
|
8
|
+
# High performant source reloader
|
9
|
+
#
|
10
|
+
# This class acts as Rack middleware.
|
11
|
+
#
|
12
|
+
# It does not depend on Ramaze itself, but you might have to adjust the
|
13
|
+
# Reloader::Hooks module or include your own module to override the hooks.
|
14
|
+
# You also might have to set the Log constant.
|
15
|
+
#
|
16
|
+
# What makes it especially suited for use in a production environment is that
|
17
|
+
# any file will only be checked once and there will only be made one system
|
18
|
+
# call stat(2).
|
19
|
+
#
|
20
|
+
# Please note that this will not reload files in the background, it does so
|
21
|
+
# only when actively called
|
22
|
+
# In case of Ramaze it is performing a check/reload cycle at the start of
|
23
|
+
# every request, but also respects a cool down time, during which nothing will
|
24
|
+
# be done.
|
25
|
+
#
|
26
|
+
# After every reload the OPTIONS hash will be checked for changed options and
|
27
|
+
# assigned to the instance, so you may change options during the lifetime of
|
28
|
+
# your application.
|
29
|
+
#
|
30
|
+
# A number of hooks will be executed during the reload cycle, see
|
31
|
+
# Ramaze::ReloaderHooks for more information.
|
32
|
+
|
33
|
+
class Reloader
|
34
|
+
OPTIONS = {
|
35
|
+
# At most check every n seconds
|
36
|
+
# nil/false will never trigger the reload cycle
|
37
|
+
# 0 will cycle on every call
|
38
|
+
:cooldown => 2,
|
39
|
+
|
40
|
+
# Compiled files cannot be reloaded during runtime
|
41
|
+
:ignore => /\.so$/,
|
42
|
+
|
43
|
+
# Run cycle in a Thread.exclusive, by default no threads are used.
|
44
|
+
:thread => false,
|
45
|
+
|
46
|
+
# If you assign a block here it will be instance_evaled instead of
|
47
|
+
# calling cycle. This allows you to use for example EventMachine for
|
48
|
+
# well performing asynchronous cycling.
|
49
|
+
:control => nil, # lambda{ cycle },
|
50
|
+
}
|
51
|
+
|
52
|
+
|
53
|
+
# Dirty! keep track of this in an instance :(
|
54
|
+
# Find out how to reasonably cache racks builder
|
55
|
+
@@last ||= Time.now
|
56
|
+
@@cache ||= {}
|
57
|
+
@@mtimes ||= {}
|
58
|
+
|
59
|
+
def initialize(app)
|
60
|
+
@app = app
|
61
|
+
options_reload
|
62
|
+
end
|
63
|
+
|
64
|
+
def options_reload
|
65
|
+
@cooldown, @ignore, @control, @thread =
|
66
|
+
OPTIONS.values_at(:cooldown, :ignore, :control, :thread)
|
67
|
+
end
|
68
|
+
|
69
|
+
def call(env)
|
70
|
+
options_reload
|
71
|
+
|
72
|
+
if @cooldown and Time.now > @@last + @cooldown
|
73
|
+
if @control
|
74
|
+
instance_eval(&@control)
|
75
|
+
elsif @thread
|
76
|
+
Thread.exclusive{ cycle }
|
77
|
+
else
|
78
|
+
cycle
|
79
|
+
end
|
80
|
+
|
81
|
+
@@last = Time.now
|
82
|
+
end
|
83
|
+
|
84
|
+
@app.call(env)
|
85
|
+
end
|
86
|
+
|
87
|
+
def cycle
|
88
|
+
before_cycle
|
89
|
+
|
90
|
+
rotation do |file, stat|
|
91
|
+
if mtime = stat.mtime
|
92
|
+
if mtime > (@@mtimes[file] ||= mtime)
|
93
|
+
safe_load(file, mtime)
|
94
|
+
end
|
95
|
+
else
|
96
|
+
@@cache.delete(file)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
after_cycle
|
101
|
+
end
|
102
|
+
|
103
|
+
# A safe Kernel::load, issuing the hooks depending on the results
|
104
|
+
def safe_load(file, mtime)
|
105
|
+
before_safe_load(file)
|
106
|
+
load(file)
|
107
|
+
after_safe_load_succeed(file)
|
108
|
+
rescue Object => ex
|
109
|
+
puts ex
|
110
|
+
after_safe_load_failed(file, ex)
|
111
|
+
ensure
|
112
|
+
@@mtimes[file] = mtime
|
113
|
+
end
|
114
|
+
|
115
|
+
def rotation
|
116
|
+
files = [$0, *$LOADED_FEATURES].uniq
|
117
|
+
paths = ['./', *$LOAD_PATH].uniq
|
118
|
+
|
119
|
+
files.each do |file|
|
120
|
+
next if file =~ @ignore
|
121
|
+
path, stat = figure_path(file, paths)
|
122
|
+
|
123
|
+
if path and stat
|
124
|
+
@@cache[file] = path
|
125
|
+
yield(path, stat)
|
126
|
+
else
|
127
|
+
# Mostly harmless
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def figure_path(file, paths)
|
133
|
+
if cached = @@cache[file]
|
134
|
+
stat = ::File.stat(cached)
|
135
|
+
return cached, stat if stat.file?
|
136
|
+
elsif Pathname.new(file).absolute?
|
137
|
+
stat = ::File.stat(file)
|
138
|
+
return file, stat if stat.file? # do directories really end up in $" ?
|
139
|
+
end
|
140
|
+
|
141
|
+
paths.each do |possible_path|
|
142
|
+
path = ::File.join(possible_path, file)
|
143
|
+
|
144
|
+
begin
|
145
|
+
stat = ::File.stat(path)
|
146
|
+
return path, stat if stat.file?
|
147
|
+
rescue Errno::ENOENT, Errno::ENOTDIR
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
return nil
|
152
|
+
rescue Errno::ENOENT => ex
|
153
|
+
@@cache.delete(file)
|
154
|
+
retry
|
155
|
+
end
|
156
|
+
|
157
|
+
# Holds hooks that are called before and after #cycle and #safe_load
|
158
|
+
module Hooks
|
159
|
+
# Overwrite to add actions before the reload rotation is started.
|
160
|
+
def before_cycle
|
161
|
+
end
|
162
|
+
|
163
|
+
# Overwrite to add actions after the reload rotation has ended.
|
164
|
+
def after_cycle
|
165
|
+
end
|
166
|
+
|
167
|
+
# Overwrite to add actions before a file is Kernel::load-ed
|
168
|
+
def before_safe_load(file)
|
169
|
+
puts "reload #{file}" # Log.debug("reload #{file}")
|
170
|
+
end
|
171
|
+
|
172
|
+
# Overwrite to add actions after a file is Kernel::load-ed successfully,
|
173
|
+
# by default we clean the Cache for compiled templates and resolved actions.
|
174
|
+
def after_safe_load_succeed(file)
|
175
|
+
after_safe_load(file)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Overwrite to add custom hook in addition to default Cache cleaning
|
179
|
+
def after_safe_load(file)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Overwrite to add actions after a file is Kernel::load-ed unsuccessfully,
|
183
|
+
# by default we output an error-message with the exception.
|
184
|
+
def after_safe_load_failed(file, error)
|
185
|
+
puts error # Log.error(error)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
include Hooks
|
190
|
+
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec/helper'
|
2
|
+
|
3
|
+
require 'example/hello'
|
4
|
+
|
5
|
+
describe 'example/hello' do
|
6
|
+
behaves_like :mock
|
7
|
+
|
8
|
+
should 'have index action' do
|
9
|
+
got = get('/')
|
10
|
+
got.status.should == 200
|
11
|
+
got['Content-Type'].should == 'text/html'
|
12
|
+
got.body.should == 'Hello, World!'
|
13
|
+
end
|
14
|
+
end
|