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,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
|