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
data/example/link.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'innate'
|
2
|
+
|
3
|
+
class Linking
|
4
|
+
include Innate::Node
|
5
|
+
map '/'
|
6
|
+
|
7
|
+
def index
|
8
|
+
"simple link<br />" +
|
9
|
+
a('Help?', :help)
|
10
|
+
end
|
11
|
+
|
12
|
+
def new
|
13
|
+
"Something new!"
|
14
|
+
end
|
15
|
+
|
16
|
+
def help
|
17
|
+
"You have help<br />" +
|
18
|
+
Different.a('A Different Node', :another)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Different
|
23
|
+
include Innate::Node
|
24
|
+
map '/link_to'
|
25
|
+
|
26
|
+
def another
|
27
|
+
a('Even deeper', 'and/deeper')
|
28
|
+
end
|
29
|
+
|
30
|
+
def and__deeper
|
31
|
+
Linking.a('Back to Linking Node', :index)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Innate.start
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'innate'
|
2
|
+
|
3
|
+
# This demonstrates how to obtain different content types from the return value
|
4
|
+
# of action methods.
|
5
|
+
#
|
6
|
+
# Try following requests:
|
7
|
+
# /set/foo/bar
|
8
|
+
# /set/duh/duf
|
9
|
+
#
|
10
|
+
# /index.json
|
11
|
+
# /index.yaml
|
12
|
+
#
|
13
|
+
# /get/foo.json
|
14
|
+
# /get/foo.yaml
|
15
|
+
#
|
16
|
+
# Note that this functionality is quite experimental, but by strategically
|
17
|
+
# placing ressources in actions it may be possible to achieve interesting
|
18
|
+
# effects and interoperability with JavaScript at a low cost.
|
19
|
+
#
|
20
|
+
# TODO:
|
21
|
+
# * parsing requests based on the content-type, but that's much less
|
22
|
+
# straight-forward and would require some kind of convention?
|
23
|
+
|
24
|
+
class Dict
|
25
|
+
include Innate::Node
|
26
|
+
map '/'
|
27
|
+
|
28
|
+
DICT = {}
|
29
|
+
|
30
|
+
# /get/foo || /get/foo.json || /get/foo.yaml
|
31
|
+
def get(key)
|
32
|
+
{key => DICT[key]}
|
33
|
+
end
|
34
|
+
|
35
|
+
# /set/foo/bar || /set/foo/bar.json || /set/foo/bar.yaml
|
36
|
+
def set(key, value)
|
37
|
+
{key => (DICT[key] = value)}
|
38
|
+
end
|
39
|
+
|
40
|
+
# /index.json || /.json || /index.yaml || /.yaml
|
41
|
+
def index
|
42
|
+
DICT
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
Innate.start
|
data/example/session.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'innate'
|
2
|
+
|
3
|
+
class Hello
|
4
|
+
include Innate::Node
|
5
|
+
map '/'
|
6
|
+
|
7
|
+
helper :link, :cgi
|
8
|
+
|
9
|
+
provide :html => :haml
|
10
|
+
|
11
|
+
TEMPLATE = '
|
12
|
+
!!! XML
|
13
|
+
!!!
|
14
|
+
%html
|
15
|
+
%head
|
16
|
+
%title Session example
|
17
|
+
%body
|
18
|
+
%h1 Session example
|
19
|
+
= "Value is #{session[:value]}"
|
20
|
+
%br/
|
21
|
+
= a :increment
|
22
|
+
%br/
|
23
|
+
= a :decrement
|
24
|
+
'.strip
|
25
|
+
|
26
|
+
def index
|
27
|
+
session[:value] = 0
|
28
|
+
TEMPLATE
|
29
|
+
end
|
30
|
+
|
31
|
+
def increment
|
32
|
+
session[:value] += 1 if session[:value]
|
33
|
+
TEMPLATE
|
34
|
+
end
|
35
|
+
|
36
|
+
def decrement
|
37
|
+
session[:value] -= 1 if session[:value]
|
38
|
+
TEMPLATE
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
Innate.start
|
data/innate.gemspec
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "innate"
|
3
|
+
s.version = "2009.02.06"
|
4
|
+
|
5
|
+
s.summary = "Powerful web-framework wrapper for Rack."
|
6
|
+
s.description = "Simple, straight-forward, base for web-frameworks."
|
7
|
+
s.platform = "ruby"
|
8
|
+
s.has_rdoc = true
|
9
|
+
s.author = "Michael 'manveru' Fellinger"
|
10
|
+
s.email = "m.fellinger@gmail.com"
|
11
|
+
s.homepage = "http://github.com/manveru/innate"
|
12
|
+
s.require_path = "lib"
|
13
|
+
|
14
|
+
s.add_dependency('rack', '>= 0.4.0')
|
15
|
+
|
16
|
+
s.files = [
|
17
|
+
"CHANGELOG",
|
18
|
+
"COPYING",
|
19
|
+
"MANIFEST",
|
20
|
+
"README.md",
|
21
|
+
"Rakefile",
|
22
|
+
"example/app/retro_games.rb",
|
23
|
+
"example/app/whywiki_erb/layout/wiki.html.erb",
|
24
|
+
"example/app/whywiki_erb/spec/wiki.rb",
|
25
|
+
"example/app/whywiki_erb/start.rb",
|
26
|
+
"example/app/whywiki_erb/view/edit.html.erb",
|
27
|
+
"example/app/whywiki_erb/view/index.html.erb",
|
28
|
+
"example/custom_middleware.rb",
|
29
|
+
"example/error_handling.rb",
|
30
|
+
"example/hello.rb",
|
31
|
+
"example/howto_spec.rb",
|
32
|
+
"example/link.rb",
|
33
|
+
"example/providing_hash.rb",
|
34
|
+
"example/session.rb",
|
35
|
+
"innate.gemspec",
|
36
|
+
"lib/innate.rb",
|
37
|
+
"lib/innate/action.rb",
|
38
|
+
"lib/innate/adapter.rb",
|
39
|
+
"lib/innate/cache.rb",
|
40
|
+
"lib/innate/cache/api.rb",
|
41
|
+
"lib/innate/cache/drb.rb",
|
42
|
+
"lib/innate/cache/file_based.rb",
|
43
|
+
"lib/innate/cache/marshal.rb",
|
44
|
+
"lib/innate/cache/memory.rb",
|
45
|
+
"lib/innate/cache/yaml.rb",
|
46
|
+
"lib/innate/core_compatibility/basic_object.rb",
|
47
|
+
"lib/innate/core_compatibility/string.rb",
|
48
|
+
"lib/innate/current.rb",
|
49
|
+
"lib/innate/dynamap.rb",
|
50
|
+
"lib/innate/helper.rb",
|
51
|
+
"lib/innate/helper/aspect.rb",
|
52
|
+
"lib/innate/helper/cgi.rb",
|
53
|
+
"lib/innate/helper/flash.rb",
|
54
|
+
"lib/innate/helper/link.rb",
|
55
|
+
"lib/innate/helper/partial.rb",
|
56
|
+
"lib/innate/helper/redirect.rb",
|
57
|
+
"lib/innate/helper/send_file.rb",
|
58
|
+
"lib/innate/log.rb",
|
59
|
+
"lib/innate/log/color_formatter.rb",
|
60
|
+
"lib/innate/log/hub.rb",
|
61
|
+
"lib/innate/mock.rb",
|
62
|
+
"lib/innate/node.rb",
|
63
|
+
"lib/innate/options.rb",
|
64
|
+
"lib/innate/options/dsl.rb",
|
65
|
+
"lib/innate/request.rb",
|
66
|
+
"lib/innate/response.rb",
|
67
|
+
"lib/innate/route.rb",
|
68
|
+
"lib/innate/session.rb",
|
69
|
+
"lib/innate/session/flash.rb",
|
70
|
+
"lib/innate/setup.rb",
|
71
|
+
"lib/innate/spec.rb",
|
72
|
+
"lib/innate/state.rb",
|
73
|
+
"lib/innate/state/accessor.rb",
|
74
|
+
"lib/innate/state/fiber.rb",
|
75
|
+
"lib/innate/state/thread.rb",
|
76
|
+
"lib/innate/traited.rb",
|
77
|
+
"lib/innate/trinity.rb",
|
78
|
+
"lib/innate/version.rb",
|
79
|
+
"lib/innate/view.rb",
|
80
|
+
"lib/innate/view/erb.rb",
|
81
|
+
"lib/innate/view/none.rb",
|
82
|
+
"lib/rack/middleware_compiler.rb",
|
83
|
+
"lib/rack/reloader.rb",
|
84
|
+
"spec/example/hello.rb",
|
85
|
+
"spec/example/link.rb",
|
86
|
+
"spec/helper.rb",
|
87
|
+
"spec/innate/cache/common.rb",
|
88
|
+
"spec/innate/cache/marshal.rb",
|
89
|
+
"spec/innate/cache/memory.rb",
|
90
|
+
"spec/innate/cache/yaml.rb",
|
91
|
+
"spec/innate/dynamap.rb",
|
92
|
+
"spec/innate/helper.rb",
|
93
|
+
"spec/innate/helper/aspect.rb",
|
94
|
+
"spec/innate/helper/cgi.rb",
|
95
|
+
"spec/innate/helper/flash.rb",
|
96
|
+
"spec/innate/helper/link.rb",
|
97
|
+
"spec/innate/helper/partial.rb",
|
98
|
+
"spec/innate/helper/redirect.rb",
|
99
|
+
"spec/innate/helper/send_file.rb",
|
100
|
+
"spec/innate/helper/view/aspect_hello.erb",
|
101
|
+
"spec/innate/helper/view/locals.erb",
|
102
|
+
"spec/innate/helper/view/loop.erb",
|
103
|
+
"spec/innate/helper/view/num.erb",
|
104
|
+
"spec/innate/helper/view/partial.erb",
|
105
|
+
"spec/innate/helper/view/recursive.erb",
|
106
|
+
"spec/innate/mock.rb",
|
107
|
+
"spec/innate/node.rb",
|
108
|
+
"spec/innate/node/bar.html",
|
109
|
+
"spec/innate/node/foo.html.erb",
|
110
|
+
"spec/innate/node/with_layout.erb",
|
111
|
+
"spec/innate/options.rb",
|
112
|
+
"spec/innate/parameter.rb",
|
113
|
+
"spec/innate/request.rb",
|
114
|
+
"spec/innate/route.rb",
|
115
|
+
"spec/innate/session.rb",
|
116
|
+
"spec/innate/traited.rb"
|
117
|
+
]
|
118
|
+
end
|
data/lib/innate.rb
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
# What can be done with fewer assumptions is done in vain with more.
|
2
|
+
# -- William of Ockham (ca. 1285-1349)
|
3
|
+
#
|
4
|
+
# Name-space of Innate, just about everything goes in here.
|
5
|
+
#
|
6
|
+
# Exceptions are:
|
7
|
+
#
|
8
|
+
# * Logger::ColorFormatter
|
9
|
+
# * In 1.8, we define ::BasicObject
|
10
|
+
# * In 1.9, we define ::String#each
|
11
|
+
#
|
12
|
+
module Innate
|
13
|
+
ROOT = File.expand_path(File.dirname(__FILE__))
|
14
|
+
|
15
|
+
unless $LOAD_PATH.any?{|lp| File.expand_path(lp) == ROOT }
|
16
|
+
$LOAD_PATH.unshift(ROOT)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# stdlib
|
21
|
+
require 'pp'
|
22
|
+
require 'set'
|
23
|
+
require 'pathname'
|
24
|
+
require 'digest/sha1'
|
25
|
+
require 'ipaddr'
|
26
|
+
require 'socket'
|
27
|
+
require 'logger'
|
28
|
+
require 'uri'
|
29
|
+
|
30
|
+
# 3rd party
|
31
|
+
require 'rack'
|
32
|
+
|
33
|
+
# innate core patches
|
34
|
+
require 'innate/core_compatibility/string'
|
35
|
+
require 'innate/core_compatibility/basic_object'
|
36
|
+
|
37
|
+
# innate core
|
38
|
+
require 'innate/version'
|
39
|
+
require 'innate/traited'
|
40
|
+
require 'innate/cache'
|
41
|
+
require 'innate/node'
|
42
|
+
require 'innate/options'
|
43
|
+
require 'innate/log'
|
44
|
+
require 'innate/state'
|
45
|
+
require 'innate/trinity'
|
46
|
+
require 'innate/current'
|
47
|
+
require 'innate/mock'
|
48
|
+
require 'innate/adapter'
|
49
|
+
require 'innate/action'
|
50
|
+
require 'innate/helper'
|
51
|
+
require 'innate/view'
|
52
|
+
require 'innate/session'
|
53
|
+
require 'innate/session/flash'
|
54
|
+
require 'innate/dynamap'
|
55
|
+
require 'innate/route'
|
56
|
+
|
57
|
+
require 'rack/reloader'
|
58
|
+
require 'rack/middleware_compiler'
|
59
|
+
|
60
|
+
module Innate
|
61
|
+
extend Trinity
|
62
|
+
|
63
|
+
# Note that `m.innate` takes away most of the boring part and leaves it up to
|
64
|
+
# you to select your middleware in your application.
|
65
|
+
#
|
66
|
+
# This expands to:
|
67
|
+
#
|
68
|
+
# use Rack::ShowExceptions
|
69
|
+
# use Rack::RouteExceptions
|
70
|
+
# use Rack::ShowStatus
|
71
|
+
# use Rack::Reloader
|
72
|
+
# use Rack::Cascade.new([
|
73
|
+
# Rack::File.new('public'),
|
74
|
+
# Innate::Current.new(
|
75
|
+
# Rack::Cascade.new([
|
76
|
+
# Innate::Rewrite.new(Innate::DynaMap),
|
77
|
+
# Innate::Route.new(Innate::DynaMap)])))
|
78
|
+
DEFAULT_MIDDLEWARE = lambda{|m|
|
79
|
+
m.use Rack::CommonLogger # usually fast, depending on the output
|
80
|
+
m.use Rack::ShowExceptions # fast
|
81
|
+
# m.use Rack::RouteExceptions # fast, use when you have custom error pages.
|
82
|
+
m.use Rack::ShowStatus # fast
|
83
|
+
m.use Rack::Reloader # reasonably fast depending on settings
|
84
|
+
# m.use Rack::Lint # slow, use only while developing
|
85
|
+
|
86
|
+
m.innate
|
87
|
+
}
|
88
|
+
|
89
|
+
module_function
|
90
|
+
|
91
|
+
def start(parameter = {}, &block)
|
92
|
+
setup_dependencies
|
93
|
+
setup_middleware(&block)
|
94
|
+
|
95
|
+
options[:app][:root] = go_figure_root(parameter, caller)
|
96
|
+
parameter.reject!{|k, v| [:root, :file].include?(k) }
|
97
|
+
options.merge!(parameter)
|
98
|
+
|
99
|
+
return if options.started
|
100
|
+
options.started = true
|
101
|
+
|
102
|
+
trap(options[:trap]){ stop(10) } if options[:trap]
|
103
|
+
|
104
|
+
start!(options)
|
105
|
+
end
|
106
|
+
|
107
|
+
def start!(options = Innate.options)
|
108
|
+
Adapter.start(middleware(:innate), options)
|
109
|
+
end
|
110
|
+
|
111
|
+
def stop(wait = 3)
|
112
|
+
Log.info("Shutdown Innate within #{wait} seconds")
|
113
|
+
Timeout.timeout(wait){ exit }
|
114
|
+
ensure
|
115
|
+
exit!
|
116
|
+
end
|
117
|
+
|
118
|
+
def middleware(name, &block)
|
119
|
+
Rack::MiddlewareCompiler.build(name, &block)
|
120
|
+
end
|
121
|
+
|
122
|
+
def middleware!(name, &block)
|
123
|
+
Rack::MiddlewareCompiler.build!(name, &block)
|
124
|
+
end
|
125
|
+
|
126
|
+
def setup_dependencies
|
127
|
+
options[:setup].each{|obj| obj.setup }
|
128
|
+
end
|
129
|
+
|
130
|
+
# Set the default middleware for applications.
|
131
|
+
def setup_middleware(&block)
|
132
|
+
middleware(:innate, &(block || DEFAULT_MIDDLEWARE))
|
133
|
+
end
|
134
|
+
|
135
|
+
# Pass the +env+ to this method and it will be sent to the appropriate
|
136
|
+
# middleware called +mw+.
|
137
|
+
# Tries to avoid recursion.
|
138
|
+
|
139
|
+
def call(env, mw = :innate)
|
140
|
+
this_file = File.expand_path(__FILE__)
|
141
|
+
count = 0
|
142
|
+
caller_lines(caller){|f, l, m| count += 1 if f == this_file }
|
143
|
+
|
144
|
+
raise RuntimeError, "Recursive loop in Innate::call" if count > 10
|
145
|
+
|
146
|
+
middleware(mw).call(env)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Innate can be started by:
|
150
|
+
#
|
151
|
+
# Innate.start :file => __FILE__
|
152
|
+
# Innate.start :root => '/path/to/here'
|
153
|
+
#
|
154
|
+
# In case these options are not passed we will try to figure out a file named
|
155
|
+
# `start.rb` in the backtrace and use the directory it resides in.
|
156
|
+
#
|
157
|
+
# TODO: better documentation and nice defaults, don't want to rely on a
|
158
|
+
# filename, bad mojo.
|
159
|
+
|
160
|
+
def go_figure_root(options, backtrace)
|
161
|
+
if o_file = options[:file]
|
162
|
+
return File.dirname(o_file)
|
163
|
+
elsif root = options[:root]
|
164
|
+
return root
|
165
|
+
end
|
166
|
+
|
167
|
+
pwd = Dir.pwd
|
168
|
+
|
169
|
+
return pwd if File.file?(File.join(pwd, 'start.rb'))
|
170
|
+
|
171
|
+
caller_lines(backtrace) do |file, line, method|
|
172
|
+
dir, file = File.split(File.expand_path(file))
|
173
|
+
return dir if file == "start.rb"
|
174
|
+
end
|
175
|
+
|
176
|
+
return nil
|
177
|
+
end
|
178
|
+
|
179
|
+
# yields +file+, +line+, +method+
|
180
|
+
def caller_lines(backtrace)
|
181
|
+
backtrace.each do |line|
|
182
|
+
if line =~ /^(.*?):(\d+):in `(.*)'$/
|
183
|
+
file, line, method = $1, $2.to_i, $3
|
184
|
+
elsif line =~ /^(.*?):(\d+)$/
|
185
|
+
file, line, method = $1, $2.to_i, nil
|
186
|
+
end
|
187
|
+
|
188
|
+
yield(File.expand_path(file), line, method) if file and File.file?(file)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
module Innate
|
2
|
+
ACTION_MEMBERS = [ :node, :method, :params, :view, :layout, :instance, :exts,
|
3
|
+
:wish, :options, :variables, :value, :view_value, :name ]
|
4
|
+
|
5
|
+
class Action < Struct.new(*ACTION_MEMBERS)
|
6
|
+
# Create a new Action instance.
|
7
|
+
#
|
8
|
+
# @param [Hash, #values_at] hash used to seed new Action instance
|
9
|
+
# @return [Action] action with the given defaults from hash
|
10
|
+
# @author manveru
|
11
|
+
def self.create(hash = {})
|
12
|
+
new(*hash.values_at(*ACTION_MEMBERS))
|
13
|
+
end
|
14
|
+
|
15
|
+
# Call the Action instance, will insert itself temporarily into Current.actions during the render operation so even in nested calls one can still access all other Action instances.
|
16
|
+
# Will initialize the assigned node and call Action#render
|
17
|
+
#
|
18
|
+
# @return [String] The rendition of all nested calls
|
19
|
+
# @see Action#render Node#action_found
|
20
|
+
# @author manveru
|
21
|
+
def call
|
22
|
+
Current.actions << self
|
23
|
+
self.instance = node.new
|
24
|
+
self.variables[:content] ||= nil
|
25
|
+
render
|
26
|
+
ensure
|
27
|
+
Current.actions.delete(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Binding] binding of the instance for this Action
|
31
|
+
# @see Node#binding
|
32
|
+
# @author manveru
|
33
|
+
def binding
|
34
|
+
instance.binding
|
35
|
+
end
|
36
|
+
|
37
|
+
# Copy the instance variable names and values from given
|
38
|
+
# from_action#instance into the Action#variables of the action this method
|
39
|
+
# is called on.
|
40
|
+
#
|
41
|
+
# @param [Action #instance] from_action
|
42
|
+
# @return [Action] from_action
|
43
|
+
# @see Action#wrap_in_layout
|
44
|
+
# @author manveru
|
45
|
+
def sync_variables(from_action)
|
46
|
+
instance = from_action.instance
|
47
|
+
|
48
|
+
instance.instance_variables.each{|iv|
|
49
|
+
iv_value = instance.instance_variable_get(iv)
|
50
|
+
iv_name = iv.to_s[1..-1]
|
51
|
+
self.variables[iv_name.to_sym] = iv_value
|
52
|
+
}
|
53
|
+
|
54
|
+
from_action
|
55
|
+
end
|
56
|
+
|
57
|
+
# Copy Action#variables as instance variables into the given binding.
|
58
|
+
#
|
59
|
+
# This relies on Innate::STATE, so should be thread-safe and doesn't depend
|
60
|
+
# on Innate::Current::actions order.
|
61
|
+
# So we avoid nasty business with Objectspace#_id2ref which may not work on
|
62
|
+
# all ruby implementations and seems to cause other problems as well.
|
63
|
+
#
|
64
|
+
# @param [Binding #eval] binding
|
65
|
+
# @return [NilClass] there is no indication of failure or success
|
66
|
+
# @see View::ERB::render
|
67
|
+
# @author manveru
|
68
|
+
def copy_variables(binding = self.binding)
|
69
|
+
return unless variables.any?
|
70
|
+
|
71
|
+
STATE.sync do
|
72
|
+
STATE[:action_variables] = self.variables
|
73
|
+
|
74
|
+
binding.eval('
|
75
|
+
STATE[:action_variables].each do |iv, value|
|
76
|
+
instance_variable_set("@#{iv}", value)
|
77
|
+
end')
|
78
|
+
|
79
|
+
STATE[:action_variables] = nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def render
|
84
|
+
instance.wrap_action_call(self) do
|
85
|
+
self.value = instance.__send__(method, *params) if method
|
86
|
+
self.view_value = File.read(view) if view
|
87
|
+
end
|
88
|
+
|
89
|
+
content_type, body = send(Innate.options.action.wish[wish] || :as_html)
|
90
|
+
Current.response['Content-Type'] ||= content_type
|
91
|
+
|
92
|
+
body
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return [Array] Content-Type and rendered action
|
96
|
+
# @see Action#render Action#wrap_in_layout
|
97
|
+
# @author manveru
|
98
|
+
def as_html
|
99
|
+
return 'text/html', wrap_in_layout{ fulfill_wish(view_value || value) }
|
100
|
+
end
|
101
|
+
|
102
|
+
# @return [Array] Content-Type and rendered action
|
103
|
+
# @see Action#render Action#wrap_in_layout
|
104
|
+
# @author manveru
|
105
|
+
def as_yaml
|
106
|
+
require 'yaml'
|
107
|
+
return 'text/yaml', (value || view_value).to_yaml
|
108
|
+
end
|
109
|
+
|
110
|
+
# @return [Array] Content-Type and rendered action
|
111
|
+
# @see Action#render Action#wrap_in_layout
|
112
|
+
# @author manveru
|
113
|
+
def as_json
|
114
|
+
require 'json'
|
115
|
+
return 'application/json', (value || view_value).to_json
|
116
|
+
end
|
117
|
+
|
118
|
+
# @param [String, #to_str] string to be rendered
|
119
|
+
# @return [String] The rendered result of the templating engine
|
120
|
+
# @raise [RuntimeError] if no suitable templating engine was found
|
121
|
+
# @see Action#as_html
|
122
|
+
# @author manveru
|
123
|
+
def fulfill_wish(string)
|
124
|
+
way = File.basename(view).gsub!(/.*?#{wish}\./, '') if view
|
125
|
+
way ||= node.provide[wish] || node.provide['html']
|
126
|
+
|
127
|
+
if way
|
128
|
+
# Rack::Mime.mime_type(".#{wish}", 'text/html')
|
129
|
+
View.get(way).render(self, string)
|
130
|
+
else
|
131
|
+
raise("No templating engine was found for %p" % way)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def wrap_in_layout
|
136
|
+
return yield unless layout
|
137
|
+
|
138
|
+
action = dup
|
139
|
+
action.view, action.method = layout_view_or_method(*layout)
|
140
|
+
action.layout = nil
|
141
|
+
action.sync_variables(self)
|
142
|
+
action.variables[:content] = yield
|
143
|
+
action.call
|
144
|
+
end
|
145
|
+
|
146
|
+
def layout_view_or_method(name, arg)
|
147
|
+
return arg, nil if name == :layout || name == :view
|
148
|
+
return nil, arg
|
149
|
+
end
|
150
|
+
|
151
|
+
#Try to figure out a sane name for current action.
|
152
|
+
def name
|
153
|
+
File.basename((method || view).to_s).split('.').first
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|