manveru-innate 2009.02.25 → 2009.03.24
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +383 -0
- data/MANIFEST +17 -8
- data/README.md +222 -136
- data/Rakefile +7 -2
- data/example/provides.rb +28 -0
- data/innate.gemspec +19 -10
- data/lib/innate/action.rb +21 -47
- data/lib/innate/adapter.rb +65 -56
- data/lib/innate/cache.rb +16 -8
- data/lib/innate/dynamap.rb +39 -29
- data/lib/innate/helper/aspect.rb +60 -0
- data/lib/innate/helper/cgi.rb +2 -0
- data/lib/innate/helper/link.rb +43 -11
- data/lib/innate/helper/partial.rb +6 -5
- data/lib/innate/helper.rb +10 -13
- data/lib/innate/log/hub.rb +1 -0
- data/lib/innate/log.rb +3 -6
- data/lib/{rack → innate}/middleware_compiler.rb +19 -12
- data/lib/innate/mock.rb +3 -2
- data/lib/innate/node.rb +573 -179
- data/lib/innate/options/dsl.rb +46 -6
- data/lib/innate/options/stub.rb +7 -0
- data/lib/innate/options.rb +14 -93
- data/lib/innate/request.rb +21 -7
- data/lib/innate/response.rb +12 -0
- data/lib/innate/route.rb +2 -3
- data/lib/innate/session.rb +37 -20
- data/lib/innate/spec.rb +4 -0
- data/lib/innate/state/fiber.rb +14 -7
- data/lib/innate/state/thread.rb +10 -2
- data/lib/innate/state.rb +8 -11
- data/lib/innate/traited.rb +14 -6
- data/lib/innate/trinity.rb +0 -4
- data/lib/innate/version.rb +1 -1
- data/lib/innate/view/erb.rb +4 -2
- data/lib/innate/view/none.rb +2 -2
- data/lib/innate/view.rb +14 -21
- data/lib/innate.rb +49 -30
- data/spec/helper.rb +8 -0
- data/spec/innate/action/layout.rb +9 -6
- data/spec/innate/cache/common.rb +3 -3
- data/spec/innate/helper/aspect.rb +3 -5
- data/spec/innate/helper/flash.rb +1 -1
- data/spec/innate/helper/link.rb +45 -2
- data/spec/innate/helper/partial.rb +34 -10
- data/spec/innate/helper/view/loop.erb +1 -1
- data/spec/innate/helper/view/recursive.erb +1 -1
- data/spec/innate/node/mapping.rb +37 -0
- data/spec/innate/node/node.rb +142 -0
- data/spec/innate/node/resolve.rb +82 -0
- data/spec/innate/node/{another_layout → view/another_layout}/another_layout.erb +0 -0
- data/spec/innate/node/{bar.html → view/bar.erb} +0 -0
- data/spec/innate/node/{foo.html.erb → view/foo.html.erb} +0 -0
- data/spec/innate/node/{only_view.html → view/only_view.erb} +0 -0
- data/spec/innate/node/view/with_layout.erb +1 -0
- data/spec/innate/node/wrap_action_call.rb +83 -0
- data/spec/innate/options.rb +28 -6
- data/spec/innate/provides/list.html.erb +1 -0
- data/spec/innate/provides/list.txt.erb +1 -0
- data/spec/innate/provides.rb +99 -0
- data/spec/innate/request.rb +23 -10
- data/spec/innate/route.rb +2 -4
- data/spec/innate/session.rb +1 -1
- data/spec/innate/state/fiber.rb +57 -0
- data/spec/innate/state/thread.rb +40 -0
- metadata +20 -11
- data/lib/rack/reloader.rb +0 -192
- data/spec/innate/node/with_layout.erb +0 -3
- data/spec/innate/node.rb +0 -224
data/innate.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "innate"
|
3
|
-
s.version = "2009.
|
3
|
+
s.version = "2009.03.24"
|
4
4
|
|
5
5
|
s.summary = "Powerful web-framework wrapper for Rack."
|
6
6
|
s.description = "Simple, straight-forward, base for web-frameworks."
|
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.homepage = "http://github.com/manveru/innate"
|
12
12
|
s.require_path = "lib"
|
13
13
|
|
14
|
-
s.add_dependency('rack', '>= 0.
|
14
|
+
s.add_dependency('rack', '>= 0.9.1')
|
15
15
|
|
16
16
|
s.files = [
|
17
17
|
"CHANGELOG",
|
@@ -33,6 +33,7 @@ Gem::Specification.new do |s|
|
|
33
33
|
"example/hello.rb",
|
34
34
|
"example/howto_spec.rb",
|
35
35
|
"example/link.rb",
|
36
|
+
"example/provides.rb",
|
36
37
|
"example/session.rb",
|
37
38
|
"innate.gemspec",
|
38
39
|
"lib/innate.rb",
|
@@ -60,10 +61,12 @@ Gem::Specification.new do |s|
|
|
60
61
|
"lib/innate/log.rb",
|
61
62
|
"lib/innate/log/color_formatter.rb",
|
62
63
|
"lib/innate/log/hub.rb",
|
64
|
+
"lib/innate/middleware_compiler.rb",
|
63
65
|
"lib/innate/mock.rb",
|
64
66
|
"lib/innate/node.rb",
|
65
67
|
"lib/innate/options.rb",
|
66
68
|
"lib/innate/options/dsl.rb",
|
69
|
+
"lib/innate/options/stub.rb",
|
67
70
|
"lib/innate/request.rb",
|
68
71
|
"lib/innate/response.rb",
|
69
72
|
"lib/innate/route.rb",
|
@@ -80,8 +83,6 @@ Gem::Specification.new do |s|
|
|
80
83
|
"lib/innate/view.rb",
|
81
84
|
"lib/innate/view/erb.rb",
|
82
85
|
"lib/innate/view/none.rb",
|
83
|
-
"lib/rack/middleware_compiler.rb",
|
84
|
-
"lib/rack/reloader.rb",
|
85
86
|
"spec/example/hello.rb",
|
86
87
|
"spec/example/link.rb",
|
87
88
|
"spec/example/session.rb",
|
@@ -108,17 +109,25 @@ Gem::Specification.new do |s|
|
|
108
109
|
"spec/innate/helper/view/partial.erb",
|
109
110
|
"spec/innate/helper/view/recursive.erb",
|
110
111
|
"spec/innate/mock.rb",
|
111
|
-
"spec/innate/node.rb",
|
112
|
-
"spec/innate/node/
|
113
|
-
"spec/innate/node/
|
114
|
-
"spec/innate/node/
|
115
|
-
"spec/innate/node/
|
116
|
-
"spec/innate/node/
|
112
|
+
"spec/innate/node/mapping.rb",
|
113
|
+
"spec/innate/node/node.rb",
|
114
|
+
"spec/innate/node/resolve.rb",
|
115
|
+
"spec/innate/node/view/another_layout/another_layout.erb",
|
116
|
+
"spec/innate/node/view/bar.erb",
|
117
|
+
"spec/innate/node/view/foo.html.erb",
|
118
|
+
"spec/innate/node/view/only_view.erb",
|
119
|
+
"spec/innate/node/view/with_layout.erb",
|
120
|
+
"spec/innate/node/wrap_action_call.rb",
|
117
121
|
"spec/innate/options.rb",
|
118
122
|
"spec/innate/parameter.rb",
|
123
|
+
"spec/innate/provides.rb",
|
124
|
+
"spec/innate/provides/list.html.erb",
|
125
|
+
"spec/innate/provides/list.txt.erb",
|
119
126
|
"spec/innate/request.rb",
|
120
127
|
"spec/innate/route.rb",
|
121
128
|
"spec/innate/session.rb",
|
129
|
+
"spec/innate/state/fiber.rb",
|
130
|
+
"spec/innate/state/thread.rb",
|
122
131
|
"spec/innate/traited.rb"
|
123
132
|
]
|
124
133
|
end
|
data/lib/innate/action.rb
CHANGED
@@ -1,19 +1,25 @@
|
|
1
1
|
module Innate
|
2
2
|
ACTION_MEMBERS = [ :node, :method, :params, :view, :layout, :instance, :exts,
|
3
|
-
|
3
|
+
:wish, :options, :variables, :value, :view_value, :engine ]
|
4
4
|
|
5
5
|
class Action < Struct.new(*ACTION_MEMBERS)
|
6
|
-
# Holds the default values for merging in {Action::create}
|
7
|
-
DEFAULT = {:options => {}, :variables => {}, :params => []}
|
8
|
-
|
9
6
|
# Create a new Action instance.
|
7
|
+
# Note that the default cannot be a constant as assigning the value objects
|
8
|
+
# to the struct would modify them and might lead to bugs due to persisting
|
9
|
+
# action contents.
|
10
10
|
#
|
11
11
|
# @param [Hash, #to_hash] hash used to seed new Action instance
|
12
12
|
# @return [Action] action with the given defaults from hash
|
13
13
|
# @api stable
|
14
14
|
# @author manveru
|
15
15
|
def self.create(hash = {})
|
16
|
-
|
16
|
+
default = {:options => {}, :variables => {}, :params => []}
|
17
|
+
new(*default.merge(hash.to_hash).values_at(*ACTION_MEMBERS))
|
18
|
+
end
|
19
|
+
|
20
|
+
def merge!(hash)
|
21
|
+
hash.each_pair{|key, value| send("#{key}=", value) }
|
22
|
+
self
|
17
23
|
end
|
18
24
|
|
19
25
|
# Call the Action instance, will insert itself temporarily into
|
@@ -96,51 +102,14 @@ module Innate
|
|
96
102
|
instance.wrap_action_call(self) do
|
97
103
|
self.value = instance.__send__(method, *params) if method
|
98
104
|
self.view_value = File.read(view) if view
|
105
|
+
copy_variables
|
99
106
|
|
100
|
-
|
101
|
-
|
102
|
-
response['Content-Type'] ||= content_type if response
|
103
|
-
|
107
|
+
body, content_type = wrap_in_layout{ engine.call(self, view_value || value) }
|
108
|
+
options[:content_type] ||= content_type if content_type
|
104
109
|
body
|
105
110
|
end
|
106
111
|
end
|
107
112
|
|
108
|
-
# @return [Array] Content-Type and rendered action
|
109
|
-
# @see Action#render Action#wrap_in_layout
|
110
|
-
# @author manveru
|
111
|
-
def as_html
|
112
|
-
return 'text/html', wrap_in_layout{ fulfill_wish(view_value || value) }
|
113
|
-
end
|
114
|
-
|
115
|
-
# @return [Array] Content-Type and rendered action
|
116
|
-
# @see Action#render Action#wrap_in_layout
|
117
|
-
# @author manveru
|
118
|
-
def as_yaml
|
119
|
-
require 'yaml'
|
120
|
-
return 'text/yaml', (value || view_value).to_yaml
|
121
|
-
end
|
122
|
-
|
123
|
-
# @return [Array] Content-Type and rendered action
|
124
|
-
# @see Action#render Action#wrap_in_layout
|
125
|
-
# @author manveru
|
126
|
-
def as_json
|
127
|
-
require 'json'
|
128
|
-
return 'application/json', (value || view_value).to_json
|
129
|
-
end
|
130
|
-
|
131
|
-
# @param [String, #to_str] string to be rendered
|
132
|
-
# @return [String] The rendered result of the templating engine
|
133
|
-
# @raise [RuntimeError] if no suitable templating engine was found
|
134
|
-
# @see Action#as_html
|
135
|
-
# @author manveru
|
136
|
-
def fulfill_wish(string)
|
137
|
-
way = File.basename(view).gsub!(/.*?#{wish}\./, '') if view
|
138
|
-
way ||= node.provide[wish] || node.provide['html']
|
139
|
-
|
140
|
-
return View.render(way, self, string || view) if way
|
141
|
-
raise("No templating engine was found for %p" % way)
|
142
|
-
end
|
143
|
-
|
144
113
|
def wrap_in_layout
|
145
114
|
return yield unless layout
|
146
115
|
|
@@ -148,8 +117,9 @@ module Innate
|
|
148
117
|
action.view, action.method = layout_view_or_method(*layout)
|
149
118
|
action.layout = nil
|
150
119
|
action.sync_variables(self)
|
151
|
-
|
152
|
-
action.
|
120
|
+
body, content_type = yield
|
121
|
+
action.variables[:content] = body
|
122
|
+
return action.call, content_type
|
153
123
|
end
|
154
124
|
|
155
125
|
def layout_view_or_method(name, arg)
|
@@ -161,5 +131,9 @@ module Innate
|
|
161
131
|
def name
|
162
132
|
File.basename((method || view).to_s).split('.').first
|
163
133
|
end
|
134
|
+
|
135
|
+
def valid?
|
136
|
+
method || view
|
137
|
+
end
|
164
138
|
end
|
165
139
|
end
|
data/lib/innate/adapter.rb
CHANGED
@@ -17,73 +17,82 @@ module Innate
|
|
17
17
|
# where Rack doesn't want to take a stand.
|
18
18
|
|
19
19
|
module Adapter
|
20
|
-
|
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 "Using #{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
|
20
|
+
include Optional
|
38
21
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
def start_ebb(app, config)
|
43
|
-
require 'ebb'
|
44
|
-
Rack::Handler.get('ebb').run(app, config)
|
45
|
-
end
|
22
|
+
options.dsl do
|
23
|
+
o "IP address or hostname that we respond to - 0.0.0.0 for all",
|
24
|
+
:host, "0.0.0.0"
|
46
25
|
|
47
|
-
|
26
|
+
o "Port for the server",
|
27
|
+
:port, 7000
|
48
28
|
|
49
|
-
|
50
|
-
handler
|
51
|
-
|
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
|
-
}
|
29
|
+
o "Web server to run on",
|
30
|
+
:handler, :webrick
|
31
|
+
end
|
59
32
|
|
60
|
-
|
61
|
-
|
33
|
+
# Pass given app to the Handler, handler is chosen based on config.adapter
|
34
|
+
# option.
|
35
|
+
# If there is a method named start_name_of_adapter it will be run instead
|
36
|
+
# of the default run method of the handler, this makes it easy to define
|
37
|
+
# custom startup of handlers for your server of choice.
|
38
|
+
def self.start(app, given_options = nil)
|
39
|
+
options.merge!(given_options) if given_options
|
62
40
|
|
63
|
-
|
41
|
+
handler = options[:handler].to_s.downcase
|
42
|
+
config = { :Host => options[:host], :Port => options[:port] }
|
64
43
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
44
|
+
Log.debug "Using #{handler}"
|
45
|
+
|
46
|
+
if respond_to?(method = "start_#{handler}")
|
47
|
+
send(method, app, config)
|
48
|
+
else
|
49
|
+
Rack::Handler.get(handler).run(app, config)
|
70
50
|
end
|
51
|
+
end
|
71
52
|
|
72
|
-
|
53
|
+
# Due to buggy autoload on Ruby 1.8 we have to require 'ebb' manually.
|
54
|
+
# This most likely happens because autoload doesn't respect the require of
|
55
|
+
# rubygems and uses the C require directly.
|
56
|
+
def self.start_ebb(app, config)
|
57
|
+
require 'ebb'
|
58
|
+
Rack::Handler.get('ebb').run(app, config)
|
59
|
+
end
|
73
60
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
61
|
+
# We want webrick to use our logger.
|
62
|
+
def self.start_webrick(app, config)
|
63
|
+
handler = Rack::Handler.get('webrick')
|
64
|
+
config = {
|
65
|
+
:BindAddress => config[:Host],
|
66
|
+
:Port => config[:Port],
|
67
|
+
:Logger => Log,
|
68
|
+
:AccessLog => [
|
69
|
+
[Log, ::WEBrick::AccessLog::COMMON_LOG_FORMAT],
|
70
|
+
[Log, ::WEBrick::AccessLog::REFERER_LOG_FORMAT]]
|
71
|
+
}
|
72
|
+
|
73
|
+
handler.run(app, config)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Thin shouldn't give excessive output, especially not to $stdout
|
77
|
+
def self.start_thin(app, config)
|
78
|
+
require 'thin'
|
79
|
+
handler = Rack::Handler.get('thin')
|
80
|
+
::Thin::Logging.silent = true
|
81
|
+
handler.run(app, config)
|
82
|
+
end
|
79
83
|
|
80
|
-
|
84
|
+
# swiftcore has its own handler outside of rack
|
85
|
+
def self.start_emongrel(app, config)
|
86
|
+
require 'swiftcore/evented_mongrel'
|
87
|
+
handler = Rack::Handler.get('emongrel')
|
88
|
+
handler.run(app, config)
|
89
|
+
end
|
81
90
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
91
|
+
# swiftcore has its own handler outside of rack
|
92
|
+
def self.start_smongrel(app, config)
|
93
|
+
require 'swiftcore/swiftiplied_mongrel'
|
94
|
+
handler = Rack::Handler.get('smongrel')
|
95
|
+
handler.run(app, config)
|
87
96
|
end
|
88
97
|
end
|
89
98
|
end
|
data/lib/innate/cache.rb
CHANGED
@@ -14,7 +14,7 @@ module Innate
|
|
14
14
|
#
|
15
15
|
# Configuration:
|
16
16
|
#
|
17
|
-
# Innate.options
|
17
|
+
# Innate::Cache.options do |cache|
|
18
18
|
# cache.names = [:session, :user]
|
19
19
|
# cache.session = Innate::Cache::Marshal
|
20
20
|
# cache.user = Innate::Cache::YAML
|
@@ -61,20 +61,28 @@ module Innate
|
|
61
61
|
autoload :Marshal, 'innate/cache/marshal'
|
62
62
|
autoload :FileBased, 'innate/cache/file_based'
|
63
63
|
|
64
|
+
include Optional
|
65
|
+
|
66
|
+
options.dsl do
|
67
|
+
o "Assign a cache to each of these names on Innate::Cache::setup",
|
68
|
+
:names, [:session]
|
69
|
+
|
70
|
+
default "If no option for the cache name exists, fall back to this",
|
71
|
+
Innate::Cache::Memory
|
72
|
+
end
|
73
|
+
|
64
74
|
attr_reader :name, :instance
|
65
75
|
|
66
76
|
def initialize(name, klass = nil)
|
67
77
|
@name = name.to_s.dup.freeze
|
68
78
|
|
69
|
-
|
70
|
-
|
71
|
-
klass ||= options[:cache, @name.to_sym]
|
79
|
+
klass ||= options[@name.to_sym]
|
72
80
|
@instance = klass.new
|
73
81
|
|
74
82
|
@instance.cache_setup(
|
75
|
-
|
76
|
-
|
77
|
-
|
83
|
+
ENV['HOSTNAME'],
|
84
|
+
ENV['USER'],
|
85
|
+
'pristine',
|
78
86
|
@name
|
79
87
|
)
|
80
88
|
end
|
@@ -86,7 +94,7 @@ module Innate
|
|
86
94
|
# @return [Array] names of caches initialized
|
87
95
|
# @author manveru
|
88
96
|
def self.setup
|
89
|
-
|
97
|
+
options.names.each{|name| add(name) }
|
90
98
|
end
|
91
99
|
|
92
100
|
# Add accessors for cache
|
data/lib/innate/dynamap.rb
CHANGED
@@ -1,38 +1,48 @@
|
|
1
1
|
module Innate
|
2
|
+
class URLMap < Rack::URLMap
|
3
|
+
def initialize(map = {})
|
4
|
+
@originals = map
|
5
|
+
super
|
6
|
+
end
|
2
7
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
8
|
+
# super may raise when given invalid locations, so we only replace the
|
9
|
+
# `@originals` if we are sure the new map is valid
|
10
|
+
def remap(map)
|
11
|
+
value = super
|
12
|
+
@originals = map
|
13
|
+
value
|
14
|
+
end
|
9
15
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
# SCRIPT_NAME, which leads to incorrect routing as parts of the PATH_INFO
|
14
|
-
# are cut out if they matched once. Here I repair this damage and hope
|
15
|
-
# that my patch to rack will be accepted.
|
16
|
-
# Update: patch was accepted, will remove it on next rack release
|
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
|
16
|
+
def map(location, object)
|
17
|
+
return unless location and object
|
18
|
+
remap(@originals.merge(location.to_s => object))
|
26
19
|
end
|
27
20
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
21
|
+
def at(location)
|
22
|
+
@originals[location]
|
23
|
+
end
|
24
|
+
|
25
|
+
def to(object)
|
26
|
+
@originals.invert[object]
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_hash
|
30
|
+
@originals.dup
|
31
|
+
end
|
32
|
+
|
33
|
+
def call(env)
|
34
|
+
raise "Nothing mapped yet" if @originals.empty?
|
35
|
+
super
|
33
36
|
end
|
34
37
|
end
|
35
38
|
|
39
|
+
DynaMap = URLMap.new
|
40
|
+
|
41
|
+
# script_name, path_info = env['SCRIPT_NAME'], env['PATH_INFO']
|
42
|
+
# answer = app.call(env)
|
43
|
+
# env.merge!('SCRIPT_NAME' => script_name, 'PATH_INFO' => path_info)
|
44
|
+
# answer
|
45
|
+
|
36
46
|
module SingletonMethods
|
37
47
|
# Maps the given +object+ or +block+ to +location+, +object+ must respond to
|
38
48
|
# #call in order to be of any use.
|
@@ -61,7 +71,7 @@ module Innate
|
|
61
71
|
#
|
62
72
|
# Innate.at('/') # => Hello
|
63
73
|
def at(location)
|
64
|
-
DynaMap
|
74
|
+
DynaMap.at(location)
|
65
75
|
end
|
66
76
|
|
67
77
|
# Returns one of the paths the given +object+ is mapped to.
|
@@ -75,7 +85,7 @@ module Innate
|
|
75
85
|
#
|
76
86
|
# Innate.to(Hello) # => '/'
|
77
87
|
def to(object)
|
78
|
-
DynaMap
|
88
|
+
DynaMap.to(object)
|
79
89
|
end
|
80
90
|
end
|
81
91
|
end
|
data/lib/innate/helper/aspect.rb
CHANGED
@@ -2,11 +2,14 @@ module Innate
|
|
2
2
|
module Helper
|
3
3
|
|
4
4
|
# Provides before/after wrappers for actions
|
5
|
+
#
|
6
|
+
# This helper is essential for proper working of {Action#render}.
|
5
7
|
module Aspect
|
6
8
|
AOP = Hash.new{|h,k| h[k] = Hash.new{|hh,kk| hh[kk] = {} }}
|
7
9
|
|
8
10
|
def self.included(into)
|
9
11
|
into.extend(SingletonMethods)
|
12
|
+
into.add_action_wrapper(5.0, :aspect_wrap)
|
10
13
|
end
|
11
14
|
|
12
15
|
# Consider objects that have Aspect included
|
@@ -37,7 +40,56 @@ module Innate
|
|
37
40
|
result
|
38
41
|
end
|
39
42
|
|
43
|
+
# This awesome piece of hackery implements action AOP.
|
44
|
+
#
|
45
|
+
# The so-called aspects are simply methods that may yield the next aspect
|
46
|
+
# in the chain, this is similar to racks concept of middleware, but instead
|
47
|
+
# of initializing with an app we simply pass a block that may be yielded
|
48
|
+
# with the action being processed.
|
49
|
+
#
|
50
|
+
# This gives us things like logging, caching, aspects, authentication, etc.
|
51
|
+
#
|
52
|
+
# Add the name of your method to the trait[:wrap] to add your own method to
|
53
|
+
# the wrap_action_call chain.
|
54
|
+
#
|
55
|
+
# @example adding your method
|
56
|
+
#
|
57
|
+
# class MyNode
|
58
|
+
# Innate.node '/'
|
59
|
+
#
|
60
|
+
# private
|
61
|
+
#
|
62
|
+
# def wrap_logging(action)
|
63
|
+
# Innate::Log.info("Executing #{action.name}")
|
64
|
+
# yield
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# trait[:wrap]
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
#
|
71
|
+
# methods may register
|
72
|
+
# themself in the trait[:wrap] and will be called in left-to-right order,
|
73
|
+
# each being passed the action instance and a block that they have to yield
|
74
|
+
# to continue the chain.
|
75
|
+
#
|
76
|
+
# @param [Action] action instance that is being passed to every registered method
|
77
|
+
# @param [Proc] block contains the instructions to call the action method if any
|
78
|
+
#
|
79
|
+
# @see Action#render
|
80
|
+
# @author manveru
|
81
|
+
def wrap_action_call(action, &block)
|
82
|
+
wrap = SortedSet.new
|
83
|
+
action.node.ancestral_trait_values(:wrap).each{|sset| wrap.merge(sset) }
|
84
|
+
head, *tail = wrap.map{|k,v| v }
|
85
|
+
tail.reverse!
|
86
|
+
combined = tail.inject(block){|s,v| lambda{ __send__(v, action, &s) } }
|
87
|
+
__send__(head, action, &combined)
|
88
|
+
end
|
89
|
+
|
40
90
|
module SingletonMethods
|
91
|
+
include Traited
|
92
|
+
|
41
93
|
def before_all(&block)
|
42
94
|
AOP[self][:before_all] = block
|
43
95
|
end
|
@@ -58,6 +110,14 @@ module Innate
|
|
58
110
|
before(name, &block)
|
59
111
|
after(name, &block)
|
60
112
|
end
|
113
|
+
|
114
|
+
def add_action_wrapper(order, method_name)
|
115
|
+
if wrap = trait[:wrap]
|
116
|
+
wrap.merge(SortedSet[[order, method_name]])
|
117
|
+
else
|
118
|
+
trait :wrap => SortedSet[[order, method_name]]
|
119
|
+
end
|
120
|
+
end
|
61
121
|
end
|
62
122
|
end
|
63
123
|
end
|
data/lib/innate/helper/cgi.rb
CHANGED
data/lib/innate/helper/link.rb
CHANGED
@@ -28,31 +28,63 @@ module Innate
|
|
28
28
|
hashes, names = args.partition{|arg| arg.respond_to?(:merge!) }
|
29
29
|
hashes.each{|to_merge| hash.merge!(to_merge) }
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
front = Array[prefix, location, name, *names].join('/').squeeze('/')
|
31
|
+
location = route_location(self)
|
32
|
+
front = Array[location, name, *names].join('/').squeeze('/')
|
34
33
|
|
35
|
-
if hash.empty?
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
URI("#{front}?#{query}")
|
41
|
-
end
|
34
|
+
return URI(front) if hash.empty?
|
35
|
+
|
36
|
+
escape = Rack::Utils.method(:escape)
|
37
|
+
query = hash.map{|k, v| "#{escape[k]}=#{escape[v]}" }.join(';')
|
38
|
+
URI("#{front}?#{query}")
|
42
39
|
end
|
43
40
|
alias r route
|
44
41
|
|
42
|
+
def route_location(klass)
|
43
|
+
prefix = Innate.options.prefix
|
44
|
+
location = Innate.to(klass) || Innate.to(klass.class)
|
45
|
+
[prefix, location].join('/')
|
46
|
+
end
|
47
|
+
|
48
|
+
# Create a route to the currently active Node.
|
49
|
+
#
|
50
|
+
# This method is mostly here in case you include this helper elsewhere
|
51
|
+
# and don't want (or can't) type SomeNode.r all the time.
|
52
|
+
#
|
53
|
+
# The usage is identical with {route}.
|
54
|
+
#
|
55
|
+
# @param [#to_s] name
|
56
|
+
# @return [URI] to the location
|
57
|
+
# @see Ramaze::Helper::Link#route
|
58
|
+
# @author manveru
|
59
|
+
def route_self(name = '/', *args)
|
60
|
+
Current.action.node.route(name, *args)
|
61
|
+
end
|
62
|
+
alias rs route_self
|
63
|
+
|
45
64
|
# Create a link tag
|
46
65
|
#
|
47
66
|
# Usage, given Wiki is mapped to `/wiki`:
|
67
|
+
#
|
48
68
|
# Wiki.a(:home) # => '<a href="/wiki/home">home</a>'
|
49
69
|
# Wiki.a('home', :home) # => '<a href="/wiki/home">home</a>'
|
50
70
|
# Wiki.a('home', :/) # => '<a href="/wiki/">home</a>'
|
51
71
|
# Wiki.a('foo', :/, :foo => :bar) # => '<a href="/wiki/?foo=bar">foo</a>'
|
72
|
+
# Wiki.a('example', 'http://example.com')
|
73
|
+
# # => '<a href="http://example.com">example</a>'
|
52
74
|
#
|
53
75
|
# @return [String]
|
54
76
|
def anchor(text, *args)
|
55
|
-
|
77
|
+
case first = (args.first || text)
|
78
|
+
when URI
|
79
|
+
href = first.to_s
|
80
|
+
when /^\w+:\/\//
|
81
|
+
uri = URI(first)
|
82
|
+
uri.query = Rack::Utils.escape_html(uri.query)
|
83
|
+
href = uri.to_s
|
84
|
+
else
|
85
|
+
href = args.empty? ? r(text) : r(*args)
|
86
|
+
end
|
87
|
+
|
56
88
|
text = Rack::Utils.escape_html(text)
|
57
89
|
%(<a href="#{href}">#{text}</a>)
|
58
90
|
end
|
@@ -72,16 +72,17 @@ module Innate
|
|
72
72
|
|
73
73
|
ext = File.extname(path)
|
74
74
|
basename = File.basename(path, ext)
|
75
|
-
|
76
|
-
|
75
|
+
|
77
76
|
action = Innate::Current.action.dup
|
78
77
|
action.layout = nil
|
79
|
-
action.view = action.node.find_view(basename,
|
78
|
+
action.view = action.node.find_view(basename, 'html')
|
80
79
|
action.method = action.node.find_method(basename, action.params)
|
80
|
+
|
81
81
|
action.variables = action.variables.merge(variables)
|
82
82
|
action.sync_variables(action)
|
83
|
-
|
84
|
-
action.call
|
83
|
+
|
84
|
+
return action.call if action.valid?
|
85
|
+
raise(ArgumentError, "cannot render %p" % path)
|
85
86
|
end
|
86
87
|
|
87
88
|
def render_action(method, *params)
|