manveru-innate 2009.02.06
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.
- 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
|