innate 2009.04

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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