innate 2009.04

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. data/CHANGELOG +2981 -0
  2. data/COPYING +18 -0
  3. data/MANIFEST +127 -0
  4. data/README.md +563 -0
  5. data/Rakefile +35 -0
  6. data/example/app/retro_games.rb +60 -0
  7. data/example/app/todo/layout/default.xhtml +11 -0
  8. data/example/app/todo/spec/todo.rb +63 -0
  9. data/example/app/todo/start.rb +51 -0
  10. data/example/app/todo/view/index.xhtml +39 -0
  11. data/example/app/whywiki_erb/layout/wiki.html.erb +15 -0
  12. data/example/app/whywiki_erb/spec/wiki.rb +19 -0
  13. data/example/app/whywiki_erb/start.rb +42 -0
  14. data/example/app/whywiki_erb/view/edit.erb +6 -0
  15. data/example/app/whywiki_erb/view/index.erb +12 -0
  16. data/example/custom_middleware.rb +35 -0
  17. data/example/hello.rb +11 -0
  18. data/example/howto_spec.rb +35 -0
  19. data/example/link.rb +27 -0
  20. data/example/provides.rb +31 -0
  21. data/example/session.rb +38 -0
  22. data/innate.gemspec +29 -0
  23. data/lib/innate.rb +269 -0
  24. data/lib/innate/action.rb +150 -0
  25. data/lib/innate/adapter.rb +76 -0
  26. data/lib/innate/cache.rb +134 -0
  27. data/lib/innate/cache/api.rb +128 -0
  28. data/lib/innate/cache/drb.rb +58 -0
  29. data/lib/innate/cache/file_based.rb +41 -0
  30. data/lib/innate/cache/marshal.rb +17 -0
  31. data/lib/innate/cache/memory.rb +22 -0
  32. data/lib/innate/cache/yaml.rb +17 -0
  33. data/lib/innate/current.rb +37 -0
  34. data/lib/innate/dynamap.rb +96 -0
  35. data/lib/innate/helper.rb +183 -0
  36. data/lib/innate/helper/aspect.rb +124 -0
  37. data/lib/innate/helper/cgi.rb +54 -0
  38. data/lib/innate/helper/flash.rb +36 -0
  39. data/lib/innate/helper/link.rb +94 -0
  40. data/lib/innate/helper/redirect.rb +85 -0
  41. data/lib/innate/helper/render.rb +87 -0
  42. data/lib/innate/helper/send_file.rb +26 -0
  43. data/lib/innate/log.rb +20 -0
  44. data/lib/innate/log/color_formatter.rb +43 -0
  45. data/lib/innate/log/hub.rb +73 -0
  46. data/lib/innate/middleware_compiler.rb +65 -0
  47. data/lib/innate/mock.rb +49 -0
  48. data/lib/innate/node.rb +1025 -0
  49. data/lib/innate/options.rb +37 -0
  50. data/lib/innate/options/dsl.rb +202 -0
  51. data/lib/innate/options/stub.rb +7 -0
  52. data/lib/innate/request.rb +141 -0
  53. data/lib/innate/response.rb +23 -0
  54. data/lib/innate/route.rb +110 -0
  55. data/lib/innate/session.rb +121 -0
  56. data/lib/innate/session/flash.rb +94 -0
  57. data/lib/innate/spec.rb +23 -0
  58. data/lib/innate/state.rb +27 -0
  59. data/lib/innate/state/accessor.rb +130 -0
  60. data/lib/innate/state/fiber.rb +74 -0
  61. data/lib/innate/state/thread.rb +47 -0
  62. data/lib/innate/traited.rb +85 -0
  63. data/lib/innate/trinity.rb +18 -0
  64. data/lib/innate/version.rb +3 -0
  65. data/lib/innate/view.rb +60 -0
  66. data/lib/innate/view/erb.rb +15 -0
  67. data/lib/innate/view/etanni.rb +36 -0
  68. data/lib/innate/view/none.rb +9 -0
  69. data/spec/example/app/retro_games.rb +30 -0
  70. data/spec/example/hello.rb +13 -0
  71. data/spec/example/link.rb +25 -0
  72. data/spec/example/provides.rb +16 -0
  73. data/spec/example/session.rb +22 -0
  74. data/spec/helper.rb +10 -0
  75. data/spec/innate/action/layout.rb +107 -0
  76. data/spec/innate/action/layout/file_layout.xhtml +1 -0
  77. data/spec/innate/cache/common.rb +47 -0
  78. data/spec/innate/cache/marshal.rb +5 -0
  79. data/spec/innate/cache/memory.rb +5 -0
  80. data/spec/innate/cache/yaml.rb +5 -0
  81. data/spec/innate/dynamap.rb +22 -0
  82. data/spec/innate/helper.rb +86 -0
  83. data/spec/innate/helper/aspect.rb +75 -0
  84. data/spec/innate/helper/cgi.rb +37 -0
  85. data/spec/innate/helper/flash.rb +118 -0
  86. data/spec/innate/helper/link.rb +139 -0
  87. data/spec/innate/helper/redirect.rb +160 -0
  88. data/spec/innate/helper/render.rb +133 -0
  89. data/spec/innate/helper/send_file.rb +21 -0
  90. data/spec/innate/helper/view/aspect_hello.xhtml +1 -0
  91. data/spec/innate/helper/view/locals.xhtml +1 -0
  92. data/spec/innate/helper/view/loop.xhtml +4 -0
  93. data/spec/innate/helper/view/num.xhtml +1 -0
  94. data/spec/innate/helper/view/partial.xhtml +1 -0
  95. data/spec/innate/helper/view/recursive.xhtml +7 -0
  96. data/spec/innate/mock.rb +84 -0
  97. data/spec/innate/node/mapping.rb +37 -0
  98. data/spec/innate/node/node.rb +134 -0
  99. data/spec/innate/node/resolve.rb +82 -0
  100. data/spec/innate/node/view/another_layout/another_layout.xhtml +3 -0
  101. data/spec/innate/node/view/bar.xhtml +1 -0
  102. data/spec/innate/node/view/foo.html.xhtml +1 -0
  103. data/spec/innate/node/view/only_view.xhtml +1 -0
  104. data/spec/innate/node/view/with_layout.xhtml +1 -0
  105. data/spec/innate/node/wrap_action_call.rb +83 -0
  106. data/spec/innate/options.rb +115 -0
  107. data/spec/innate/parameter.rb +154 -0
  108. data/spec/innate/provides.rb +99 -0
  109. data/spec/innate/provides/list.html.xhtml +1 -0
  110. data/spec/innate/provides/list.txt.xhtml +1 -0
  111. data/spec/innate/request.rb +77 -0
  112. data/spec/innate/route.rb +135 -0
  113. data/spec/innate/session.rb +54 -0
  114. data/spec/innate/state/fiber.rb +58 -0
  115. data/spec/innate/state/thread.rb +40 -0
  116. data/spec/innate/traited.rb +55 -0
  117. data/tasks/bacon.rake +66 -0
  118. data/tasks/changelog.rake +18 -0
  119. data/tasks/gem.rake +22 -0
  120. data/tasks/gem_installer.rake +76 -0
  121. data/tasks/grancher.rake +12 -0
  122. data/tasks/install_dependencies.rake +4 -0
  123. data/tasks/manifest.rake +4 -0
  124. data/tasks/rcov.rake +19 -0
  125. data/tasks/release.rake +51 -0
  126. data/tasks/reversion.rake +8 -0
  127. data/tasks/setup.rake +28 -0
  128. metadata +181 -0
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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