innate 2009.04
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +2981 -0
- data/COPYING +18 -0
- data/MANIFEST +127 -0
- data/README.md +563 -0
- data/Rakefile +35 -0
- data/example/app/retro_games.rb +60 -0
- data/example/app/todo/layout/default.xhtml +11 -0
- data/example/app/todo/spec/todo.rb +63 -0
- data/example/app/todo/start.rb +51 -0
- data/example/app/todo/view/index.xhtml +39 -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 +42 -0
- data/example/app/whywiki_erb/view/edit.erb +6 -0
- data/example/app/whywiki_erb/view/index.erb +12 -0
- data/example/custom_middleware.rb +35 -0
- data/example/hello.rb +11 -0
- data/example/howto_spec.rb +35 -0
- data/example/link.rb +27 -0
- data/example/provides.rb +31 -0
- data/example/session.rb +38 -0
- data/innate.gemspec +29 -0
- data/lib/innate.rb +269 -0
- data/lib/innate/action.rb +150 -0
- data/lib/innate/adapter.rb +76 -0
- data/lib/innate/cache.rb +134 -0
- data/lib/innate/cache/api.rb +128 -0
- data/lib/innate/cache/drb.rb +58 -0
- data/lib/innate/cache/file_based.rb +41 -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/current.rb +37 -0
- data/lib/innate/dynamap.rb +96 -0
- data/lib/innate/helper.rb +183 -0
- data/lib/innate/helper/aspect.rb +124 -0
- data/lib/innate/helper/cgi.rb +54 -0
- data/lib/innate/helper/flash.rb +36 -0
- data/lib/innate/helper/link.rb +94 -0
- data/lib/innate/helper/redirect.rb +85 -0
- data/lib/innate/helper/render.rb +87 -0
- data/lib/innate/helper/send_file.rb +26 -0
- data/lib/innate/log.rb +20 -0
- data/lib/innate/log/color_formatter.rb +43 -0
- data/lib/innate/log/hub.rb +73 -0
- data/lib/innate/middleware_compiler.rb +65 -0
- data/lib/innate/mock.rb +49 -0
- data/lib/innate/node.rb +1025 -0
- data/lib/innate/options.rb +37 -0
- data/lib/innate/options/dsl.rb +202 -0
- data/lib/innate/options/stub.rb +7 -0
- data/lib/innate/request.rb +141 -0
- data/lib/innate/response.rb +23 -0
- data/lib/innate/route.rb +110 -0
- data/lib/innate/session.rb +121 -0
- data/lib/innate/session/flash.rb +94 -0
- data/lib/innate/spec.rb +23 -0
- data/lib/innate/state.rb +27 -0
- data/lib/innate/state/accessor.rb +130 -0
- data/lib/innate/state/fiber.rb +74 -0
- data/lib/innate/state/thread.rb +47 -0
- data/lib/innate/traited.rb +85 -0
- data/lib/innate/trinity.rb +18 -0
- data/lib/innate/version.rb +3 -0
- data/lib/innate/view.rb +60 -0
- data/lib/innate/view/erb.rb +15 -0
- data/lib/innate/view/etanni.rb +36 -0
- data/lib/innate/view/none.rb +9 -0
- data/spec/example/app/retro_games.rb +30 -0
- data/spec/example/hello.rb +13 -0
- data/spec/example/link.rb +25 -0
- data/spec/example/provides.rb +16 -0
- data/spec/example/session.rb +22 -0
- data/spec/helper.rb +10 -0
- data/spec/innate/action/layout.rb +107 -0
- data/spec/innate/action/layout/file_layout.xhtml +1 -0
- data/spec/innate/cache/common.rb +47 -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 +86 -0
- data/spec/innate/helper/aspect.rb +75 -0
- data/spec/innate/helper/cgi.rb +37 -0
- data/spec/innate/helper/flash.rb +118 -0
- data/spec/innate/helper/link.rb +139 -0
- data/spec/innate/helper/redirect.rb +160 -0
- data/spec/innate/helper/render.rb +133 -0
- data/spec/innate/helper/send_file.rb +21 -0
- data/spec/innate/helper/view/aspect_hello.xhtml +1 -0
- data/spec/innate/helper/view/locals.xhtml +1 -0
- data/spec/innate/helper/view/loop.xhtml +4 -0
- data/spec/innate/helper/view/num.xhtml +1 -0
- data/spec/innate/helper/view/partial.xhtml +1 -0
- data/spec/innate/helper/view/recursive.xhtml +7 -0
- data/spec/innate/mock.rb +84 -0
- data/spec/innate/node/mapping.rb +37 -0
- data/spec/innate/node/node.rb +134 -0
- data/spec/innate/node/resolve.rb +82 -0
- data/spec/innate/node/view/another_layout/another_layout.xhtml +3 -0
- data/spec/innate/node/view/bar.xhtml +1 -0
- data/spec/innate/node/view/foo.html.xhtml +1 -0
- data/spec/innate/node/view/only_view.xhtml +1 -0
- data/spec/innate/node/view/with_layout.xhtml +1 -0
- data/spec/innate/node/wrap_action_call.rb +83 -0
- data/spec/innate/options.rb +115 -0
- data/spec/innate/parameter.rb +154 -0
- data/spec/innate/provides.rb +99 -0
- data/spec/innate/provides/list.html.xhtml +1 -0
- data/spec/innate/provides/list.txt.xhtml +1 -0
- data/spec/innate/request.rb +77 -0
- data/spec/innate/route.rb +135 -0
- data/spec/innate/session.rb +54 -0
- data/spec/innate/state/fiber.rb +58 -0
- data/spec/innate/state/thread.rb +40 -0
- data/spec/innate/traited.rb +55 -0
- data/tasks/bacon.rake +66 -0
- data/tasks/changelog.rake +18 -0
- data/tasks/gem.rake +22 -0
- data/tasks/gem_installer.rake +76 -0
- data/tasks/grancher.rake +12 -0
- data/tasks/install_dependencies.rake +4 -0
- data/tasks/manifest.rake +4 -0
- data/tasks/rcov.rake +19 -0
- data/tasks/release.rake +51 -0
- data/tasks/reversion.rake +8 -0
- data/tasks/setup.rake +28 -0
- metadata +181 -0
data/example/link.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'innate'
|
2
|
+
|
3
|
+
class Linking
|
4
|
+
Innate.node '/'
|
5
|
+
|
6
|
+
def index
|
7
|
+
"Index links to " + a('Help?', :help)
|
8
|
+
end
|
9
|
+
|
10
|
+
def help
|
11
|
+
"Help links to " + Different.a('A Different Node', :another)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Different
|
16
|
+
Innate.node '/link_to'
|
17
|
+
|
18
|
+
def another
|
19
|
+
a('Another links even deeper', 'and/deeper')
|
20
|
+
end
|
21
|
+
|
22
|
+
def and__deeper
|
23
|
+
Linking.a('Back to Linking Node', :index)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
Innate.start
|
data/example/provides.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'innate'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
ARTICLES = {
|
6
|
+
'hello' => {
|
7
|
+
:author => 'manveru',
|
8
|
+
:title => 'Hello, World!',
|
9
|
+
:text => 'Some text'
|
10
|
+
}
|
11
|
+
}
|
12
|
+
|
13
|
+
class BlogArticles
|
14
|
+
Innate.node('/')
|
15
|
+
|
16
|
+
# provide a content representation for requests to /<action>.yaml
|
17
|
+
# If you request `/list.yaml`, you will get the `ARTICLES object serialized
|
18
|
+
# to YAML.
|
19
|
+
provide(:yaml, :type => 'text/yaml'){|action, value| value.to_yaml }
|
20
|
+
|
21
|
+
# Since there will always be an `html` representation (the default), you have
|
22
|
+
# to take care of it. If you simply want to return an empty page, use following.
|
23
|
+
provide(:html){|action, value| '' }
|
24
|
+
|
25
|
+
# The return value of this method is the `value` in the provides above.
|
26
|
+
def list
|
27
|
+
return ARTICLES
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
Innate.start
|
data/example/session.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'innate'
|
2
|
+
|
3
|
+
class Hello
|
4
|
+
Innate.node '/'
|
5
|
+
|
6
|
+
TEMPLATE = '
|
7
|
+
<html>
|
8
|
+
<head>
|
9
|
+
<title>Session example</title>
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
<h1>Session example</h1>
|
13
|
+
<p>
|
14
|
+
Value is: #{ session[:value] }<br />
|
15
|
+
#{ a :increment }<br />
|
16
|
+
#{ a :decrement }
|
17
|
+
</p>
|
18
|
+
</body>
|
19
|
+
</html>
|
20
|
+
'.strip
|
21
|
+
|
22
|
+
def index
|
23
|
+
session[:value] = 0
|
24
|
+
TEMPLATE
|
25
|
+
end
|
26
|
+
|
27
|
+
def increment
|
28
|
+
session[:value] = session[:value].to_i + 1
|
29
|
+
TEMPLATE
|
30
|
+
end
|
31
|
+
|
32
|
+
def decrement
|
33
|
+
session[:value] = session[:value].to_i - 1
|
34
|
+
TEMPLATE
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Innate.start
|
data/innate.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{innate}
|
5
|
+
s.version = "2009.04"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Michael 'manveru' Fellinger"]
|
9
|
+
s.date = %q{2009-04-26}
|
10
|
+
s.description = %q{Simple, straight-forward base for web-frameworks.}
|
11
|
+
s.email = %q{m.fellinger@gmail.com}
|
12
|
+
s.files = ["CHANGELOG", "COPYING", "MANIFEST", "README.md", "Rakefile", "example/app/retro_games.rb", "example/app/todo/layout/default.xhtml", "example/app/todo/spec/todo.rb", "example/app/todo/start.rb", "example/app/todo/view/index.xhtml", "example/app/whywiki_erb/layout/wiki.html.erb", "example/app/whywiki_erb/spec/wiki.rb", "example/app/whywiki_erb/start.rb", "example/app/whywiki_erb/view/edit.erb", "example/app/whywiki_erb/view/index.erb", "example/custom_middleware.rb", "example/hello.rb", "example/howto_spec.rb", "example/link.rb", "example/provides.rb", "example/session.rb", "innate.gemspec", "lib/innate.rb", "lib/innate/action.rb", "lib/innate/adapter.rb", "lib/innate/cache.rb", "lib/innate/cache/api.rb", "lib/innate/cache/drb.rb", "lib/innate/cache/file_based.rb", "lib/innate/cache/marshal.rb", "lib/innate/cache/memory.rb", "lib/innate/cache/yaml.rb", "lib/innate/current.rb", "lib/innate/dynamap.rb", "lib/innate/helper.rb", "lib/innate/helper/aspect.rb", "lib/innate/helper/cgi.rb", "lib/innate/helper/flash.rb", "lib/innate/helper/link.rb", "lib/innate/helper/redirect.rb", "lib/innate/helper/render.rb", "lib/innate/helper/send_file.rb", "lib/innate/log.rb", "lib/innate/log/color_formatter.rb", "lib/innate/log/hub.rb", "lib/innate/middleware_compiler.rb", "lib/innate/mock.rb", "lib/innate/node.rb", "lib/innate/options.rb", "lib/innate/options/dsl.rb", "lib/innate/options/stub.rb", "lib/innate/request.rb", "lib/innate/response.rb", "lib/innate/route.rb", "lib/innate/session.rb", "lib/innate/session/flash.rb", "lib/innate/spec.rb", "lib/innate/state.rb", "lib/innate/state/accessor.rb", "lib/innate/state/fiber.rb", "lib/innate/state/thread.rb", "lib/innate/traited.rb", "lib/innate/trinity.rb", "lib/innate/version.rb", "lib/innate/view.rb", "lib/innate/view/erb.rb", "lib/innate/view/etanni.rb", "lib/innate/view/none.rb", "spec/example/app/retro_games.rb", "spec/example/hello.rb", "spec/example/link.rb", "spec/example/provides.rb", "spec/example/session.rb", "spec/helper.rb", "spec/innate/action/layout.rb", "spec/innate/action/layout/file_layout.xhtml", "spec/innate/cache/common.rb", "spec/innate/cache/marshal.rb", "spec/innate/cache/memory.rb", "spec/innate/cache/yaml.rb", "spec/innate/dynamap.rb", "spec/innate/helper.rb", "spec/innate/helper/aspect.rb", "spec/innate/helper/cgi.rb", "spec/innate/helper/flash.rb", "spec/innate/helper/link.rb", "spec/innate/helper/redirect.rb", "spec/innate/helper/render.rb", "spec/innate/helper/send_file.rb", "spec/innate/helper/view/aspect_hello.xhtml", "spec/innate/helper/view/locals.xhtml", "spec/innate/helper/view/loop.xhtml", "spec/innate/helper/view/num.xhtml", "spec/innate/helper/view/partial.xhtml", "spec/innate/helper/view/recursive.xhtml", "spec/innate/mock.rb", "spec/innate/node/mapping.rb", "spec/innate/node/node.rb", "spec/innate/node/resolve.rb", "spec/innate/node/view/another_layout/another_layout.xhtml", "spec/innate/node/view/bar.xhtml", "spec/innate/node/view/foo.html.xhtml", "spec/innate/node/view/only_view.xhtml", "spec/innate/node/view/with_layout.xhtml", "spec/innate/node/wrap_action_call.rb", "spec/innate/options.rb", "spec/innate/parameter.rb", "spec/innate/provides.rb", "spec/innate/provides/list.html.xhtml", "spec/innate/provides/list.txt.xhtml", "spec/innate/request.rb", "spec/innate/route.rb", "spec/innate/session.rb", "spec/innate/state/fiber.rb", "spec/innate/state/thread.rb", "spec/innate/traited.rb", "tasks/bacon.rake", "tasks/changelog.rake", "tasks/gem.rake", "tasks/gem_installer.rake", "tasks/grancher.rake", "tasks/install_dependencies.rake", "tasks/manifest.rake", "tasks/rcov.rake", "tasks/release.rake", "tasks/reversion.rake", "tasks/setup.rake"]
|
13
|
+
s.has_rdoc = true
|
14
|
+
s.homepage = %q{http://github.com/manveru/innate}
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
s.rubyforge_project = %q{innate}
|
17
|
+
s.rubygems_version = %q{1.3.2}
|
18
|
+
s.summary = %q{Powerful web-framework wrapper for Rack.}
|
19
|
+
|
20
|
+
if s.respond_to? :specification_version then
|
21
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
22
|
+
s.specification_version = 3
|
23
|
+
|
24
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
25
|
+
else
|
26
|
+
end
|
27
|
+
else
|
28
|
+
end
|
29
|
+
end
|
data/lib/innate.rb
ADDED
@@ -0,0 +1,269 @@
|
|
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
|
+
# The only exception is Logger::ColorFormatter.
|
7
|
+
#
|
8
|
+
module Innate
|
9
|
+
ROOT = File.expand_path(File.dirname(__FILE__))
|
10
|
+
|
11
|
+
unless $LOAD_PATH.any?{|lp| File.expand_path(lp) == ROOT }
|
12
|
+
$LOAD_PATH.unshift(ROOT)
|
13
|
+
end
|
14
|
+
|
15
|
+
# stdlib
|
16
|
+
require 'pp'
|
17
|
+
require 'set'
|
18
|
+
require 'pathname'
|
19
|
+
require 'digest/sha1'
|
20
|
+
require 'digest/sha2'
|
21
|
+
require 'ipaddr'
|
22
|
+
require 'socket'
|
23
|
+
require 'logger'
|
24
|
+
require 'uri'
|
25
|
+
|
26
|
+
# 3rd party
|
27
|
+
require 'rack'
|
28
|
+
|
29
|
+
# innate core
|
30
|
+
require 'innate/version'
|
31
|
+
require 'innate/traited'
|
32
|
+
require 'innate/trinity'
|
33
|
+
require 'innate/middleware_compiler'
|
34
|
+
require 'innate/options/dsl'
|
35
|
+
require 'innate/options/stub'
|
36
|
+
require 'innate/dynamap'
|
37
|
+
|
38
|
+
# innate full
|
39
|
+
require 'innate/cache'
|
40
|
+
require 'innate/node'
|
41
|
+
require 'innate/options'
|
42
|
+
require 'innate/log'
|
43
|
+
require 'innate/state'
|
44
|
+
require 'innate/current'
|
45
|
+
require 'innate/mock'
|
46
|
+
require 'innate/adapter'
|
47
|
+
require 'innate/action'
|
48
|
+
require 'innate/helper'
|
49
|
+
require 'innate/view'
|
50
|
+
require 'innate/session'
|
51
|
+
require 'innate/session/flash'
|
52
|
+
require 'innate/route'
|
53
|
+
|
54
|
+
extend Trinity
|
55
|
+
|
56
|
+
# Contains all the module functions for Innate, we keep them in a module so
|
57
|
+
# Ramaze can simply use them as well.
|
58
|
+
module SingletonMethods
|
59
|
+
PROXY_OPTIONS = { :port => 'adapter.port', :host => 'adapter.host',
|
60
|
+
:adapter => 'adapter.handler' }
|
61
|
+
# The method that starts the whole business.
|
62
|
+
#
|
63
|
+
# Call Innate.start after you defined your application.
|
64
|
+
#
|
65
|
+
# Usually, this is a blocking call and will not return until the adapter
|
66
|
+
# has finished, which usually happens when you kill the application or hit
|
67
|
+
# ^C.
|
68
|
+
#
|
69
|
+
# We do return if options.started is true, which indicates that all you
|
70
|
+
# wanted to do is setup the environment and update options.
|
71
|
+
#
|
72
|
+
# @example usage
|
73
|
+
#
|
74
|
+
# # passing options
|
75
|
+
# Innate.start :adapter => :mongrel, :mode => :live
|
76
|
+
#
|
77
|
+
# # defining custom middleware
|
78
|
+
# Innate.start do |m|
|
79
|
+
# m.innate
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# @return [nil] if options.started is true
|
83
|
+
# @yield [MiddlewareCompiler]
|
84
|
+
# @param [Proc] block will be passed to {middleware!}
|
85
|
+
#
|
86
|
+
# @option param :host [String] ('0.0.0.0')
|
87
|
+
# IP address or hostname that we respond to - 0.0.0.0 for all
|
88
|
+
# @option param :port [Fixnum] (7000)
|
89
|
+
# Port for the server
|
90
|
+
# @option param :started [boolean] (false)
|
91
|
+
# Indicate that calls Innate::start will be ignored
|
92
|
+
# @option param :adapter [Symbol] (:webrick)
|
93
|
+
# Web server to run on
|
94
|
+
# @option param :setup [Array] ([Innate::Cache, Innate::Node])
|
95
|
+
# Will send ::setup to each element during Innate::start
|
96
|
+
# @option param :header [Hash] ({'Content-Type' => 'text/html'})
|
97
|
+
# Headers that will be merged into the response before Node::call
|
98
|
+
# @option param :trap [String] ('SIGINT')
|
99
|
+
# Trap this signal to issue shutdown, nil/false to disable trap
|
100
|
+
# @option param :state [Symbol] (:Fiber)
|
101
|
+
# Keep state in Thread or Fiber, fall back to Thread if Fiber not available
|
102
|
+
# @option param :mode [Symbol] (:dev)
|
103
|
+
# Indicates which default middleware to use, (:dev|:live)
|
104
|
+
def start(given_options = {}, &block)
|
105
|
+
root = given_options.delete(:root)
|
106
|
+
file = given_options.delete(:file)
|
107
|
+
|
108
|
+
found_root = go_figure_root(caller, :root => root, :file => file)
|
109
|
+
Innate.options.roots = [found_root] if found_root
|
110
|
+
|
111
|
+
# Convert some top-level option keys to the internal ones that we use.
|
112
|
+
PROXY_OPTIONS.each{|k,v| given_options[v] = given_options.delete(k) }
|
113
|
+
given_options.delete_if{|k,v| v.nil? }
|
114
|
+
|
115
|
+
# Merge the user's given options into our existing set, which contains defaults.
|
116
|
+
options.merge!(given_options)
|
117
|
+
|
118
|
+
setup_dependencies
|
119
|
+
middleware!(options.mode, &block) if block_given?
|
120
|
+
|
121
|
+
return if options.started
|
122
|
+
options.started = true
|
123
|
+
|
124
|
+
signal = options.trap
|
125
|
+
trap(signal){ stop(10) } if signal
|
126
|
+
|
127
|
+
start!
|
128
|
+
end
|
129
|
+
|
130
|
+
def start!(mode = options[:mode])
|
131
|
+
Adapter.start(middleware(mode))
|
132
|
+
end
|
133
|
+
|
134
|
+
def stop(wait = 3)
|
135
|
+
Log.info("Shutdown within #{wait} seconds")
|
136
|
+
Timeout.timeout(wait){ teardown_dependencies }
|
137
|
+
Timeout.timeout(wait){ exit }
|
138
|
+
ensure
|
139
|
+
exit!
|
140
|
+
end
|
141
|
+
|
142
|
+
def setup_dependencies
|
143
|
+
options[:setup].each{|obj| obj.setup if obj.respond_to?(:setup) }
|
144
|
+
end
|
145
|
+
|
146
|
+
def teardown_dependencies
|
147
|
+
options[:setup].each{|obj| obj.teardown if obj.respond_to?(:teardown) }
|
148
|
+
end
|
149
|
+
|
150
|
+
# Treat Innate like a rack application, pass the rack +env+ and optionally
|
151
|
+
# the +mode+ the application runs in.
|
152
|
+
#
|
153
|
+
# @param [Hash] env rack env
|
154
|
+
# @param [Symbol] mode indicates the mode of the application
|
155
|
+
# @default mode options.mode
|
156
|
+
# @return [Array] with [body, header, status]
|
157
|
+
# @author manveru
|
158
|
+
def call(env, mode = options[:mode])
|
159
|
+
middleware(mode).call(env)
|
160
|
+
end
|
161
|
+
|
162
|
+
def middleware(mode = options[:mode], &block)
|
163
|
+
options[:middleware_compiler].build(mode, &block)
|
164
|
+
end
|
165
|
+
|
166
|
+
def middleware!(mode = options[:mode], &block)
|
167
|
+
options[:middleware_compiler].build!(mode, &block)
|
168
|
+
end
|
169
|
+
|
170
|
+
def middleware_recompile(mode = options[:mode])
|
171
|
+
options[:middleware_compiler]::COMPILED[mode].compile!
|
172
|
+
end
|
173
|
+
|
174
|
+
# @example Innate can be started by:
|
175
|
+
#
|
176
|
+
# Innate.start :file => __FILE__
|
177
|
+
# Innate.start :root => File.dirname(__FILE__)
|
178
|
+
#
|
179
|
+
# Either setting will surpress the warning that might show up on startup
|
180
|
+
# and tells you it couldn't find an explicit root.
|
181
|
+
#
|
182
|
+
# In case these options are not passed we will try to figure out a file named
|
183
|
+
# `start.rb` in the process' working directory and assume it's a valid point.
|
184
|
+
def go_figure_root(backtrace, options)
|
185
|
+
if root = options[:root]
|
186
|
+
root
|
187
|
+
elsif file = options[:file]
|
188
|
+
File.dirname(file)
|
189
|
+
elsif File.file?('start.rb')
|
190
|
+
Dir.pwd
|
191
|
+
else
|
192
|
+
root = File.dirname(backtrace[0][/^(.*?):\d+/, 1])
|
193
|
+
Log.warn "No explicit root folder found, assuming it is #{root}"
|
194
|
+
root
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
extend SingletonMethods
|
200
|
+
|
201
|
+
# This sets up the default modes.
|
202
|
+
# The Proc to use is determined by the value of options.mode.
|
203
|
+
# The Proc value is passed to setup_middleware if no block is given to
|
204
|
+
# Innate::start.
|
205
|
+
#
|
206
|
+
# A quick overview over the middleware used here:
|
207
|
+
#
|
208
|
+
# * Rack::CommonLogger
|
209
|
+
# Logs a line in Apache common log format or <tt>rack.errors</tt>.
|
210
|
+
#
|
211
|
+
# * Rack::ShowExceptions
|
212
|
+
# Catches all exceptions raised from the app it wraps. It shows a useful
|
213
|
+
# backtrace with the sourcefile and clickable context, the whole Rack
|
214
|
+
# environment and the request data.
|
215
|
+
# Be careful when you use this on public-facing sites as it could reveal
|
216
|
+
# information helpful to attackers.
|
217
|
+
#
|
218
|
+
# * Rack::ShowStatus
|
219
|
+
# Catches all empty responses the app it wraps and replaces them with a
|
220
|
+
# site explaining the error.
|
221
|
+
# Additional details can be put into <tt>rack.showstatus.detail</tt> and
|
222
|
+
# will be shown as HTML. If such details exist, the error page is always
|
223
|
+
# rendered, even if the reply was not empty.
|
224
|
+
#
|
225
|
+
# * Rack::ConditionalGet
|
226
|
+
# Middleware that enables conditional GET using If-None-Match and
|
227
|
+
# If-Modified-Since. The application should set either or both of the
|
228
|
+
# Last-Modified or Etag response headers according to RFC 2616. When
|
229
|
+
# either of the conditions is met, the response body is set to be zero
|
230
|
+
# length and the response status is set to 304 Not Modified.
|
231
|
+
#
|
232
|
+
# * Rack::Head
|
233
|
+
# Removes the body of the response for HEAD requests.
|
234
|
+
#
|
235
|
+
# * Rack::Reloader
|
236
|
+
# Pure ruby source reloader, runs on every request with a configurable
|
237
|
+
# cooldown period.
|
238
|
+
#
|
239
|
+
# * Rack::Lint
|
240
|
+
# Rack::Lint validates your application and the requests and responses
|
241
|
+
# according to the Rack spec.
|
242
|
+
#
|
243
|
+
# Note that `m.innate` takes away most of the boring part and leaves it up to
|
244
|
+
# you to select your middleware in your application.
|
245
|
+
#
|
246
|
+
# `m.innate` expands to:
|
247
|
+
#
|
248
|
+
# use Rack::Cascade.new([
|
249
|
+
# Rack::File.new('public'),
|
250
|
+
# Innate::Current.new(
|
251
|
+
# Rack::Cascade.new([
|
252
|
+
# Innate::Rewrite.new(Innate::DynaMap),
|
253
|
+
# Innate::Route.new(Innate::DynaMap)])))
|
254
|
+
#
|
255
|
+
# @see Rack::MiddlewareCompiler
|
256
|
+
middleware :dev do |m|
|
257
|
+
m.apps(Rack::Lint, Rack::CommonLogger, Rack::ShowExceptions,
|
258
|
+
Rack::ShowStatus, Rack::ConditionalGet, Rack::ContentLength,
|
259
|
+
Rack::Head)
|
260
|
+
m.use(Rack::Reloader, 2)
|
261
|
+
m.innate
|
262
|
+
end
|
263
|
+
|
264
|
+
middleware :live do |m|
|
265
|
+
m.apps(Rack::CommonLogger, Rack::ShowStatus, Rack::ConditionalGet,
|
266
|
+
Rack::ContentLength, Rack::Head)
|
267
|
+
m.innate
|
268
|
+
end
|
269
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module Innate
|
2
|
+
ACTION_MEMBERS = [ :node, :instance, :method, :params, :method_value, :view,
|
3
|
+
:view_value, :layout, :wish, :options, :variables, :engine, :path ]
|
4
|
+
|
5
|
+
class Action < Struct.new(*ACTION_MEMBERS)
|
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
|
+
#
|
11
|
+
# @param [Hash, #to_hash] hash used to seed new Action instance
|
12
|
+
# @return [Action] action with the given defaults from hash
|
13
|
+
# @api stable
|
14
|
+
# @author manveru
|
15
|
+
def self.create(hash = {})
|
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
|
23
|
+
end
|
24
|
+
|
25
|
+
# Call the Action instance, will insert itself temporarily into
|
26
|
+
# Current.actions during the render operation so even in nested calls one
|
27
|
+
# can still access all other Action instances.
|
28
|
+
# Will initialize the assigned node and call Action#render
|
29
|
+
#
|
30
|
+
# @return [String] The rendition of all nested calls
|
31
|
+
# @see Action#render Node#action_found
|
32
|
+
# @api stable
|
33
|
+
# @author manveru
|
34
|
+
def call
|
35
|
+
Current.actions ? wrap_in_current{ render } : render
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Binding] binding of the instance for this Action
|
39
|
+
# @see Node#binding
|
40
|
+
# @api stable
|
41
|
+
# @author manveru
|
42
|
+
def binding
|
43
|
+
instance.binding
|
44
|
+
end
|
45
|
+
|
46
|
+
# Copy the instance variable names and values from given
|
47
|
+
# from_action#instance into the Action#variables of the action this method
|
48
|
+
# is called on.
|
49
|
+
#
|
50
|
+
# @param [Action #instance] from_action
|
51
|
+
# @return [Action] from_action
|
52
|
+
# @see Action#wrap_in_layout
|
53
|
+
# @api unstable
|
54
|
+
# @author manveru
|
55
|
+
def sync_variables(from_action)
|
56
|
+
instance = from_action.instance
|
57
|
+
|
58
|
+
instance.instance_variables.each{|iv|
|
59
|
+
iv_value = instance.instance_variable_get(iv)
|
60
|
+
iv_name = iv.to_s[1..-1]
|
61
|
+
self.variables[iv_name.to_sym] = iv_value
|
62
|
+
}
|
63
|
+
|
64
|
+
from_action
|
65
|
+
end
|
66
|
+
|
67
|
+
COPY_VARIABLES = '
|
68
|
+
STATE[:action_variables].each do |iv, value|
|
69
|
+
instance_variable_set("@#{iv}", value)
|
70
|
+
end'.strip.freeze
|
71
|
+
|
72
|
+
# Copy Action#variables as instance variables into the given binding.
|
73
|
+
#
|
74
|
+
# This relies on Innate::STATE, so should be thread-safe and doesn't depend
|
75
|
+
# on Innate::Current::actions order.
|
76
|
+
# So we avoid nasty business with Objectspace#_id2ref which may not work on
|
77
|
+
# all ruby implementations and seems to cause other problems as well.
|
78
|
+
#
|
79
|
+
# @param [Binding #eval] binding
|
80
|
+
# @return [NilClass] there is no indication of failure or success
|
81
|
+
# @see View::ERB::render
|
82
|
+
# @author manveru
|
83
|
+
def copy_variables(binding = self.binding)
|
84
|
+
return unless variables.any?
|
85
|
+
|
86
|
+
STATE.sync do
|
87
|
+
STATE[:action_variables] = self.variables
|
88
|
+
|
89
|
+
eval(COPY_VARIABLES, binding)
|
90
|
+
|
91
|
+
STATE[:action_variables] = nil
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def render
|
96
|
+
self.instance = node.new
|
97
|
+
self.variables[:content] ||= nil
|
98
|
+
|
99
|
+
instance.wrap_action_call(self) do
|
100
|
+
copy_variables # this might need another position after all
|
101
|
+
self.method_value = instance.__send__(method, *params) if method
|
102
|
+
self.view_value = ::File.read(view) if view
|
103
|
+
|
104
|
+
body, content_type = wrap_in_layout{
|
105
|
+
engine.call(self, view_value || method_value || '') }
|
106
|
+
options[:content_type] ||= content_type if content_type
|
107
|
+
body
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def wrap_in_layout
|
112
|
+
return yield unless layout
|
113
|
+
|
114
|
+
action = dup
|
115
|
+
action.view, action.method = layout_view_or_method(*layout)
|
116
|
+
action.layout = nil
|
117
|
+
action.view_value = nil
|
118
|
+
action.sync_variables(self)
|
119
|
+
body, content_type = yield
|
120
|
+
action.variables[:content] = body
|
121
|
+
return action.call, content_type
|
122
|
+
end
|
123
|
+
|
124
|
+
def layout_view_or_method(name, arg)
|
125
|
+
[:layout, :view].include?(name) ? [arg, nil] : [nil, arg]
|
126
|
+
end
|
127
|
+
|
128
|
+
def wrap_in_current
|
129
|
+
Current.actions << self
|
130
|
+
yield
|
131
|
+
ensure
|
132
|
+
Current.actions.delete(self)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Try to figure out a sane name for current action.
|
136
|
+
def name
|
137
|
+
File.basename((method || view).to_s).split('.').first
|
138
|
+
end
|
139
|
+
|
140
|
+
# Path to this action, including params, with the mapping of the current
|
141
|
+
# controller prepended.
|
142
|
+
def full_path
|
143
|
+
File.join(node.mapping, path)
|
144
|
+
end
|
145
|
+
|
146
|
+
def valid?
|
147
|
+
node.needs_method? ? (method && view) : (method || view)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|