pancake 0.1.8

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 (172) hide show
  1. data/LICENSE +20 -0
  2. data/README.textile +95 -0
  3. data/Rakefile +56 -0
  4. data/TODO +17 -0
  5. data/bin/jeweler +19 -0
  6. data/bin/pancake-gen +17 -0
  7. data/bin/rubyforge +19 -0
  8. data/lib/pancake/bootloaders.rb +180 -0
  9. data/lib/pancake/configuration.rb +145 -0
  10. data/lib/pancake/constants.rb +5 -0
  11. data/lib/pancake/core_ext/class.rb +44 -0
  12. data/lib/pancake/core_ext/object.rb +22 -0
  13. data/lib/pancake/core_ext/symbol.rb +15 -0
  14. data/lib/pancake/defaults/configuration.rb +22 -0
  15. data/lib/pancake/defaults/middlewares.rb +1 -0
  16. data/lib/pancake/errors.rb +61 -0
  17. data/lib/pancake/generators/base.rb +12 -0
  18. data/lib/pancake/generators/micro_generator.rb +17 -0
  19. data/lib/pancake/generators/short_generator.rb +17 -0
  20. data/lib/pancake/generators/stack_generator.rb +17 -0
  21. data/lib/pancake/generators/templates/common/dotgitignore +22 -0
  22. data/lib/pancake/generators/templates/common/dothtaccess +17 -0
  23. data/lib/pancake/generators/templates/micro/%stack_name%/%stack_name%.rb.tt +8 -0
  24. data/lib/pancake/generators/templates/micro/%stack_name%/config.ru.tt +12 -0
  25. data/lib/pancake/generators/templates/micro/%stack_name%/pancake.init.tt +1 -0
  26. data/lib/pancake/generators/templates/micro/%stack_name%/public/.empty_directory +0 -0
  27. data/lib/pancake/generators/templates/micro/%stack_name%/tmp/.empty_directory +0 -0
  28. data/lib/pancake/generators/templates/micro/%stack_name%/views/root.html.haml +1 -0
  29. data/lib/pancake/generators/templates/short/%stack_name%/LICENSE.tt +20 -0
  30. data/lib/pancake/generators/templates/short/%stack_name%/README.tt +7 -0
  31. data/lib/pancake/generators/templates/short/%stack_name%/Rakefile.tt +50 -0
  32. data/lib/pancake/generators/templates/short/%stack_name%/VERSION.tt +1 -0
  33. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/%stack_name%.rb.tt +6 -0
  34. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config.ru.tt +10 -0
  35. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/mounts/.empty_directory +0 -0
  36. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/public/.empty_directory +0 -0
  37. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/tmp/.empty_directory +0 -0
  38. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/views/root.html.haml +2 -0
  39. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%.rb.tt +5 -0
  40. data/lib/pancake/generators/templates/short/%stack_name%/pancake.init.tt +1 -0
  41. data/lib/pancake/generators/templates/short/%stack_name%/spec/%stack_name%_spec.rb.tt +7 -0
  42. data/lib/pancake/generators/templates/short/%stack_name%/spec/spec_helper.rb.tt +9 -0
  43. data/lib/pancake/generators/templates/stack/%stack_name%/LICENSE.tt +20 -0
  44. data/lib/pancake/generators/templates/stack/%stack_name%/README.tt +7 -0
  45. data/lib/pancake/generators/templates/stack/%stack_name%/Rakefile.tt +50 -0
  46. data/lib/pancake/generators/templates/stack/%stack_name%/VERSION.tt +1 -0
  47. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/config/environments/development.rb.tt +18 -0
  48. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/config/environments/production.rb.tt +18 -0
  49. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/config/router.rb.tt +6 -0
  50. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/config.ru.tt +12 -0
  51. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/gems/cache/.empty_directory +0 -0
  52. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/mounts/.empty_directory +0 -0
  53. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/public/.empty_directory +0 -0
  54. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/tmp/.empty_directory +0 -0
  55. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%.rb.tt +3 -0
  56. data/lib/pancake/generators/templates/stack/%stack_name%/pancake.init.tt +1 -0
  57. data/lib/pancake/generators/templates/stack/%stack_name%/spec/%stack_name%_spec.rb.tt +7 -0
  58. data/lib/pancake/generators/templates/stack/%stack_name%/spec/spec_helper.rb.tt +9 -0
  59. data/lib/pancake/generators.rb +8 -0
  60. data/lib/pancake/hooks/inheritable_inner_classes.rb +60 -0
  61. data/lib/pancake/hooks/on_inherit.rb +34 -0
  62. data/lib/pancake/logger.rb +200 -0
  63. data/lib/pancake/master.rb +123 -0
  64. data/lib/pancake/middleware.rb +347 -0
  65. data/lib/pancake/middlewares/logger.rb +16 -0
  66. data/lib/pancake/middlewares/static.rb +38 -0
  67. data/lib/pancake/mime_types.rb +265 -0
  68. data/lib/pancake/mixins/publish/action_options.rb +104 -0
  69. data/lib/pancake/mixins/publish.rb +125 -0
  70. data/lib/pancake/mixins/render/render.rb +168 -0
  71. data/lib/pancake/mixins/render/template.rb +23 -0
  72. data/lib/pancake/mixins/render/view_context.rb +21 -0
  73. data/lib/pancake/mixins/render.rb +109 -0
  74. data/lib/pancake/mixins/request_helper.rb +100 -0
  75. data/lib/pancake/mixins/stack_helper.rb +46 -0
  76. data/lib/pancake/mixins/url.rb +10 -0
  77. data/lib/pancake/paths.rb +218 -0
  78. data/lib/pancake/router.rb +99 -0
  79. data/lib/pancake/stack/app.rb +10 -0
  80. data/lib/pancake/stack/bootloader.rb +79 -0
  81. data/lib/pancake/stack/configuration.rb +44 -0
  82. data/lib/pancake/stack/middleware.rb +0 -0
  83. data/lib/pancake/stack/router.rb +21 -0
  84. data/lib/pancake/stack/stack.rb +66 -0
  85. data/lib/pancake/stacks/short/bootloaders.rb +13 -0
  86. data/lib/pancake/stacks/short/controller.rb +116 -0
  87. data/lib/pancake/stacks/short/default/views/base.html.haml +5 -0
  88. data/lib/pancake/stacks/short/stack.rb +187 -0
  89. data/lib/pancake/stacks/short.rb +3 -0
  90. data/lib/pancake.rb +58 -0
  91. data/spec/helpers/helpers.rb +20 -0
  92. data/spec/helpers/matchers.rb +25 -0
  93. data/spec/pancake/bootloaders_spec.rb +109 -0
  94. data/spec/pancake/configuration_spec.rb +177 -0
  95. data/spec/pancake/constants_spec.rb +7 -0
  96. data/spec/pancake/defaults/configuration_spec.rb +58 -0
  97. data/spec/pancake/fixtures/foo_stack/pancake.init +0 -0
  98. data/spec/pancake/fixtures/middlewares/other_public/two.html +1 -0
  99. data/spec/pancake/fixtures/middlewares/public/foo#bar.html +1 -0
  100. data/spec/pancake/fixtures/middlewares/public/one.html +1 -0
  101. data/spec/pancake/fixtures/paths/controllers/controller1.rb +0 -0
  102. data/spec/pancake/fixtures/paths/controllers/controller2.rb +0 -0
  103. data/spec/pancake/fixtures/paths/controllers/controller3.rb +0 -0
  104. data/spec/pancake/fixtures/paths/models/model1.rb +0 -0
  105. data/spec/pancake/fixtures/paths/models/model2.rb +0 -0
  106. data/spec/pancake/fixtures/paths/models/model3.rb +0 -0
  107. data/spec/pancake/fixtures/paths/stack/controllers/controller1.rb +0 -0
  108. data/spec/pancake/fixtures/paths/stack/models/model3.rb +0 -0
  109. data/spec/pancake/fixtures/paths/stack/views/view1.erb +0 -0
  110. data/spec/pancake/fixtures/paths/stack/views/view1.rb +0 -0
  111. data/spec/pancake/fixtures/paths/stack/views/view2.erb +0 -0
  112. data/spec/pancake/fixtures/paths/stack/views/view2.haml +0 -0
  113. data/spec/pancake/fixtures/render_templates/context_template.html.erb +2 -0
  114. data/spec/pancake/fixtures/render_templates/erb_template.html.erb +1 -0
  115. data/spec/pancake/fixtures/render_templates/erb_template.json.erb +1 -0
  116. data/spec/pancake/fixtures/render_templates/haml_template.html.haml +1 -0
  117. data/spec/pancake/fixtures/render_templates/haml_template.xml.haml +1 -0
  118. data/spec/pancake/fixtures/render_templates/templates/context.erb +2 -0
  119. data/spec/pancake/fixtures/render_templates/view_context/capture_erb.erb +5 -0
  120. data/spec/pancake/fixtures/render_templates/view_context/capture_haml.haml +4 -0
  121. data/spec/pancake/fixtures/render_templates/view_context/concat_erb.erb +2 -0
  122. data/spec/pancake/fixtures/render_templates/view_context/concat_haml.haml +2 -0
  123. data/spec/pancake/fixtures/render_templates/view_context/context.erb +3 -0
  124. data/spec/pancake/fixtures/render_templates/view_context/context2.erb +3 -0
  125. data/spec/pancake/fixtures/render_templates/view_context/helper_methods.erb +3 -0
  126. data/spec/pancake/fixtures/render_templates/view_context/inherited_erb_from_haml.erb +5 -0
  127. data/spec/pancake/fixtures/render_templates/view_context/inherited_erb_level_0.erb +5 -0
  128. data/spec/pancake/fixtures/render_templates/view_context/inherited_erb_level_1.erb +5 -0
  129. data/spec/pancake/fixtures/render_templates/view_context/inherited_haml_from_erb.haml +4 -0
  130. data/spec/pancake/fixtures/render_templates/view_context/inherited_haml_level_0.haml +4 -0
  131. data/spec/pancake/fixtures/render_templates/view_context/inherited_haml_level_1.haml +4 -0
  132. data/spec/pancake/fixtures/render_templates/view_context/nested_content_level_0.haml +6 -0
  133. data/spec/pancake/fixtures/render_templates/view_context/nested_content_level_1.haml +4 -0
  134. data/spec/pancake/fixtures/render_templates/view_context/nested_inner.erb +1 -0
  135. data/spec/pancake/fixtures/render_templates/view_context/nested_outer.erb +3 -0
  136. data/spec/pancake/fixtures/render_templates/view_context/super_erb_from_erb_0.erb +5 -0
  137. data/spec/pancake/fixtures/render_templates/view_context/super_erb_from_erb_1.erb +6 -0
  138. data/spec/pancake/fixtures/render_templates/view_context/super_erb_from_haml_0.erb +5 -0
  139. data/spec/pancake/fixtures/render_templates/view_context/super_erb_from_haml_1.erb +6 -0
  140. data/spec/pancake/fixtures/render_templates/view_context/super_haml_from_erb_0.haml +4 -0
  141. data/spec/pancake/fixtures/render_templates/view_context/super_haml_from_erb_1.haml +5 -0
  142. data/spec/pancake/fixtures/render_templates/view_context/super_haml_from_haml_0.haml +5 -0
  143. data/spec/pancake/fixtures/render_templates/view_context/super_haml_from_haml_1.haml +5 -0
  144. data/spec/pancake/fixtures/stacks/short/foobar/other_root/views/base.html.haml +4 -0
  145. data/spec/pancake/fixtures/stacks/short/foobar/views/basic.html.haml +1 -0
  146. data/spec/pancake/fixtures/stacks/short/foobar/views/inherited_from_base.html.haml +5 -0
  147. data/spec/pancake/hooks/on_inherit_spec.rb +65 -0
  148. data/spec/pancake/inheritance_spec.rb +100 -0
  149. data/spec/pancake/middleware_spec.rb +401 -0
  150. data/spec/pancake/middlewares/logger_spec.rb +29 -0
  151. data/spec/pancake/middlewares/static_spec.rb +83 -0
  152. data/spec/pancake/mime_types_spec.rb +234 -0
  153. data/spec/pancake/mixins/publish_spec.rb +94 -0
  154. data/spec/pancake/mixins/render/template_spec.rb +69 -0
  155. data/spec/pancake/mixins/render/view_context_spec.rb +248 -0
  156. data/spec/pancake/mixins/render_spec.rb +56 -0
  157. data/spec/pancake/mixins/request_helper_spec.rb +27 -0
  158. data/spec/pancake/mixins/stack_helper_spec.rb +46 -0
  159. data/spec/pancake/pancake_spec.rb +90 -0
  160. data/spec/pancake/paths_spec.rb +210 -0
  161. data/spec/pancake/stack/app_spec.rb +28 -0
  162. data/spec/pancake/stack/bootloader_spec.rb +41 -0
  163. data/spec/pancake/stack/middleware_spec.rb +0 -0
  164. data/spec/pancake/stack/router_spec.rb +282 -0
  165. data/spec/pancake/stack/stack_configuration_spec.rb +101 -0
  166. data/spec/pancake/stack/stack_spec.rb +60 -0
  167. data/spec/pancake/stacks/short/controller_spec.rb +322 -0
  168. data/spec/pancake/stacks/short/middlewares_spec.rb +22 -0
  169. data/spec/pancake/stacks/short/router_spec.rb +136 -0
  170. data/spec/pancake/stacks/short/stack_spec.rb +64 -0
  171. data/spec/spec_helper.rb +23 -0
  172. metadata +294 -0
