pancake 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.textile +95 -0
- data/Rakefile +56 -0
- data/TODO +17 -0
- data/bin/jeweler +19 -0
- data/bin/pancake-gen +17 -0
- data/bin/rubyforge +19 -0
- data/lib/pancake/bootloaders.rb +180 -0
- data/lib/pancake/configuration.rb +145 -0
- data/lib/pancake/constants.rb +5 -0
- data/lib/pancake/core_ext/class.rb +44 -0
- data/lib/pancake/core_ext/object.rb +22 -0
- data/lib/pancake/core_ext/symbol.rb +15 -0
- data/lib/pancake/defaults/configuration.rb +22 -0
- data/lib/pancake/defaults/middlewares.rb +1 -0
- data/lib/pancake/errors.rb +61 -0
- data/lib/pancake/generators/base.rb +12 -0
- data/lib/pancake/generators/micro_generator.rb +17 -0
- data/lib/pancake/generators/short_generator.rb +17 -0
- data/lib/pancake/generators/stack_generator.rb +17 -0
- data/lib/pancake/generators/templates/common/dotgitignore +22 -0
- data/lib/pancake/generators/templates/common/dothtaccess +17 -0
- data/lib/pancake/generators/templates/micro/%stack_name%/%stack_name%.rb.tt +8 -0
- data/lib/pancake/generators/templates/micro/%stack_name%/config.ru.tt +12 -0
- data/lib/pancake/generators/templates/micro/%stack_name%/pancake.init.tt +1 -0
- data/lib/pancake/generators/templates/micro/%stack_name%/public/.empty_directory +0 -0
- data/lib/pancake/generators/templates/micro/%stack_name%/tmp/.empty_directory +0 -0
- data/lib/pancake/generators/templates/micro/%stack_name%/views/root.html.haml +1 -0
- data/lib/pancake/generators/templates/short/%stack_name%/LICENSE.tt +20 -0
- data/lib/pancake/generators/templates/short/%stack_name%/README.tt +7 -0
- data/lib/pancake/generators/templates/short/%stack_name%/Rakefile.tt +50 -0
- data/lib/pancake/generators/templates/short/%stack_name%/VERSION.tt +1 -0
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/%stack_name%.rb.tt +6 -0
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config.ru.tt +10 -0
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/mounts/.empty_directory +0 -0
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/public/.empty_directory +0 -0
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/tmp/.empty_directory +0 -0
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/views/root.html.haml +2 -0
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%.rb.tt +5 -0
- data/lib/pancake/generators/templates/short/%stack_name%/pancake.init.tt +1 -0
- data/lib/pancake/generators/templates/short/%stack_name%/spec/%stack_name%_spec.rb.tt +7 -0
- data/lib/pancake/generators/templates/short/%stack_name%/spec/spec_helper.rb.tt +9 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/LICENSE.tt +20 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/README.tt +7 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/Rakefile.tt +50 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/VERSION.tt +1 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/config/environments/development.rb.tt +18 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/config/environments/production.rb.tt +18 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/config/router.rb.tt +6 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/config.ru.tt +12 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/gems/cache/.empty_directory +0 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/mounts/.empty_directory +0 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/public/.empty_directory +0 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/tmp/.empty_directory +0 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%.rb.tt +3 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/pancake.init.tt +1 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/spec/%stack_name%_spec.rb.tt +7 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/spec/spec_helper.rb.tt +9 -0
- data/lib/pancake/generators.rb +8 -0
- data/lib/pancake/hooks/inheritable_inner_classes.rb +60 -0
- data/lib/pancake/hooks/on_inherit.rb +34 -0
- data/lib/pancake/logger.rb +200 -0
- data/lib/pancake/master.rb +123 -0
- data/lib/pancake/middleware.rb +347 -0
- data/lib/pancake/middlewares/logger.rb +16 -0
- data/lib/pancake/middlewares/static.rb +38 -0
- data/lib/pancake/mime_types.rb +265 -0
- data/lib/pancake/mixins/publish/action_options.rb +104 -0
- data/lib/pancake/mixins/publish.rb +125 -0
- data/lib/pancake/mixins/render/render.rb +168 -0
- data/lib/pancake/mixins/render/template.rb +23 -0
- data/lib/pancake/mixins/render/view_context.rb +21 -0
- data/lib/pancake/mixins/render.rb +109 -0
- data/lib/pancake/mixins/request_helper.rb +100 -0
- data/lib/pancake/mixins/stack_helper.rb +46 -0
- data/lib/pancake/mixins/url.rb +10 -0
- data/lib/pancake/paths.rb +218 -0
- data/lib/pancake/router.rb +99 -0
- data/lib/pancake/stack/app.rb +10 -0
- data/lib/pancake/stack/bootloader.rb +79 -0
- data/lib/pancake/stack/configuration.rb +44 -0
- data/lib/pancake/stack/middleware.rb +0 -0
- data/lib/pancake/stack/router.rb +21 -0
- data/lib/pancake/stack/stack.rb +66 -0
- data/lib/pancake/stacks/short/bootloaders.rb +13 -0
- data/lib/pancake/stacks/short/controller.rb +116 -0
- data/lib/pancake/stacks/short/default/views/base.html.haml +5 -0
- data/lib/pancake/stacks/short/stack.rb +187 -0
- data/lib/pancake/stacks/short.rb +3 -0
- data/lib/pancake.rb +58 -0
- data/spec/helpers/helpers.rb +20 -0
- data/spec/helpers/matchers.rb +25 -0
- data/spec/pancake/bootloaders_spec.rb +109 -0
- data/spec/pancake/configuration_spec.rb +177 -0
- data/spec/pancake/constants_spec.rb +7 -0
- data/spec/pancake/defaults/configuration_spec.rb +58 -0
- data/spec/pancake/fixtures/foo_stack/pancake.init +0 -0
- data/spec/pancake/fixtures/middlewares/other_public/two.html +1 -0
- data/spec/pancake/fixtures/middlewares/public/foo#bar.html +1 -0
- data/spec/pancake/fixtures/middlewares/public/one.html +1 -0
- data/spec/pancake/fixtures/paths/controllers/controller1.rb +0 -0
- data/spec/pancake/fixtures/paths/controllers/controller2.rb +0 -0
- data/spec/pancake/fixtures/paths/controllers/controller3.rb +0 -0
- data/spec/pancake/fixtures/paths/models/model1.rb +0 -0
- data/spec/pancake/fixtures/paths/models/model2.rb +0 -0
- data/spec/pancake/fixtures/paths/models/model3.rb +0 -0
- data/spec/pancake/fixtures/paths/stack/controllers/controller1.rb +0 -0
- data/spec/pancake/fixtures/paths/stack/models/model3.rb +0 -0
- data/spec/pancake/fixtures/paths/stack/views/view1.erb +0 -0
- data/spec/pancake/fixtures/paths/stack/views/view1.rb +0 -0
- data/spec/pancake/fixtures/paths/stack/views/view2.erb +0 -0
- data/spec/pancake/fixtures/paths/stack/views/view2.haml +0 -0
- data/spec/pancake/fixtures/render_templates/context_template.html.erb +2 -0
- data/spec/pancake/fixtures/render_templates/erb_template.html.erb +1 -0
- data/spec/pancake/fixtures/render_templates/erb_template.json.erb +1 -0
- data/spec/pancake/fixtures/render_templates/haml_template.html.haml +1 -0
- data/spec/pancake/fixtures/render_templates/haml_template.xml.haml +1 -0
- data/spec/pancake/fixtures/render_templates/templates/context.erb +2 -0
- data/spec/pancake/fixtures/render_templates/view_context/capture_erb.erb +5 -0
- data/spec/pancake/fixtures/render_templates/view_context/capture_haml.haml +4 -0
- data/spec/pancake/fixtures/render_templates/view_context/concat_erb.erb +2 -0
- data/spec/pancake/fixtures/render_templates/view_context/concat_haml.haml +2 -0
- data/spec/pancake/fixtures/render_templates/view_context/context.erb +3 -0
- data/spec/pancake/fixtures/render_templates/view_context/context2.erb +3 -0
- data/spec/pancake/fixtures/render_templates/view_context/helper_methods.erb +3 -0
- data/spec/pancake/fixtures/render_templates/view_context/inherited_erb_from_haml.erb +5 -0
- data/spec/pancake/fixtures/render_templates/view_context/inherited_erb_level_0.erb +5 -0
- data/spec/pancake/fixtures/render_templates/view_context/inherited_erb_level_1.erb +5 -0
- data/spec/pancake/fixtures/render_templates/view_context/inherited_haml_from_erb.haml +4 -0
- data/spec/pancake/fixtures/render_templates/view_context/inherited_haml_level_0.haml +4 -0
- data/spec/pancake/fixtures/render_templates/view_context/inherited_haml_level_1.haml +4 -0
- data/spec/pancake/fixtures/render_templates/view_context/nested_content_level_0.haml +6 -0
- data/spec/pancake/fixtures/render_templates/view_context/nested_content_level_1.haml +4 -0
- data/spec/pancake/fixtures/render_templates/view_context/nested_inner.erb +1 -0
- data/spec/pancake/fixtures/render_templates/view_context/nested_outer.erb +3 -0
- data/spec/pancake/fixtures/render_templates/view_context/super_erb_from_erb_0.erb +5 -0
- data/spec/pancake/fixtures/render_templates/view_context/super_erb_from_erb_1.erb +6 -0
- data/spec/pancake/fixtures/render_templates/view_context/super_erb_from_haml_0.erb +5 -0
- data/spec/pancake/fixtures/render_templates/view_context/super_erb_from_haml_1.erb +6 -0
- data/spec/pancake/fixtures/render_templates/view_context/super_haml_from_erb_0.haml +4 -0
- data/spec/pancake/fixtures/render_templates/view_context/super_haml_from_erb_1.haml +5 -0
- data/spec/pancake/fixtures/render_templates/view_context/super_haml_from_haml_0.haml +5 -0
- data/spec/pancake/fixtures/render_templates/view_context/super_haml_from_haml_1.haml +5 -0
- data/spec/pancake/fixtures/stacks/short/foobar/other_root/views/base.html.haml +4 -0
- data/spec/pancake/fixtures/stacks/short/foobar/views/basic.html.haml +1 -0
- data/spec/pancake/fixtures/stacks/short/foobar/views/inherited_from_base.html.haml +5 -0
- data/spec/pancake/hooks/on_inherit_spec.rb +65 -0
- data/spec/pancake/inheritance_spec.rb +100 -0
- data/spec/pancake/middleware_spec.rb +401 -0
- data/spec/pancake/middlewares/logger_spec.rb +29 -0
- data/spec/pancake/middlewares/static_spec.rb +83 -0
- data/spec/pancake/mime_types_spec.rb +234 -0
- data/spec/pancake/mixins/publish_spec.rb +94 -0
- data/spec/pancake/mixins/render/template_spec.rb +69 -0
- data/spec/pancake/mixins/render/view_context_spec.rb +248 -0
- data/spec/pancake/mixins/render_spec.rb +56 -0
- data/spec/pancake/mixins/request_helper_spec.rb +27 -0
- data/spec/pancake/mixins/stack_helper_spec.rb +46 -0
- data/spec/pancake/pancake_spec.rb +90 -0
- data/spec/pancake/paths_spec.rb +210 -0
- data/spec/pancake/stack/app_spec.rb +28 -0
- data/spec/pancake/stack/bootloader_spec.rb +41 -0
- data/spec/pancake/stack/middleware_spec.rb +0 -0
- data/spec/pancake/stack/router_spec.rb +282 -0
- data/spec/pancake/stack/stack_configuration_spec.rb +101 -0
- data/spec/pancake/stack/stack_spec.rb +60 -0
- data/spec/pancake/stacks/short/controller_spec.rb +322 -0
- data/spec/pancake/stacks/short/middlewares_spec.rb +22 -0
- data/spec/pancake/stacks/short/router_spec.rb +136 -0
- data/spec/pancake/stacks/short/stack_spec.rb +64 -0
- data/spec/spec_helper.rb +23 -0
- 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
|