rjspotter-innate 2009.06.29

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