hassox-pancake 0.1.6
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.
- data/LICENSE +20 -0
- data/README.textile +95 -0
- data/Rakefile +56 -0
- data/TODO +18 -0
- data/bin/jeweler +19 -0
- data/bin/pancake-gen +17 -0
- data/bin/rubyforge +19 -0
- data/lib/pancake.rb +48 -0
- data/lib/pancake/bootloaders.rb +180 -0
- data/lib/pancake/configuration.rb +140 -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/errors.rb +61 -0
- data/lib/pancake/generators.rb +8 -0
- data/lib/pancake/generators/base.rb +12 -0
- data/lib/pancake/generators/flat_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/flat/%stack_name%/%stack_name%.rb.tt +8 -0
- data/lib/pancake/generators/templates/flat/%stack_name%/.gitignore +21 -0
- data/lib/pancake/generators/templates/flat/%stack_name%/config.ru.tt +12 -0
- data/lib/pancake/generators/templates/flat/%stack_name%/pancake.init.tt +1 -0
- data/lib/pancake/generators/templates/flat/%stack_name%/public/.empty_directory +0 -0
- data/lib/pancake/generators/templates/flat/%stack_name%/tmp/.empty_directory +0 -0
- data/lib/pancake/generators/templates/short/%stack_name%/.gitignore +21 -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 +48 -0
- data/lib/pancake/generators/templates/short/%stack_name%/VERSION.tt +1 -0
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%.rb.tt +5 -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 +12 -0
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/gems/cache/.empty_directory +0 -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%/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%/.gitignore +21 -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 +48 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/VERSION.tt +1 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%.rb.tt +3 -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%/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%/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%/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/hooks/inheritable_inner_classes.rb +60 -0
- data/lib/pancake/hooks/on_inherit.rb +34 -0
- data/lib/pancake/master.rb +87 -0
- data/lib/pancake/middleware.rb +354 -0
- data/lib/pancake/mime_types.rb +265 -0
- data/lib/pancake/mixins/publish.rb +125 -0
- data/lib/pancake/mixins/publish/action_options.rb +104 -0
- data/lib/pancake/mixins/render.rb +61 -0
- data/lib/pancake/mixins/request_helper.rb +92 -0
- data/lib/pancake/mixins/stack_helper.rb +44 -0
- data/lib/pancake/mixins/url.rb +10 -0
- data/lib/pancake/more/controller.rb +4 -0
- data/lib/pancake/more/controller/base.rb +34 -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 +18 -0
- data/lib/pancake/stack/stack.rb +57 -0
- data/lib/pancake/stacks/short.rb +2 -0
- data/lib/pancake/stacks/short/controller.rb +105 -0
- data/lib/pancake/stacks/short/stack.rb +194 -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/fixtures/foo_stack/pancake.init +0 -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 +1 -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/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/mime_types_spec.rb +234 -0
- data/spec/pancake/mixins/publish_spec.rb +94 -0
- data/spec/pancake/mixins/render_spec.rb +55 -0
- data/spec/pancake/mixins/stack_helper_spec.rb +46 -0
- data/spec/pancake/pancake_spec.rb +31 -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 +266 -0
- data/spec/pancake/stack/stack_configuration_spec.rb +101 -0
- data/spec/pancake/stack/stack_spec.rb +55 -0
- data/spec/pancake/stacks/short/controller_spec.rb +287 -0
- data/spec/pancake/stacks/short/router_spec.rb +132 -0
- data/spec/pancake/stacks/short/stack_spec.rb +40 -0
- data/spec/spec_helper.rb +21 -0
- metadata +238 -0
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Pancake
|
|
2
|
+
class Stack
|
|
3
|
+
class_inheritable_accessor :_router
|
|
4
|
+
@_router = Pancake::Router.new
|
|
5
|
+
|
|
6
|
+
def self.router
|
|
7
|
+
yield _router if block_given?
|
|
8
|
+
_router
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.with_router
|
|
12
|
+
yield router if block_given?
|
|
13
|
+
router
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Pancake
|
|
2
|
+
class Stack
|
|
3
|
+
attr_accessor :app_name
|
|
4
|
+
|
|
5
|
+
# extend Hooks::InheritableInnerClasses
|
|
6
|
+
extend Hooks::OnInherit
|
|
7
|
+
extend Pancake::Middleware
|
|
8
|
+
extend Pancake::Paths
|
|
9
|
+
|
|
10
|
+
# Push the default paths in for this stack
|
|
11
|
+
push_paths(:config, "config", "config.rb")
|
|
12
|
+
push_paths(:config, "config/environments", "#{Pancake.env}.rb")
|
|
13
|
+
push_paths(:models, "app/models", "**/*.rb")
|
|
14
|
+
push_paths(:controllers, "app/controllers", "**/*.rb")
|
|
15
|
+
push_paths(:router, "config", "router.rb")
|
|
16
|
+
|
|
17
|
+
#Iterates the list of roots in the stack, and initializes the app found their
|
|
18
|
+
def self.initialize_stack
|
|
19
|
+
raise "Application root not set" if roots.empty?
|
|
20
|
+
|
|
21
|
+
# Run any :init level bootloaders for this stack
|
|
22
|
+
self::BootLoader.run!(:stack_class => self, :only => {:level => :init})
|
|
23
|
+
|
|
24
|
+
@initialized = true
|
|
25
|
+
end # initiailze stack
|
|
26
|
+
|
|
27
|
+
def self.initialized?
|
|
28
|
+
!!@initialized
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def initialize(app = nil, opts = {})
|
|
32
|
+
@app_name = opts.delete(:app_name) || self.class
|
|
33
|
+
self.class.initialize_stack unless self.class.initialized?
|
|
34
|
+
Pancake.configuration.stacks[@app_name] = self
|
|
35
|
+
|
|
36
|
+
# setup the configuration for this stack
|
|
37
|
+
Pancake.configuration.configs[@app_name] = opts[:config] if opts [:config]
|
|
38
|
+
self.configuration(@app_name)
|
|
39
|
+
yield self.configuration(@app_name) if block_given?
|
|
40
|
+
|
|
41
|
+
self.class::BootLoader.run!({
|
|
42
|
+
:stack_class => self.class,
|
|
43
|
+
:stack => self,
|
|
44
|
+
:app => app,
|
|
45
|
+
:app_name => @app_name,
|
|
46
|
+
:except => {:level => :init}
|
|
47
|
+
}.merge(opts))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Construct a stack using the application, wrapped in the middlewares
|
|
51
|
+
# :api: public
|
|
52
|
+
def self.stackup(opts = {}, &block)
|
|
53
|
+
app = new(nil, opts, &block)
|
|
54
|
+
Pancake.configuration.configs[app.app_name].router
|
|
55
|
+
end # stackup
|
|
56
|
+
end # Stack
|
|
57
|
+
end # Pancake
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
module Pancake
|
|
2
|
+
module Stacks
|
|
3
|
+
class Short
|
|
4
|
+
inheritable_inner_classes :Controller
|
|
5
|
+
|
|
6
|
+
class Controller
|
|
7
|
+
extend Mixins::Publish
|
|
8
|
+
include Mixins::Render
|
|
9
|
+
include Mixins::RequestHelper
|
|
10
|
+
include Mixins::StackHelper
|
|
11
|
+
|
|
12
|
+
class_inheritable_accessor :_handle_exception
|
|
13
|
+
|
|
14
|
+
push_paths :views, ["app/views", "views"], "**/*"
|
|
15
|
+
|
|
16
|
+
DEFAULT_EXCEPTION_HANDLER = lambda do |error|
|
|
17
|
+
"#{error.name}: #{error.description}"
|
|
18
|
+
end unless defined?(DEFAULT_EXCEPTION_HANDLER)
|
|
19
|
+
|
|
20
|
+
# @api private
|
|
21
|
+
def self.call(env)
|
|
22
|
+
app = new(env)
|
|
23
|
+
app.dispatch!
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @api public
|
|
27
|
+
attr_accessor :status
|
|
28
|
+
|
|
29
|
+
def initialize(env)
|
|
30
|
+
@env, @request = env, Rack::Request.new(env)
|
|
31
|
+
@status = 200
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Provides access to the request params
|
|
35
|
+
# @api public
|
|
36
|
+
def params
|
|
37
|
+
request.params
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Dispatches to an action based on the params["action"] parameter
|
|
41
|
+
def dispatch!
|
|
42
|
+
params["action"] ||= params[:action]
|
|
43
|
+
params[:format] ||= params["format"]
|
|
44
|
+
|
|
45
|
+
# Check that the action is available
|
|
46
|
+
raise Errors::NotFound, "No Action Found" unless allowed_action?(params["action"])
|
|
47
|
+
|
|
48
|
+
@action_opts = actions[params["action"]]
|
|
49
|
+
if params[:format]
|
|
50
|
+
@content_type, ct, @mime_type = Pancake::MimeTypes.negotiate_by_extension(params[:format].to_s, @action_opts.formats)
|
|
51
|
+
else
|
|
52
|
+
@content_type, ct, @mime_type = Pancake::MimeTypes.negotiate_accept_type(env["HTTP_ACCEPT"], @action_opts.formats)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
raise Errors::NotAcceptable unless @content_type
|
|
56
|
+
|
|
57
|
+
# set the response header
|
|
58
|
+
headers["Content-Type"] = ct
|
|
59
|
+
Rack::Response.new(self.send(params["action"]), status, headers).finish
|
|
60
|
+
|
|
61
|
+
rescue Errors::HttpError => e
|
|
62
|
+
handle_request_exception(e)
|
|
63
|
+
rescue Exception => e
|
|
64
|
+
server_error = Errors::Server.new
|
|
65
|
+
server_error.exceptions << e
|
|
66
|
+
handle_request_exception(server_error)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def content_type
|
|
70
|
+
@content_type
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.handle_exception(&block)
|
|
74
|
+
if block_given?
|
|
75
|
+
self._handle_exception = block
|
|
76
|
+
else
|
|
77
|
+
self._handle_exception || DEFAULT_EXCEPTION_HANDLER
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def handle_request_exception(error)
|
|
82
|
+
self.status = error.code
|
|
83
|
+
result = instance_exec error, &self.class.handle_exception
|
|
84
|
+
Rack::Response.new(result, status, headers).finish
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
def allowed_action?(action)
|
|
89
|
+
self.class.actions.include?(action.to_s)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
public
|
|
93
|
+
def self.roots
|
|
94
|
+
stack_class.roots
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def _tempate_name_for(name, opts)
|
|
98
|
+
opts[:format] ||= content_type
|
|
99
|
+
"#{name}.#{opts[:format]}"
|
|
100
|
+
end
|
|
101
|
+
end # Controller
|
|
102
|
+
|
|
103
|
+
end # Short
|
|
104
|
+
end # Stacks
|
|
105
|
+
end # Pancake
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
module Pancake
|
|
2
|
+
module Stacks
|
|
3
|
+
class Short < Pancake::Stack
|
|
4
|
+
|
|
5
|
+
def self.new_app_instance
|
|
6
|
+
self::Controller
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Marks a method as published.
|
|
10
|
+
# This is done implicitly when using the get, post, put, delete methods on a Stacks::Short
|
|
11
|
+
# But can be done explicitly
|
|
12
|
+
#
|
|
13
|
+
# @see Pancake::Mixins::Publish#publish
|
|
14
|
+
# @api public
|
|
15
|
+
def self.publish(*args)
|
|
16
|
+
@published = true
|
|
17
|
+
self::Controller.publish(*args)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @see Pancake::Mixins::Publish#as
|
|
21
|
+
def self.as(*args)
|
|
22
|
+
self::Controller.as(*args)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @see Pancake::Mixins::Publish#provides
|
|
26
|
+
def self.provides(*formats)
|
|
27
|
+
self::Controller.provides(*formats)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.handle_exception(*args, &block)
|
|
31
|
+
self::Controller.handle_exception(*args, &block)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Gets a resource at a given path
|
|
35
|
+
#
|
|
36
|
+
# The block should finish with the final result of the action
|
|
37
|
+
#
|
|
38
|
+
# @param [String] path - a url path that conforms to Rack::Router match path.
|
|
39
|
+
# @param block - the contents of the block are executed when the path is matched.
|
|
40
|
+
#
|
|
41
|
+
# @example
|
|
42
|
+
# get "/posts(/:year(/:month(/:date))" do
|
|
43
|
+
# # do stuff to get posts and render them
|
|
44
|
+
# end
|
|
45
|
+
#
|
|
46
|
+
# @see Rack::Router
|
|
47
|
+
# @api public
|
|
48
|
+
# @author Daniel Neighman
|
|
49
|
+
def self.get(path, opts = {}, &block)
|
|
50
|
+
define_published_action(:get, path, opts, block)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Posts a resource to a given path
|
|
54
|
+
#
|
|
55
|
+
# The block should finish with the final result of the action
|
|
56
|
+
#
|
|
57
|
+
# @param [String] path - a url path that conforms to Rack::Router match path.
|
|
58
|
+
# @param block - the contents of the block are executed when the path is matched.
|
|
59
|
+
#
|
|
60
|
+
# @example
|
|
61
|
+
# post "/posts" do
|
|
62
|
+
# # do stuff to post /posts and render them
|
|
63
|
+
# end
|
|
64
|
+
#
|
|
65
|
+
# @see Rack::Router
|
|
66
|
+
# @api public
|
|
67
|
+
# @author Daniel Neighman
|
|
68
|
+
def self.post(path, opts = {}, &block)
|
|
69
|
+
define_published_action(:post, path, opts, block)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Puts a resource to a given path
|
|
73
|
+
#
|
|
74
|
+
# The block should finish with the final result of the action
|
|
75
|
+
#
|
|
76
|
+
# @param [String] path - a url path that conforms to Rack::Router match path.
|
|
77
|
+
# @param block - the contents of the block are executed when the path is matched.
|
|
78
|
+
#
|
|
79
|
+
# @example
|
|
80
|
+
# put "/posts" do
|
|
81
|
+
# # do stuff to post /posts and render them
|
|
82
|
+
# end
|
|
83
|
+
#
|
|
84
|
+
# @see Rack::Router
|
|
85
|
+
# @api public
|
|
86
|
+
# @author Daniel Neighman
|
|
87
|
+
def self.put(path, opts = {}, &block)
|
|
88
|
+
define_published_action(:put, path, opts, block)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Deletes the resource at a given path
|
|
92
|
+
#
|
|
93
|
+
# The block should finish with the final result of the action
|
|
94
|
+
#
|
|
95
|
+
# @param [String] path - a url path that conforms to Rack::Router match path.
|
|
96
|
+
# @param block - the contents of the block are executed when the path is matched.
|
|
97
|
+
#
|
|
98
|
+
# @example
|
|
99
|
+
# delete "/posts/foo-is-post" do
|
|
100
|
+
# # do stuff to post foo-is-post and render the result
|
|
101
|
+
# end
|
|
102
|
+
#
|
|
103
|
+
# @see Rack::Router
|
|
104
|
+
# @api public
|
|
105
|
+
# @author Daniel Neighman
|
|
106
|
+
def self.delete(path, opts = {}, &block)
|
|
107
|
+
define_published_action(:delete, path, opts, block)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
# Defines an action on the inner Controller class of this stack.
|
|
112
|
+
# Also sets it as published if it's not already published.
|
|
113
|
+
#
|
|
114
|
+
# @param [Symbol] method - a smbol specifying the HTTP method
|
|
115
|
+
# @param [String] path - a string specifying the path to map the url to
|
|
116
|
+
# @api private
|
|
117
|
+
# @author Daniel Neighman
|
|
118
|
+
def self.define_published_action(method, path, opts, block)
|
|
119
|
+
self::Controller.publish unless @published
|
|
120
|
+
@published = nil
|
|
121
|
+
|
|
122
|
+
action_name = next_action_name(method,path)
|
|
123
|
+
attach_action(action_name, block)
|
|
124
|
+
|
|
125
|
+
attach_route(method, path, action_name, opts)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Does the work of actually defining the action on the Controller Class
|
|
129
|
+
#
|
|
130
|
+
# @param [String] - the name of the method to create on the Controller class
|
|
131
|
+
# @api private
|
|
132
|
+
# @author Daniel Neighman
|
|
133
|
+
def self.attach_action(method_name, block)
|
|
134
|
+
self::Controller.class_eval do
|
|
135
|
+
define_method(method_name, &block)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Supplies the path as a route to the stack router
|
|
140
|
+
#
|
|
141
|
+
# @param method [Symbol]
|
|
142
|
+
#
|
|
143
|
+
# @example
|
|
144
|
+
# attach_route(:get, "/foo/bar", "get_00001__foo_bar")
|
|
145
|
+
#
|
|
146
|
+
# @api private
|
|
147
|
+
# @author Daniel Neighman
|
|
148
|
+
def self.attach_route(method, path, action_name, options)
|
|
149
|
+
name = options.delete(:_name)
|
|
150
|
+
options[:conditions] ||= {}
|
|
151
|
+
options[:conditions][:request_method] = method.to_s.upcase
|
|
152
|
+
options[:default_values] ||= {}
|
|
153
|
+
options[:default_values][:action] = action_name
|
|
154
|
+
options[:_exact] = true unless options[:_exact] == false
|
|
155
|
+
r = router.add(path, options)
|
|
156
|
+
r.name(name) if name
|
|
157
|
+
r
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# provides for methods of the following form on Controller
|
|
161
|
+
# :<method>_<number>_<sanitized_path>
|
|
162
|
+
# where <number> is incremented
|
|
163
|
+
#
|
|
164
|
+
# @param [Symbol] method - the HTTP method to look for
|
|
165
|
+
# @param [String] path - the url path expression to encode into the method name
|
|
166
|
+
#
|
|
167
|
+
# @return [String] - The next method name to use that won't clash with previously configured actions
|
|
168
|
+
#
|
|
169
|
+
# @api private
|
|
170
|
+
# @author Daniel Neighman
|
|
171
|
+
def self.next_action_name(method, path)
|
|
172
|
+
last = self::Controller.public_instance_methods.grep(%r[#{method}_\d+]).sort.reverse.first
|
|
173
|
+
next_method = 0
|
|
174
|
+
unless last.nil?
|
|
175
|
+
last =~ %r[#{method}_(\d+)]
|
|
176
|
+
next_method = $1
|
|
177
|
+
end
|
|
178
|
+
sprintf("#{method}_%04d_#{sanitize_path(path)}", next_method)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# sanitizes a path so it's able to be used as a method name
|
|
182
|
+
#
|
|
183
|
+
# @param [String] path - the path to sanitize
|
|
184
|
+
#
|
|
185
|
+
# @return [String] the sanitized version of the path safe to use as a method name
|
|
186
|
+
# @api private
|
|
187
|
+
# @author Daniel Neighman
|
|
188
|
+
def self.sanitize_path(path)
|
|
189
|
+
path.gsub(/\W/, "_")
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
end # Short
|
|
193
|
+
end # Stacks
|
|
194
|
+
end # Pancake
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Pancake
|
|
2
|
+
module Spec
|
|
3
|
+
module Helpers
|
|
4
|
+
def clear_constants(*classes)
|
|
5
|
+
classes.flatten.each do |klass|
|
|
6
|
+
begin
|
|
7
|
+
Object.class_eval do
|
|
8
|
+
remove_const klass
|
|
9
|
+
end
|
|
10
|
+
rescue => e
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end # clear_constnat3
|
|
14
|
+
|
|
15
|
+
def env_for(path = "/", opts = {})
|
|
16
|
+
Rack::MockRequest.env_for(path, opts)
|
|
17
|
+
end
|
|
18
|
+
end # Helpers
|
|
19
|
+
end # Spec
|
|
20
|
+
end # Pancake
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Pancake
|
|
2
|
+
module Matchers
|
|
3
|
+
|
|
4
|
+
class InheritFrom
|
|
5
|
+
def initialize(expected)
|
|
6
|
+
@expected = expected
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def matches?(target)
|
|
10
|
+
@target = target
|
|
11
|
+
@target.ancestors.include?(@expected)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def failure_message
|
|
15
|
+
"expected #{@target} to inherit from #{@expected} but did not"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def inherit_from(expected)
|
|
20
|
+
InheritFrom.new(expected)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
end # Matchers
|
|
25
|
+
end # Pancake
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe "Pancake::Stack::BootLoader" do
|
|
4
|
+
|
|
5
|
+
before(:each) do
|
|
6
|
+
$captures = []
|
|
7
|
+
|
|
8
|
+
class ::FooStack < Pancake::Stack
|
|
9
|
+
roots << File.join(Pancake.get_root(__FILE__), "..", "fixtures", "foo_stack")
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
after(:each) do
|
|
14
|
+
clear_constants(:FooStack)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should not add the bootloader without it having a run! method" do
|
|
18
|
+
lambda do
|
|
19
|
+
FooStack::BootLoader.add(:foo){|s,c| :here }
|
|
20
|
+
end.should raise_error
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "should allow me to add an application specific BootLoader" do
|
|
24
|
+
FooStack::BootLoader.add(:my_initializer){ def run!; :foo; end}
|
|
25
|
+
FooStack::BootLoader[:my_initializer].call({}).should == :foo
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "should provide a bootloader instance" do
|
|
29
|
+
FooStack::BootLoader.add(:my_initializer){ def run!; :foo; end}
|
|
30
|
+
FooStack::BootLoader[:my_initializer].should inherit_from(Pancake::BootLoaderMixin::Base)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "should allow me to add multiple boot loaders" do
|
|
34
|
+
FooStack::BootLoader.add(:foo){ def run!; :foo; end}
|
|
35
|
+
FooStack::BootLoader.add(:bar){ def run!; :bar; end}
|
|
36
|
+
FooStack::BootLoader[:foo].call({}).should == :foo
|
|
37
|
+
FooStack::BootLoader[:bar].call({}).should == :bar
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "should allow me to add a bootloader before another" do
|
|
41
|
+
$captures.should be_empty
|
|
42
|
+
FooStack::BootLoader.add(:foo){ def run!; $captures << :foo; end}
|
|
43
|
+
FooStack::BootLoader.add(:bar){ def run!; $captures << :bar; end}
|
|
44
|
+
FooStack::BootLoader.add(:baz, :before => :bar){ def run!; $captures << :baz; end}
|
|
45
|
+
FooStack.new
|
|
46
|
+
$captures.should == [:foo, :baz, :bar]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "should allow me to add a bootloader after another" do
|
|
50
|
+
$captures.should be_empty
|
|
51
|
+
FooStack::BootLoader.add(:foo){ def run!; $captures << :foo; end}
|
|
52
|
+
FooStack::BootLoader.add(:bar){ def run!; $captures << :bar; end}
|
|
53
|
+
FooStack::BootLoader.add(:baz, :after => :foo){ def run!; $captures << :baz; end}
|
|
54
|
+
FooStack.new
|
|
55
|
+
$captures.should == [:foo, :baz, :bar]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "should provide an arbitrarily complex setup" do
|
|
59
|
+
$captures.should be_empty
|
|
60
|
+
FooStack::BootLoader.add(:foo ){ def run!; $captures << :foo; end}
|
|
61
|
+
FooStack::BootLoader.add(:bar ){ def run!; $captures << :bar; end}
|
|
62
|
+
FooStack::BootLoader.add(:baz, :after => :foo ){ def run!; $captures << :baz; end}
|
|
63
|
+
FooStack::BootLoader.add(:paz, :before => :baz ){ def run!; $captures << :paz; end}
|
|
64
|
+
FooStack::BootLoader.add(:fred, :before => :bar ){ def run!; $captures << :fred; end}
|
|
65
|
+
FooStack::BootLoader.add(:barney, :after => :fred ){ def run!; $captures << :barney; end}
|
|
66
|
+
|
|
67
|
+
FooStack.new
|
|
68
|
+
$captures.should == [:foo, :paz, :baz, :fred, :barney, :bar]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
describe "types" do
|
|
72
|
+
|
|
73
|
+
it "should run bootloaders marked as :init" do
|
|
74
|
+
FooStack::BootLoader.add(:bar, :level => :init ){ def run!; $captures << [:bar, :init ]; end}
|
|
75
|
+
FooStack::BootLoader.add(:baz, :level => :init ){ def run!; $captures << [:baz, :init ]; end}
|
|
76
|
+
FooStack::BootLoader.add(:paz, :level => :init, :before => :baz){ def run!; $captures << [:paz, :init]; end}
|
|
77
|
+
|
|
78
|
+
FooStack::BootLoader.run!(:only => {:level => :init})
|
|
79
|
+
$captures.should == [[:bar, :init], [:paz, :init], [:baz, :init]]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "should run init or then default level bootloaders individually" do
|
|
83
|
+
FooStack::BootLoader.add(:foo ){ def run!; $captures << [:foo, :default]; end}
|
|
84
|
+
FooStack::BootLoader.add(:grh ){ def run!; $captures << [:grh, :default]; end}
|
|
85
|
+
FooStack::BootLoader.add(:bar, :level => :init ){ def run!; $captures << [:bar, :init ]; end}
|
|
86
|
+
FooStack::BootLoader.add(:ptf, :before => :grh ){ def run!; $captures << [:ptf, :default]; end}
|
|
87
|
+
FooStack::BootLoader.add(:baz, :level => :init ){ def run!; $captures << [:baz, :init ]; end}
|
|
88
|
+
FooStack::BootLoader.add(:paz, :level => :init, :before => :baz){ def run!; $captures << [:paz, :init]; end}
|
|
89
|
+
|
|
90
|
+
FooStack.new
|
|
91
|
+
$captures.should == [[:bar, :init], [:paz, :init], [:baz, :init], [:foo, :default], [:ptf, :default], [:grh, :default]]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "should inherit from the default boot loaders" do
|
|
95
|
+
::Pancake::Stack::BootLoader.add(:default_boot_loader_test){def run!; end}
|
|
96
|
+
class ::Bario < Pancake::Stack; end
|
|
97
|
+
Bario::BootLoader.map{|n,bl| n}.should include(:default_boot_loader_test)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "should let me pass options to the bootloaders and pass them on" do
|
|
101
|
+
FooStack::BootLoader.add(:foo){ def run!; config[:result] << :foo; end}
|
|
102
|
+
FooStack::BootLoader.add(:bar){ def run!; config[:result] << :bar; end}
|
|
103
|
+
|
|
104
|
+
opts = { :result => [] }
|
|
105
|
+
FooStack.new(nil, opts)
|
|
106
|
+
opts[:result].should == [:foo, :bar]
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|