@@ -0,0 +1,200 @@
1
+ # Pancake::Logger == Merb::Logger
2
+ class Pancake::Logger < Extlib::Logger
3
+ # :api: public
4
+ def verbose!(message, level = :warn)
5
+ send("#{level}!", message) if Pancake.configuration.verbose_logging
6
+ end
7
+
8
+ # :api: public
9
+ def verbose(message, level = :warn)
10
+ send(level, message) if Pancake.configuration.verbose_logging
11
+ end
12
+ end
13
+
14
+ # require "time" # httpdate
15
+ # ==== Public Pancake Logger API
16
+ #
17
+ # To replace an existing logger with a new one:
18
+ # Pancake::Logger.set_log(log{String, IO},level{Symbol, String})
19
+ #
20
+ # Available logging levels are
21
+ # Pancake::Logger::{ Fatal, Error, Warn, Info, Debug }
22
+ #
23
+ # Logging via:
24
+ # Pancake.logger.fatal(message<String>,&block)
25
+ # Pancake.logger.error(message<String>,&block)
26
+ # Pancake.logger.warn(message<String>,&block)
27
+ # Pancake.logger.info(message<String>,&block)
28
+ # Pancake.logger.debug(message<String>,&block)
29
+ #
30
+ # Logging with autoflush:
31
+ # Pancake.logger.fatal!(message<String>,&block)
32
+ # Pancake.logger.error!(message<String>,&block)
33
+ # Pancake.logger.warn!(message<String>,&block)
34
+ # Pancake.logger.info!(message<String>,&block)
35
+ # Pancake.logger.debug!(message<String>,&block)
36
+ #
37
+ # Flush the buffer to
38
+ # Pancake.logger.flush
39
+ #
40
+ # Remove the current log object
41
+ # Pancake.logger.close
42
+ #
43
+ # ==== Private Pancake Logger API
44
+ #
45
+ # To initialize the logger you create a new object, proxies to set_log.
46
+ # Pancake::Logger.new(log{String, IO},level{Symbol, String})
47
+ module Pancake
48
+
49
+ class Logger
50
+
51
+ attr_accessor :level
52
+ attr_accessor :delimiter
53
+ attr_accessor :auto_flush
54
+ attr_reader :buffer
55
+ attr_reader :log
56
+ attr_reader :init_args
57
+
58
+ # ==== Notes
59
+ # Ruby (standard) logger levels:
60
+ # :fatal:: An unhandleable error that results in a program crash
61
+ # :error:: A handleable error condition
62
+ # :warn:: A warning
63
+ # :info:: generic (useful) information about system operation
64
+ # :debug:: low-level information for developers
65
+ Levels = Mash.new({
66
+ :fatal => 7,
67
+ :error => 6,
68
+ :warn => 4,
69
+ :info => 3,
70
+ :debug => 0
71
+ }) unless const_defined?(:Levels)
72
+
73
+ @@mutex = {}
74
+
75
+ public
76
+
77
+ # To initialize the logger you create a new object, proxies to set_log.
78
+ #
79
+ # ==== Parameters
80
+ # *args:: Arguments to create the log from. See set_logs for specifics.
81
+ def initialize(*args)
82
+ set_log(*args)
83
+ end
84
+
85
+ # Replaces an existing logger with a new one.
86
+ #
87
+ # ==== Parameters
88
+ # log<IO, String>:: Either an IO object or a name of a logfile.
89
+ # log_level<~to_sym>::
90
+ # The log level from, e.g. :fatal or :info. Defaults to :error in the
91
+ # production environment and :debug otherwise.
92
+ # delimiter<String>::
93
+ # Delimiter to use between message sections. Defaults to " ~ ".
94
+ # auto_flush<Boolean>::
95
+ # Whether the log should automatically flush after new messages are
96
+ # added. Defaults to false.
97
+ def set_log(stream = Pancake.configuration.log_stream,
98
+ log_level = Pancake.configuration.log_level,
99
+ delimiter = Pancake.configuration.log_delimiter,
100
+ auto_flush = Pancake.configuration.log_auto_flush)
101
+
102
+ @buffer = []
103
+ @delimiter = delimiter
104
+ @auto_flush = auto_flush
105
+
106
+ if Levels[log_level]
107
+ @level = Levels[log_level]
108
+ else
109
+ @level = log_level
110
+ end
111
+
112
+ @log = stream
113
+ @log.sync = true
114
+ @mutex = (@@mutex[@log] ||= Mutex.new)
115
+ end
116
+
117
+ # Flush the entire buffer to the log object.
118
+ def flush
119
+ return unless @buffer.size > 0
120
+ @mutex.synchronize do
121
+ @log.write(@buffer.slice!(0..-1).join(''))
122
+ end
123
+ end
124
+
125
+ # Close and remove the current log object.
126
+ def close
127
+ flush
128
+ @log.close if @log.respond_to?(:close) && !@log.tty?
129
+ @log = nil
130
+ end
131
+
132
+ # Appends a message to the log. The methods yield to an optional block and
133
+ # the output of this block will be appended to the message.
134
+ #
135
+ # ==== Parameters
136
+ # string<String>:: The message to be logged. Defaults to nil.
137
+ #
138
+ # ==== Returns
139
+ # String:: The resulting message added to the log file.
140
+ def <<(string = nil)
141
+ message = ""
142
+ message << delimiter
143
+ message << string if string
144
+ message << "\n" unless message[-1] == ?\n
145
+ @buffer << message
146
+ flush if @auto_flush
147
+
148
+ message
149
+ end
150
+ alias :push :<<
151
+
152
+ # Generate the logging methods for Pancake.logger for each log level.
153
+ Levels.each_pair do |name, number|
154
+ class_eval <<-LEVELMETHODS, __FILE__, __LINE__
155
+
156
+ # Appends a message to the log if the log level is at least as high as
157
+ # the log level of the logger.
158
+ #
159
+ # ==== Parameters
160
+ # string<String>:: The message to be logged. Defaults to nil.
161
+ #
162
+ # ==== Returns
163
+ # self:: The logger object for chaining.
164
+ def #{name}(message = nil)
165
+ if #{number} >= level
166
+ message = block_given? ? yield : message
167
+ self << message if #{number} >= level
168
+ end
169
+ self
170
+ end
171
+
172
+ # Appends a message to the log if the log level is at least as high as
173
+ # the log level of the logger. The bang! version of the method also auto
174
+ # flushes the log buffer to disk.
175
+ #
176
+ # ==== Parameters
177
+ # string<String>:: The message to be logged. Defaults to nil.
178
+ #
179
+ # ==== Returns
180
+ # self:: The logger object for chaining.
181
+ def #{name}!(message = nil)
182
+ if #{number} >= level
183
+ message = block_given? ? yield : message
184
+ self << message if #{number} >= level
185
+ flush if #{number} >= level
186
+ end
187
+ self
188
+ end
189
+
190
+ # ==== Returns
191
+ # Boolean:: True if this level will be logged by this logger.
192
+ def #{name}?
193
+ #{number} >= level
194
+ end
195
+ LEVELMETHODS
196
+ end
197
+
198
+ end
199
+
200
+ end
@@ -0,0 +1,123 @@
1
+ module Pancake
2
+ # A simple rack application
3
+ OK_APP = lambda{|env| Rack::Response.new("OK", 200, {"Content-Type" => "text/plain"}).finish}
4
+ MISSING_APP = lambda{|env| Rack::Response.new("NOT FOUND", 404, {"Content-Type" => "text/plain"}).finish}
5
+
6
+ extend Middleware
7
+
8
+ class << self
9
+ attr_accessor :root
10
+
11
+ # Start Pancake. This provides a full pancake stack to use inside a rack application
12
+ #
13
+ # @param [Hash] opts
14
+ # @option opts [String] :root The root of the pancake stack
15
+ #
16
+ # @example Starting a pancake stack
17
+ # Pancake.start(:root => "/path/to/root"){ MyApp # App to use}
18
+ #
19
+ # @api public
20
+ # @author Daniel Neighman
21
+ def start(opts, &block)
22
+ raise "You must specify a root directory for pancake" unless opts[:root]
23
+ self.root = opts[:root]
24
+
25
+ # Build Pancake
26
+ the_app = instance_eval(&block)
27
+ Pancake::Middleware.build(the_app, middlewares)
28
+ end
29
+
30
+ # Provides the environment for the currently running pancake
31
+ #
32
+ # @return [String] The currently running environment
33
+ # @api public
34
+ # @author Daniel Neighman
35
+ def env
36
+ ENV['RACK_ENV'] ||= "development"
37
+ end
38
+
39
+ # A helper method to get the expanded directory name of a __FILE__
40
+ #
41
+ # @return [String] an expanded version of file
42
+ # @api public
43
+ # @author Daniel Neighman
44
+ def get_root(file, *args)
45
+ File.expand_path(File.join(File.dirname(file), *args))
46
+ end
47
+
48
+ # Labels that specify what kind of stack you're intending on loading.
49
+ # This is a simliar concept to environments but it is in fact seperate conceptually.
50
+ #
51
+ # The reasoning is that you may want to use a particular stack type or types.
52
+ # By using stack labels, you can define middleware to be active.
53
+ #
54
+ # @example
55
+ # Pancake.stack_labels == [:development, :demo]
56
+ #
57
+ # # This would activate middleware marked with :development or :demo or the implicit :any label
58
+ #
59
+ # @return [Array<Symbol>]
60
+ # An array of labels to activate
61
+ # The default is [:production]
62
+ # @see Pancake.stack_labels= to set the labels for this stack
63
+ # @see Pancake::Middleware#stack to see how to specify middleware to be active for the given labels
64
+ # @api public
65
+ # @author Daniel Neighman
66
+ def stack_labels
67
+ return @stack_labels unless @stack_labels.nil? || @stack_labels.empty?
68
+ self.stack_labels = [:production]
69
+ end
70
+
71
+ # Sets the stack labels to activate the associated middleware
72
+ #
73
+ # @param [Array<Symbol>, Symbol] An array of labels or a single label, specifying the middlewares to activate
74
+ #
75
+ # @example
76
+ # Pancake.stack_labels = [:demo, :production]
77
+ #
78
+ # @see Pancake.stack_labels
79
+ # @see Pancake::Middleware#stack
80
+ # @api public
81
+ # @author Daniel Neighman
82
+ def stack_labels=(*labels)
83
+ @stack_labels = labels.flatten.compact
84
+ end
85
+
86
+ def handle_errors!(*args)
87
+ @handle_errors = begin
88
+ if args.size > 1
89
+ args.flatten
90
+ else
91
+ args.first
92
+ end
93
+ end
94
+ end
95
+
96
+ def handle_errors?
97
+ if @handle_errors.nil?
98
+ !(Pancake.env == "development")
99
+ else
100
+ case @handle_errors
101
+ when Array
102
+ @handle_errors.include?(Pancake.env)
103
+ when TrueClass, FalseClass
104
+ @handle_errors
105
+ when String
106
+ Pancake.env == @handle_errors
107
+ end
108
+ end
109
+ end
110
+
111
+ def default_error_handling!
112
+ @handle_errors = nil
113
+ end
114
+
115
+ def logger
116
+ @logger ||= Pancake::Logger.new
117
+ end
118
+
119
+ def logger=(logr)
120
+ @logger = logr
121
+ end
122
+ end # self
123
+ end # Pancake
@@ -0,0 +1,347 @@
1
+ module Pancake
2
+ # Provides a mixin to use on any class to give it middleware management capabilities.
3
+ # This module provides a rich featureset for defining a middleware stack.
4
+ #
5
+ # Middlware can be set before, or after other middleware, can be tagged / named,
6
+ # and can be declared to only be active in certain types of stacks.
7
+ module Middleware
8
+
9
+ # When extending a base class with the Pancake::Middleware,
10
+ # an inner class StackMiddleware is setup on the base class.
11
+ # This inner class is where all the inforamation is stored on the stack to be defined
12
+ # The inner StackMiddleware class is also set to be inherited
13
+ # with the base class (and all children classes)
14
+ # So that each class gets its own copy and may maintain the base
15
+ # stack from the parent, but edit it in the child.
16
+ def self.extended(base)
17
+ base.class_eval <<-RUBY
18
+ class StackMiddleware < Pancake::Middleware::StackMiddleware; end
19
+ RUBY
20
+ if base.is_a?(Class)
21
+ base.inheritable_inner_classes :StackMiddleware
22
+ end
23
+ super
24
+ end # self.extended
25
+
26
+ # Build a middleware stack given an application and some middleware classes
27
+ #
28
+ # @param [Object] app a rack application to wrap in the middlware list
29
+ # @param [Array<StackMiddleware>] mwares an array of
30
+ # StackMiddleware instances where each instance
31
+ # defines a middleware to use in constructing the stack
32
+ #
33
+ # @example
34
+ # Pancake::Middleware.build(@app, [MWare_1, MWare_2])
35
+ # @return [Object]
36
+ # An application instance of the first middleware defined in the array
37
+ # The application should be an instance that conforms to Rack specifications
38
+ #
39
+ # @api public
40
+ # @since 0.1.0
41
+ # @author Daniel Neighman
42
+ def self.build(app, mwares)
43
+ mwares.reverse.inject(app) do |a, m|
44
+ m.middleware.new(a, *m.args, &m.block)
45
+ end
46
+ end
47
+
48
+ # @param [Array<Symbol>] labels An array of labels specifying the stack labels to use to build the middlware list
49
+ #
50
+ # @example
51
+ # MyApp.middlewares(:production) # provides all middlewares matching the :production label, or the implicit :any label
52
+ # MyApp.middlewares(:development, :demo) # provides all middlewares matching the :development or :demo or implicit :any label
53
+ #
54
+ # @return [Array<StackMiddleware>]
55
+ # An array of middleware specifications in the order they should be used to wrap the application
56
+ #
57
+ # @see Pancake::Middleware::StackMiddleware
58
+ # @see Pancake.stack_labels for a decription of stack_labels
59
+ # @api public
60
+ # @since 0.1.0
61
+ # @author Daniel Neighman
62
+ def middlewares(*labels)
63
+ labels = labels.flatten
64
+ self::StackMiddleware.middlewares(*labels)
65
+ end
66
+
67
+ # Useful for adding additional information into your middleware stack definition
68
+ #
69
+ # @param [Object] name
70
+ # The name of a given middleware. Each piece of middleware has a name in the stack.
71
+ # By naming middleware we can refer to it later, swap it out for a different class or even just remove it from the stack.
72
+ # @param [Hash] opts An options hash
73
+ # @option opts [Array<Symbol>] :labels ([:any])
74
+ # An array of symbols, or a straight symbol that defines what stacks this middleware sould be active in
75
+ # @option opts [Object] :before
76
+ # Sets this middlware to be run after the middleware named. Name is either the name given to the
77
+ # middleware stack, or the Middleware class itself.
78
+ # @option opts [Object] :after
79
+ # Sets this middleware to be run after the middleware name. Name is either the name given to the
80
+ # middleware stack or the Middleware class itself.
81
+ #
82
+ # @example Declaring un-named middleware via the stack
83
+ # MyClass.stack.use(MyMiddleware)
84
+ #
85
+ # This middleware will be named MyMiddleware, and can be specified with (:before | :after) => MyMiddleware
86
+ #
87
+ # @example Declaring a named middleware via the stack
88
+ # MyClass.stack(:foo).use(MyMiddleware)
89
+ #
90
+ # This middleware will be named :foo and can be specified with (:before | :after) => :foo
91
+ #
92
+ # @example Declaring a named middleware with a :before key
93
+ # MyClass.stack(:foo, :before => :bar).use(MyMiddleware)
94
+ #
95
+ # This middleware will be named :foo and will be run before the middleware named :bar
96
+ # If :bar is not run, :foo will not be run either
97
+ #
98
+ # @example Declaring a named middlware with an :after key
99
+ # MyClass.stack(:foo, :after => :bar).use(MyMiddleware)
100
+ #
101
+ # This middleware will be named :foo and will be run after the middleware named :bar
102
+ # If :bar is not run, :foo will not be run either
103
+ #
104
+ # @example Declaring a named middleware with some labels
105
+ # MyClass.stack(:foo, :lables => [:demo, :production, :staging]).use(MyMiddleware)
106
+ #
107
+ # This middleware will only be run when pancake is set with the :demo, :production or :staging labels
108
+ #
109
+ # @example A full example
110
+ # MyClass.stack(:foo, :labels => [:staging, :development], :after => :session).use(MyMiddleware)
111
+ #
112
+ #
113
+ # @see Pancake::Middleware#use
114
+ # @api public
115
+ # @since 0.1.0
116
+ # @author Daniel Neighman
117
+ def stack(name = nil, opts = {})
118
+ if self::StackMiddleware._mwares[name] && mw = self::StackMiddleware._mwares[name]
119
+ unless mw.stack == self
120
+ mw = self::StackMiddleware._mwares[name] = self::StackMiddleware._mwares[name].dup
121
+ end
122
+ mw
123
+ else
124
+ self::StackMiddleware.new(name, self, opts)
125
+ end
126
+ end
127
+
128
+ # Adds middleware to the current stack definition
129
+ #
130
+ # @param [Class] middleware The middleware class to use in the stack
131
+ # @param [Hash] opts An options hash that is passed through to the middleware when it is instantiated
132
+ #
133
+ # @yield The block is provided to the middlewares #new method when it is initialized
134
+ #
135
+ # @example Bare use call
136
+ # MyApp.use(MyMiddleware, :some => :option){ # middleware initialization block here }
137
+ #
138
+ # @example Use call after a stack call
139
+ # MyApp.stack(:foo).use(MyMiddleware, :some => :option){ # middleware initialization block here }
140
+ #
141
+ # @see Pancake::Middleware#stack
142
+ # @api public
143
+ # @since 0.1.0
144
+ # @author Daniel Neighman
145
+ def use(middleware, *_args, &block)
146
+ stack(middleware).use(middleware, *_args, &block)
147
+ end # use
148
+
149
+ # StackMiddleware manages the definition of the middleware stack for a given class.
150
+ # It's instances are responsible for the definition of a single piece of middleware, and the class
151
+ # is responsible for specifying the full stack for a given class.
152
+ #
153
+ # When Pancake::Middleware extends a class, an inner class is created in that class called StackMiddleware.
154
+ # That StackMiddleware class inherits from Pancake::Middleware::StackMiddleware.
155
+ #
156
+ # @example The setup when Pancake::Middleware is extended
157
+ # MyClass.extend Pancake::Middleware
158
+ # # sets up
159
+ #
160
+ # class MyClass
161
+ # class StackMiddleware < Pancake::Middleware::StackMiddleware; end
162
+ # end
163
+ #
164
+ # This is then set is an inheritable inner class on the extended class, such that when it is inherited,
165
+ # the StackMiddleware class is inherited to an inner class of the same name on the child.
166
+ class StackMiddleware
167
+ # @api private
168
+ class_inheritable_reader :_central_mwares, :_mwares, :_before, :_after
169
+ @_central_mwares, @_before, @_after, @_mwares = [], {}, {}, {}
170
+
171
+ # @api private
172
+ attr_reader :middleware, :name
173
+ # @api private
174
+ attr_accessor :args, :block, :stack, :options
175
+
176
+ class << self
177
+ def use(mware, *_args, &block)
178
+ new(mware).use(mware, *_args, &block)
179
+ end
180
+
181
+ # Resets this stack middlware. Useful for specs
182
+ def reset!
183
+ _central_mwares.clear
184
+ _mwares.clear
185
+ _before.clear
186
+ _after.clear
187
+ end
188
+
189
+ # Get the middleware list for this StackMiddleware for the given labels
190
+ #
191
+ # @param [Symbol] labels The label or list of labels to construct a stack from.
192
+ #
193
+ # @example Specified labels
194
+ # MyClass::StackMiddleware.middlewares(:production, :demo)
195
+ #
196
+ # @example No Labels Specified
197
+ # MyClass::StackMiddleware.middlewares
198
+ #
199
+ # This will include all defined middlewares in the given stack
200
+ #
201
+ # @return [Array<StackMiddleware>]
202
+ # An array of the middleware definitions to use in the order that they should be applied
203
+ # Takes into account all :before, :after settings and only constructs the stack where
204
+ # the labels are applied
205
+ #
206
+ # @see Pancake.stack_labels for a description on stack labels
207
+ # @api public
208
+ # @since 0.1.0
209
+ # @author Daniel Neighman
210
+ def middlewares(*labels)
211
+ _central_mwares.map do |name|
212
+ map_middleware(name, *labels)
213
+ end.flatten
214
+ end
215
+
216
+ # Map the middleware for a given <name>ed middleware. Applies the before and after groups of middlewares
217
+ #
218
+ # @param [Object] name The name of the middleware to map the before and after groups to
219
+ # @param [Symbol] labels A label or list of labels to use to construct the middleware stack
220
+ #
221
+ # @example
222
+ # MyClass::StackMiddleware.map_middleware(:foo, :production, :demo)
223
+ #
224
+ # Constructs the middleware list based on the middleware named :foo, including all :before, and :after groups
225
+ #
226
+ # @return [Array<StackMiddleware>]
227
+ # Provides an array of StackMiddleware instances in the array [<before :foo>, <:foo>, <after :foo>]
228
+ #
229
+ # @api private
230
+ # @since 0.1.0
231
+ # @author Daniel Neighman
232
+ def map_middleware(name, *labels)
233
+ result = []
234
+ _before[name] ||= []
235
+ _after[name] ||= []
236
+ if _mwares[name] && _mwares[name].use_for_labels?(*labels)
237
+ result << _before[name].map{|n| map_middleware(n)}
238
+ result << _mwares[name]
239
+ result << _after[name].map{|n| map_middleware(n)}
240
+ result.flatten
241
+ end
242
+ result
243
+ end
244
+
245
+ # Provides access to a named middleware
246
+ #
247
+ # @param [Object] name The name of the defined middleware
248
+ #
249
+ # @return [StackMiddleware] The middleware definition associated with <name>
250
+ #
251
+ # @api public
252
+ # @since 0.1.0
253
+ # @author Daniel Neighman
254
+ def [](name)
255
+ _mwares[name]
256
+ end
257
+ end
258
+
259
+ # Provides access to a named middleware
260
+ #
261
+ # @see Pancake::Middleware::StackMiddleware.[] for an explaination
262
+ # @since 0.1.0
263
+ # @author Daniel Neighman
264
+ def [](name)
265
+ self.class._mwares[name]
266
+ end
267
+
268
+ # @param [Object] name a name for this middleware definition. Usually a symbol, but could be the class.
269
+ # @param [Object] stack the stack owner of this middleware.
270
+ # @param [Hash] options an options hash. Provide labels for this middleware.
271
+ # @option options [Array] :labels ([:any])
272
+ # The labels that are associated with this middleware
273
+ # @option options [Object] :before A middleware name to add this middleware before
274
+ # @option options [Object] :after A middleware name to add this middleware after
275
+ #
276
+ # @see Pancake::Middleware.stack_labels
277
+ # @api private
278
+ # @author Daniel Neighman
279
+ def initialize(name, stack, options = {})
280
+ @name, @stack, @options = name, stack, options
281
+ @options[:labels] ||= [:any]
282
+ end
283
+
284
+ # Delete this middleware from the current stack
285
+ #
286
+ # @api public
287
+ # @since 0.1.0
288
+ # @author Daniel Neighman
289
+ def delete!
290
+ self.class._mwares.delete(name)
291
+ self.class._before.delete(name)
292
+ self.class._after.delete(name)
293
+ self.class._central_mwares.delete(name)
294
+ self
295
+ end
296
+
297
+ # Specify the actual middleware definition to use
298
+ #
299
+ # @param [Class] mware A Middleware class to use. This should be a class of Middleware which conforms to the Rack spec
300
+ # @param [Hash] config A configuration hash to give to the middleware class on initialization
301
+ # @yield The block is passed to the middleware on initialization
302
+ #
303
+ # @see Pancake::Middleware.use
304
+ # @api public
305
+ # @since 0.1.0
306
+ # @author Daniel Neighman
307
+ def use(mware, *_args, &block)
308
+ @middleware, @args, @block = mware, _args, block
309
+ @name = @middleware if name.nil?
310
+ if options[:before]
311
+ raise "#{options[:before].inspect} middleware is not defined for this stack" unless self.class._mwares.keys.include?(options[:before])
312
+ self.class._before[options[:before]] ||= []
313
+ self.class._before[options[:before]] << name
314
+ elsif options[:after]
315
+ raise "#{options[:after].inspect} middleware is not defined for this stack" unless self.class._mwares.keys.include?(options[:after])
316
+ self.class._after[options[:after]] ||= []
317
+ self.class._after[options[:after]] << name
318
+ else
319
+ self.class._central_mwares << name unless self.class._central_mwares.include?(name)
320
+ end
321
+ self.class._mwares[name] = self
322
+ self
323
+ end
324
+
325
+ # Checks if this middleware definition should be included from the labels given
326
+ # @param [Symbol] labels The label or list of labels to check if this middleware should be included
327
+ #
328
+ # @return [Boolean] true if this middlware should be included
329
+ #
330
+ # @api private
331
+ # @since 0.1.0
332
+ # @author Daniel Neighman
333
+ def use_for_labels?(*labels)
334
+ return true if labels.empty? || options[:labels].nil? || options[:labels].include?(:any)
335
+ !(options[:labels] & labels).empty?
336
+ end
337
+
338
+ # @api private
339
+ def dup
340
+ result = super
341
+ result.args = result.args.map{|element| element.dup}
342
+ result.options = result.options.dup
343
+ result
344
+ end
345
+ end
346
+ end # Middleware
347
+ end # Pancake
@@ -0,0 +1,16 @@
1
+ require 'logger'
2
+ module Pancake
3
+ module Middlewares
4
+ class Logger
5
+ attr_reader :app
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ env[Pancake::Constants::ENV_LOGGER_KEY] ||= Pancake.logger
12
+ @app.call(env)
13
+ end
14
+ end
15
+ end
16
+ end