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,17 @@
|
|
1
|
+
require 'yaml/store'
|
2
|
+
|
3
|
+
module Innate
|
4
|
+
class Cache
|
5
|
+
# Keeps every cache in a separate file like this:
|
6
|
+
#
|
7
|
+
# /tmp/innate-cache-yaml/delta-manveru-session.yaml
|
8
|
+
class YAML
|
9
|
+
include Cache::API
|
10
|
+
include Cache::FileBased
|
11
|
+
|
12
|
+
STORE = ::YAML::Store
|
13
|
+
DIR = 'innate-cache-yaml'
|
14
|
+
EXT = '.yaml'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class BasicObject
|
2
|
+
# Remove all but these methods
|
3
|
+
# NOTE: __id__ is not there in 1.9, but would give a warning in 1.8
|
4
|
+
KEEP = %w[== equal? ! != instance_eval instance_exec __send__ __id__]
|
5
|
+
|
6
|
+
(instance_methods - KEEP).each do |im|
|
7
|
+
undef_method(im)
|
8
|
+
end
|
9
|
+
end unless defined?(BasicObject)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'innate/request'
|
2
|
+
require 'innate/response'
|
3
|
+
|
4
|
+
module Innate
|
5
|
+
# Uses STATE to scope request/response/session per Fiber/Thread so we can
|
6
|
+
# reach them from anywhere in the code without passing around the objects
|
7
|
+
# directly.
|
8
|
+
class Current
|
9
|
+
extend Trinity
|
10
|
+
|
11
|
+
def initialize(app, *rest)
|
12
|
+
if rest.empty?
|
13
|
+
@app = app
|
14
|
+
else
|
15
|
+
@app = Rack::Cascade.new([app, *rest])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Wrap into STATE, run setup and call the app inside STATE.
|
20
|
+
|
21
|
+
def call(env)
|
22
|
+
STATE.wrap do
|
23
|
+
setup(env)
|
24
|
+
@app.call(env)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Setup new Request/Response/Session for this request/response cycle
|
29
|
+
|
30
|
+
def setup(env)
|
31
|
+
req = STATE[:request] = Request.new(env)
|
32
|
+
res = STATE[:response] = Response.new
|
33
|
+
STATE[:actions] = []
|
34
|
+
STATE[:session] = Session.new(req, res)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Innate
|
2
|
+
|
3
|
+
# This is a dynamic routing mapper used to outsmart Rack::URLMap
|
4
|
+
# Every time a mapping is changed a new Rack::URLMap will be put into
|
5
|
+
# Innate::DynaMap::CACHE[:map]
|
6
|
+
class DynaMap
|
7
|
+
MAP = {}
|
8
|
+
CACHE = {}
|
9
|
+
|
10
|
+
# Delegate the call to the current Rack::URLMap instance.
|
11
|
+
#
|
12
|
+
# NOTE: Currently Rack::URLMap will destructively modify PATH_INFO and
|
13
|
+
# SCRIPT_NAME, which leads to incorrect routing as parts of the
|
14
|
+
# PATH_INFO are cut out if they matched once.
|
15
|
+
# Here I repair this damage and hope that my patch to rack will be
|
16
|
+
# accepted.
|
17
|
+
def self.call(env)
|
18
|
+
if app = CACHE[:map]
|
19
|
+
script_name, path_info = env['SCRIPT_NAME'], env['PATH_INFO']
|
20
|
+
answer = app.call(env)
|
21
|
+
env.merge!('SCRIPT_NAME' => script_name, 'PATH_INFO' => path_info)
|
22
|
+
answer
|
23
|
+
else
|
24
|
+
raise "Nothing mapped yet"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Map node to location, create a new Rack::URLMap instance and cache it.
|
29
|
+
def self.map(location, node)
|
30
|
+
return unless location
|
31
|
+
MAP[location.to_s] = node
|
32
|
+
CACHE[:map] = Rack::URLMap.new(MAP)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module_function
|
37
|
+
|
38
|
+
# Maps the given +object+ or +block+ to +location+, +object+ must respond to
|
39
|
+
# #call in order to be of any use.
|
40
|
+
#
|
41
|
+
# Usage with passed +object+:
|
42
|
+
#
|
43
|
+
# Innate.map('/', lambda{|env| [200, {}, "Hello, World"] })
|
44
|
+
# Innate.at('/').call({}) # => [200, {}, "Hello, World"]
|
45
|
+
#
|
46
|
+
# Usage with passed +block+:
|
47
|
+
#
|
48
|
+
# Innate.map('/'){|env| [200, {}, ['Hello, World!']] }
|
49
|
+
# Innate.at('/').call({})
|
50
|
+
def map(location, object = nil, &block)
|
51
|
+
DynaMap.map(location, object || block)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Answer with object at +location+.
|
55
|
+
#
|
56
|
+
# Usage:
|
57
|
+
#
|
58
|
+
# class Hello
|
59
|
+
# include Innate::Node
|
60
|
+
# map '/'
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# Innate.at('/') # => Hello
|
64
|
+
def at(location)
|
65
|
+
DynaMap::MAP[location.to_s]
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns one of the paths the given +object+ is mapped to.
|
69
|
+
#
|
70
|
+
# Usage:
|
71
|
+
#
|
72
|
+
# class Hello
|
73
|
+
# include Innate::Node
|
74
|
+
# map '/'
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# Innate.to(Hello) # => '/'
|
78
|
+
def to(object)
|
79
|
+
DynaMap::MAP.invert[object]
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
module Innate
|
2
|
+
|
3
|
+
# Acts as name-space for helpers
|
4
|
+
module Helper
|
5
|
+
# Public instance methods of helpers in here will be recognized as actions
|
6
|
+
LOOKUP = EXPOSE = Set.new
|
7
|
+
|
8
|
+
# Usually called from Innate::Node::included
|
9
|
+
# We also include Innate::Trinity here, as it may be needed in models when
|
10
|
+
# you use helper methods there.
|
11
|
+
def self.included(into)
|
12
|
+
into.extend(HelperAccess)
|
13
|
+
into.__send__(:include, Trinity)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Provides access to #helper method without polluting the name-space any
|
18
|
+
# further.
|
19
|
+
module HelperAccess
|
20
|
+
|
21
|
+
# Convenience method used by Innate::Node.
|
22
|
+
#
|
23
|
+
# Usage:
|
24
|
+
#
|
25
|
+
# class Hi
|
26
|
+
# extend Innate::HelperAccess
|
27
|
+
# helper :cgi, :link, :aspect
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# This will require the helpers and call:
|
31
|
+
#
|
32
|
+
# Hi.include(Innate::Helper::CGI)
|
33
|
+
# Hi.include(Innate::Helper::Link)
|
34
|
+
# Hi.include(Innate::Helper::Aspect)
|
35
|
+
def helper(*helpers)
|
36
|
+
HelpersHelper.each_include(self, *helpers)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Here come the utility methods used from the HelperAccess#helper method, we
|
41
|
+
# do this to keep method count at a minimum and because HelpersHelper is such
|
42
|
+
# an awesome name that just can't be wasted.
|
43
|
+
#
|
44
|
+
# Usage if you want to only extend with helpers:
|
45
|
+
#
|
46
|
+
# class Hi
|
47
|
+
# Innate::HelpersHelper.each_extend(self, :cgi, :link, :aspect)
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# Usage if you only want to include helpers:
|
51
|
+
#
|
52
|
+
# class Hi
|
53
|
+
# Innate::HelpersHelper.each_include(self, :cgi, :link, :aspect)
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# Usage for iteration:
|
57
|
+
#
|
58
|
+
# Innate::HelpersHelper.each(:cgi, :link, :aspect) do |mod|
|
59
|
+
# p mod
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# Usage for translating helpers to modules:
|
63
|
+
#
|
64
|
+
# p Innate::HelpersHelper.each(:cgi, :link, :aspect)
|
65
|
+
module HelpersHelper
|
66
|
+
EXTS = %w[rb so bundle]
|
67
|
+
|
68
|
+
# By default, lib/innate/ is added to the PATH, you may add your
|
69
|
+
# application root here so innate will look in your own helper/ directory.
|
70
|
+
PATH = []
|
71
|
+
|
72
|
+
# The namespaces that may container helper modules.
|
73
|
+
# Lookup is done from left to right.
|
74
|
+
POOL = []
|
75
|
+
|
76
|
+
# all of the following are singleton methods
|
77
|
+
|
78
|
+
module_function
|
79
|
+
|
80
|
+
def add_pool(pool)
|
81
|
+
POOL.unshift(pool)
|
82
|
+
POOL.uniq!
|
83
|
+
end
|
84
|
+
|
85
|
+
add_pool(Helper)
|
86
|
+
|
87
|
+
def add_path(path)
|
88
|
+
PATH.unshift(File.expand_path(path))
|
89
|
+
PATH.uniq!
|
90
|
+
end
|
91
|
+
|
92
|
+
add_path(File.dirname(__FILE__))
|
93
|
+
add_path('')
|
94
|
+
|
95
|
+
# Yield all the modules we can find for the given names of helpers, try to
|
96
|
+
# require them if not available.
|
97
|
+
#
|
98
|
+
# NOTE: Unlike usual #each, this will actually return an Array of the found
|
99
|
+
# modules instead of the given +*names+
|
100
|
+
#
|
101
|
+
#
|
102
|
+
# Usage:
|
103
|
+
#
|
104
|
+
# Innate::HelpersHelper.each(:cgi, :link, :aspect) do |mod|
|
105
|
+
# p mod
|
106
|
+
# end
|
107
|
+
|
108
|
+
def each(*names)
|
109
|
+
names.map do |name|
|
110
|
+
if name.class == Module
|
111
|
+
yield(name) if block_given?
|
112
|
+
name
|
113
|
+
elsif mod = get(name)
|
114
|
+
yield(mod) if block_given?
|
115
|
+
mod
|
116
|
+
elsif try_require(name)
|
117
|
+
redo
|
118
|
+
else
|
119
|
+
raise LoadError, "Helper #{name} not found"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Shortcut to extend +into+ with Helper modules corresponding to +*names+.
|
125
|
+
# +into+ has to respond to #extend.
|
126
|
+
#
|
127
|
+
# Usage:
|
128
|
+
#
|
129
|
+
# class Hi
|
130
|
+
# Innate::HelpersHelper.each_extend(self, :cgi, :link, :aspect)
|
131
|
+
# end
|
132
|
+
def each_extend(into, *names, &block)
|
133
|
+
return if names.empty?
|
134
|
+
into.extend(*each(*names, &block))
|
135
|
+
end
|
136
|
+
|
137
|
+
# Shortcut to include Helper modules corresponding to +*names+ on +into+.
|
138
|
+
# +into+ has to respond to #include.
|
139
|
+
# #__send__(:include) is used in case #include raises due to being
|
140
|
+
# private/protected
|
141
|
+
#
|
142
|
+
# in case #include is a private/protected method.
|
143
|
+
#
|
144
|
+
# Usage:
|
145
|
+
#
|
146
|
+
# class Hi
|
147
|
+
# Innate::HelpersHelper.each_include(self, :cgi, :link, :aspect)
|
148
|
+
# end
|
149
|
+
def each_include(into, *names, &block)
|
150
|
+
return if names.empty?
|
151
|
+
mods = each(*names, &block)
|
152
|
+
into.include(*mods)
|
153
|
+
rescue NoMethodError
|
154
|
+
into.__send__(:include, *mods)
|
155
|
+
end
|
156
|
+
|
157
|
+
# Based on a simple set of rules we will first construct the most likely
|
158
|
+
# name for the helper and then grep the constants in the Innate::Helper
|
159
|
+
# module for any matches.
|
160
|
+
#
|
161
|
+
# helper :foo_bar # => FooBar
|
162
|
+
# helper :foo # => Foo
|
163
|
+
def get(name)
|
164
|
+
name = name.to_s.split('_').map{|e| e.capitalize}.join
|
165
|
+
POOL.each do |namespace|
|
166
|
+
if found = namespace.constants.grep(/^#{name}$/i).first
|
167
|
+
return namespace.const_get(found)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
nil
|
172
|
+
end
|
173
|
+
|
174
|
+
# Figure out files that might have the helper we ask for and then require
|
175
|
+
# the first we find, if any.
|
176
|
+
def try_require(name)
|
177
|
+
if found = Dir[glob(name)].first
|
178
|
+
require File.expand_path(found)
|
179
|
+
else
|
180
|
+
raise LoadError, "Helper #{name} not found"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# Return a nice list of filenames in correct locations with correct
|
185
|
+
# filename-extensions.
|
186
|
+
def glob(name = '*')
|
187
|
+
"{#{paths.join(',')}}/helper/#{name}.{#{EXTS.join(',')}}"
|
188
|
+
end
|
189
|
+
|
190
|
+
# In case you want to do something better.
|
191
|
+
def paths
|
192
|
+
PATH
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Innate
|
2
|
+
module Helper
|
3
|
+
|
4
|
+
# Provides before/after wrappers for actions
|
5
|
+
module Aspect
|
6
|
+
AOP = Hash.new{|h,k| h[k] = Hash.new{|hh,kk| hh[kk] = {} }}
|
7
|
+
|
8
|
+
def self.included(into)
|
9
|
+
into.extend(SingletonMethods)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Consider objects that have Aspect included
|
13
|
+
def self.ancestral_aop(from)
|
14
|
+
aop = {}
|
15
|
+
from.ancestors.reverse.map{|anc| aop.merge!(AOP[anc]) if anc < Aspect }
|
16
|
+
aop
|
17
|
+
end
|
18
|
+
|
19
|
+
def aspect_call(position, name)
|
20
|
+
return unless aop = Aspect.ancestral_aop(self.class)
|
21
|
+
return unless block = at_position = aop[position]
|
22
|
+
|
23
|
+
block = at_position[name.to_sym] unless at_position.is_a?(Proc)
|
24
|
+
|
25
|
+
instance_eval(&block) if block
|
26
|
+
end
|
27
|
+
|
28
|
+
def aspect_wrap(action)
|
29
|
+
return yield unless method = action.name
|
30
|
+
|
31
|
+
aspect_call(:before_all, method)
|
32
|
+
aspect_call(:before, method)
|
33
|
+
yield
|
34
|
+
aspect_call(:after, method)
|
35
|
+
aspect_call(:after_all, method)
|
36
|
+
end
|
37
|
+
|
38
|
+
module SingletonMethods
|
39
|
+
def before_all(&block)
|
40
|
+
AOP[self][:before_all] = block
|
41
|
+
end
|
42
|
+
|
43
|
+
def before(name, &block)
|
44
|
+
AOP[self][:before][name] = block
|
45
|
+
end
|
46
|
+
|
47
|
+
def after_all(&block)
|
48
|
+
AOP[self][:after_all] = block
|
49
|
+
end
|
50
|
+
|
51
|
+
def after(name, &block)
|
52
|
+
AOP[self][:after][name] = block
|
53
|
+
end
|
54
|
+
|
55
|
+
def wrap(name, &block)
|
56
|
+
before(name, &block)
|
57
|
+
after(name, &block)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
autoload(:CGI, 'cgi') # in case you want to use html_unescape
|
2
|
+
|
3
|
+
module Innate
|
4
|
+
|
5
|
+
# Shortcuts to some CGI methods
|
6
|
+
|
7
|
+
module Helper
|
8
|
+
module CGI
|
9
|
+
# shortcut for Rack::Utils.escape
|
10
|
+
def url_encode(*args)
|
11
|
+
Rack::Utils.escape(*args.map{|a| a.to_s })
|
12
|
+
end
|
13
|
+
|
14
|
+
# shortcut for Rack::Utils.unescape
|
15
|
+
def url_decode(*args)
|
16
|
+
Rack::Utils.unescape(*args.map{|a| a.to_s })
|
17
|
+
end
|
18
|
+
|
19
|
+
# shortcut for Rack::Utils.escape_html
|
20
|
+
def html_escape(string)
|
21
|
+
Rack::Utils.escape_html(string)
|
22
|
+
end
|
23
|
+
|
24
|
+
# shortcut for CGI.unescapeHTML
|
25
|
+
def html_unescape(string)
|
26
|
+
::CGI.unescapeHTML(string.to_s)
|
27
|
+
end
|
28
|
+
|
29
|
+
# safely escape all HTML and code
|
30
|
+
def h(string)
|
31
|
+
Rack::Utils.escape_html(string).gsub(/#([{@$]@?)/, '#\1')
|
32
|
+
end
|
33
|
+
|
34
|
+
# one-letter versions help in case like #{h foo.inspect}
|
35
|
+
# ERb/ERuby/Rails compatible
|
36
|
+
alias u url_encode
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|