short_stack 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/.document +5 -0
  2. data/.gitignore +24 -0
  3. data/Gemfile +13 -0
  4. data/Gemfile.lock +71 -0
  5. data/LICENSE +20 -0
  6. data/README.rdoc +17 -0
  7. data/Rakefile +26 -0
  8. data/VERSION +1 -0
  9. data/lib/pancake/generators/global.rb +2 -0
  10. data/lib/pancake/generators/micro_generator.rb +23 -0
  11. data/lib/pancake/generators/short_generator.rb +23 -0
  12. data/lib/pancake/generators/templates/common/Gemfile +7 -0
  13. data/lib/pancake/generators/templates/common/dotgitignore +22 -0
  14. data/lib/pancake/generators/templates/common/dothtaccess +17 -0
  15. data/lib/pancake/generators/templates/micro/%stack_name%/%stack_name%.rb.tt +9 -0
  16. data/lib/pancake/generators/templates/micro/%stack_name%/Rakefile.tt +40 -0
  17. data/lib/pancake/generators/templates/micro/%stack_name%/config.ru.tt +15 -0
  18. data/lib/pancake/generators/templates/micro/%stack_name%/pancake_init.rb.tt +1 -0
  19. data/lib/pancake/generators/templates/micro/%stack_name%/public/.empty_directory +0 -0
  20. data/lib/pancake/generators/templates/micro/%stack_name%/tmp/.empty_directory +0 -0
  21. data/lib/pancake/generators/templates/micro/%stack_name%/views/layouts/application.html.haml +5 -0
  22. data/lib/pancake/generators/templates/micro/%stack_name%/views/root.html.haml +1 -0
  23. data/lib/pancake/generators/templates/short/%stack_name%/LICENSE.tt +20 -0
  24. data/lib/pancake/generators/templates/short/%stack_name%/README.tt +7 -0
  25. data/lib/pancake/generators/templates/short/%stack_name%/Rakefile.tt +56 -0
  26. data/lib/pancake/generators/templates/short/%stack_name%/VERSION.tt +1 -0
  27. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%.rb.tt +14 -0
  28. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/%stack_name%.rb.tt +6 -0
  29. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config.ru.tt +11 -0
  30. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/config.rb.tt +23 -0
  31. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/environments/development.rb.tt +15 -0
  32. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/environments/production.rb.tt +16 -0
  33. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/environments/staging.rb.tt +15 -0
  34. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/models/.empty_directory +0 -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%/tasks/%stack_name%.rake.tt +4 -0
  38. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/tmp/.empty_directory +0 -0
  39. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/views/layouts/application.html.haml +5 -0
  40. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/views/root.html.haml +2 -0
  41. data/lib/pancake/generators/templates/short/%stack_name%/pancake_init.rb.tt +1 -0
  42. data/lib/pancake/generators/templates/short/%stack_name%/spec/%stack_name%_spec.rb.tt +11 -0
  43. data/lib/pancake/generators/templates/short/%stack_name%/spec/spec_helper.rb.tt +13 -0
  44. data/lib/short_stack.rb +213 -0
  45. data/lib/short_stack/controller.rb +185 -0
  46. data/lib/short_stack/default/views/base.html.haml +5 -0
  47. data/lib/short_stack/default/views/error.html.haml +12 -0
  48. data/lib/short_stack/middleware.rb +14 -0
  49. data/short_stack.gemspec +38 -0
  50. data/spec/fixtures/foobar/other_root/views/base.html.haml +4 -0
  51. data/spec/fixtures/foobar/views/basic.html.haml +1 -0
  52. data/spec/fixtures/foobar/views/inherited_from_base.html.haml +5 -0
  53. data/spec/fixtures/foobar/views/template.html.haml +3 -0
  54. data/spec/fixtures/foobar/views/vault.html.haml +3 -0
  55. data/spec/short_stack/controller_spec.rb +444 -0
  56. data/spec/short_stack/middlewares_spec.rb +14 -0
  57. data/spec/short_stack/router_spec.rb +153 -0
  58. data/spec/short_stack/short_stack_spec.rb +122 -0
  59. data/spec/short_stack/stack_spec.rb +124 -0
  60. data/spec/spec.opts +1 -0
  61. data/spec/spec_helper.rb +13 -0
  62. metadata +181 -0
@@ -0,0 +1,14 @@
1
+ require 'short_stack'
2
+ require 'haml'
3
+
4
+ class <%= ActiveSupport::Inflector.camelize(stack_name) %> < ShortStack
5
+ add_root(__FILE__, "<%= stack_name %>")
6
+
7
+ # Hook to use before we mount any applications
8
+ # before_mount_applications do
9
+ # end
10
+
11
+ initialize_stack
12
+ end
13
+
14
+ require ::File.join(Pancake.get_root(__FILE__, "<%= stack_name %>"), "<%= stack_name %>")
@@ -0,0 +1,6 @@
1
+ class <%= ActiveSupport::Inflector.camelize(stack_name) %>
2
+ get "/" do
3
+ render :root
4
+ end
5
+ end
6
+
@@ -0,0 +1,11 @@
1
+ require 'pancake'
2
+ require ::File.join(::File.expand_path(::File.dirname(__FILE__)), "..", "<%= stack_name %>")
3
+
4
+ # get the application to run. The application in the Pancake.start block
5
+ # is the master application. It will have all requests directed to it through the
6
+ # pancake middleware
7
+ # This should be a very minimal file, but should be used when any stand alone code needs to be included
8
+ <%= ActiveSupport::Inflector.camelize(stack_name) %>.include_pancake_stack!
9
+ app = Pancake.start(:root => Pancake.get_root(__FILE__)){ <%= ActiveSupport::Inflector.camelize(stack_name) %>.stackup(:master => true) }
10
+
11
+ run app
@@ -0,0 +1,23 @@
1
+ # Enter any global configuration for the stack in this file.
2
+ <% klass_name = ActiveSupport::Inflector.camelize(stack_name) %>
3
+ class <%= klass_name %>
4
+ # include middleware for the development stack
5
+ # Labels can be set in the config/environments/<env>.rb file to limit
6
+ # middleware loading.
7
+ # stack(:middleware_name, :labels => [:development, :production]).use(MiddlewareClass)
8
+
9
+ class self::Configuration
10
+ # Add defaults to your stack configuration.
11
+ # This is scoped to this stack, and is inhertied into child stacks
12
+ #
13
+ # Fixed value defaults:
14
+ # default :var_name, :value, "A description of the variable"
15
+ #
16
+ # Lazy Defaults:
17
+ # default :var_name, lambda{ configuration_method }, "Some Description"
18
+
19
+ # Declare methods on your configuraiton
20
+ # def configuration_method; #stuff; end
21
+ end
22
+ end
23
+
@@ -0,0 +1,15 @@
1
+ Pancake.logger.info "Loading Development Environment"
2
+
3
+ # Pancake.handle_errors!(true) # uncomment to have the stack handle any errors that occur
4
+
5
+ class <%= ActiveSupport::Inflector.camelize(stack_name) %>
6
+ # include middleware for the development stack
7
+ # stack(:middleware_name).use(MiddlewareClass)
8
+ end
9
+
10
+ # Add code to hooks. Default available hooks:
11
+ # :before_build_stack, :before_mount_applications, :after_initialize_application, :after_build_stack
12
+
13
+ # <%= ActiveSupport::Inflector.camelize(stack_name) %>.before_build_stack do
14
+ # # stuff to do
15
+ # end
@@ -0,0 +1,16 @@
1
+ Pancake.logger.info "Loading Production Environment"
2
+
3
+ Pancake.handle_errors!(true) # uncomment to have the stack handle any errors that occur
4
+ <% klass_name = ActiveSupport::Inflector.camelize(stack_name) %>
5
+ class <%= klass_name %>
6
+ # include middleware for the development stack
7
+ # stack(:middleware_name).use(MiddlewareClass)
8
+ end
9
+
10
+ # Add code to hooks. Default available hooks:
11
+ # :before_build_stack, :before_mount_applications, :after_initialize_application, :after_build_stack
12
+
13
+ # <%= klass_name %>.before_build_stack do
14
+ # # stuff to do
15
+ # end
16
+
@@ -0,0 +1,15 @@
1
+ Pancake.logger.info "Loading Staging Environment"
2
+
3
+ Pancake.handle_errors!(true) # uncomment to have the stack handle any errors that occur
4
+ <% klass_name = ActiveSupport::Inflector.camelize(stack_name) %>
5
+ class <%= klass_name %>
6
+ # include middleware for the development stack
7
+ # stack(:middleware_name).use(MiddlewareClass)
8
+ end
9
+
10
+ # Add code to hooks. Default available hooks:
11
+ # :before_build_stack, :before_mount_applications, :after_initialize_application, :after_build_stack
12
+
13
+ # <%= klass_name %>.before_build_stack do
14
+ # # stuff to do
15
+ # end
@@ -0,0 +1,4 @@
1
+ namespace :<%= stack_name %> do
2
+ # Create your tasks here
3
+ end
4
+
@@ -0,0 +1 @@
1
+ require ::File.join(::File.expand_path(::File.dirname(__FILE__)), "lib", "<%= stack_name %>")
@@ -0,0 +1,11 @@
1
+ require ::File.expand_path(::File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe <%= ActiveSupport::Inflector.camelize(stack_name) %> do
4
+ def app
5
+ <%= stack_name %>.stackup
6
+ end
7
+
8
+ it "fails" do
9
+ fail "hey buddy, you should probably rename this file and start specing for real"
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec'
2
+ require 'rack/test'
3
+ require 'pancake'
4
+
5
+
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
8
+ require '<%= stack_name -%>'
9
+
10
+ Spec::Runner.configure do |config|
11
+ config.include(Rack::Test::Methods)
12
+ config.include(Pancake::Test::Matchers)
13
+ end
@@ -0,0 +1,213 @@
1
+ require 'pancake'
2
+ require 'short_stack/middleware'
3
+ require 'short_stack/controller'
4
+
5
+ class ShortStack < Pancake::Stack
6
+ add_root(__FILE__, "short_stack/default")
7
+
8
+ push_paths(:models,"models", "**/*.rb")
9
+
10
+ # Make sure that the stack has been initialized
11
+ # Before we inherit any children.
12
+ # Otherwise they might not be loaded yet
13
+ before_inner_class_inheritance do |parent|
14
+ parent.initialize_stack
15
+ end
16
+
17
+ def self.new_endpoint_instance
18
+ self::Controller
19
+ end
20
+
21
+ # Marks a method as published.
22
+ # This is done implicitly when using the get, post, put, delete methods on a Stacks::Short
23
+ # But can be done explicitly
24
+ #
25
+ # @see Pancake::Mixins::Publish#publish
26
+ # @api public
27
+ def self.publish(*args)
28
+ @published = true
29
+ self::Controller.publish(*args)
30
+ end
31
+
32
+ # @see Pancake::Mixins::Publish#as
33
+ def self.as(*args)
34
+ self::Controller.as(*args)
35
+ end
36
+
37
+ # @see Pancake::Mixins::Publish#provides
38
+ def self.provides(*formats)
39
+ self::Controller.provides(*formats)
40
+ end
41
+
42
+ def self.handle_exception(*args, &block)
43
+ self::Controller.handle_exception(*args, &block)
44
+ end
45
+
46
+ def self.helpers(&blk)
47
+ m = Module.new(&blk)
48
+ self::Controller.class_eval{ include m }
49
+ end
50
+
51
+ def self.template(*args)
52
+ self::Controller.template(*args)
53
+ end
54
+
55
+ def self.base_template_name
56
+ self::Controller.base_template_name
57
+ end
58
+
59
+
60
+ # Gets a resource at a given path
61
+ #
62
+ # The block should finish with the final result of the action
63
+ #
64
+ # @param [String] path - a url path that conforms to Usher match path.
65
+ # @param block - the contents of the block are executed when the path is matched.
66
+ #
67
+ # @example
68
+ # get "/posts(/:year(/:month(/:date))" do
69
+ # # do stuff to get posts and render them
70
+ # end
71
+ #
72
+ # @see Usher
73
+ # @api public
74
+ # @author Daniel Neighman
75
+ def self.get(path, opts = {}, &block)
76
+ define_published_action(:get, path, opts, block)
77
+ end
78
+
79
+ # Posts a resource to a given path
80
+ #
81
+ # The block should finish with the final result of the action
82
+ #
83
+ # @param [String] path - a url path that conforms to Usher match path.
84
+ # @param block - the contents of the block are executed when the path is matched.
85
+ #
86
+ # @example
87
+ # post "/posts" do
88
+ # # do stuff to post /posts and render them
89
+ # end
90
+ #
91
+ # @see Usher
92
+ # @api public
93
+ # @author Daniel Neighman
94
+ def self.post(path, opts = {}, &block)
95
+ define_published_action(:post, path, opts, block)
96
+ end
97
+
98
+ # Puts a resource to a given path
99
+ #
100
+ # The block should finish with the final result of the action
101
+ #
102
+ # @param [String] path - a url path that conforms to Usher match path.
103
+ # @param block - the contents of the block are executed when the path is matched.
104
+ #
105
+ # @example
106
+ # put "/posts" do
107
+ # # do stuff to post /posts and render them
108
+ # end
109
+ #
110
+ # @see Usher
111
+ # @api public
112
+ # @author Daniel Neighman
113
+ def self.put(path, opts = {}, &block)
114
+ define_published_action(:put, path, opts, block)
115
+ end
116
+
117
+ # Deletes the resource at a given path
118
+ #
119
+ # The block should finish with the final result of the action
120
+ #
121
+ # @param [String] path - a url path that conforms to Usher match path.
122
+ # @param block - the contents of the block are executed when the path is matched.
123
+ #
124
+ # @example
125
+ # delete "/posts/foo-is-post" do
126
+ # # do stuff to post foo-is-post and render the result
127
+ # end
128
+ #
129
+ # @see Usher
130
+ # @api public
131
+ # @author Daniel Neighman
132
+ def self.delete(path, opts = {}, &block)
133
+ define_published_action(:delete, path, opts, block)
134
+ end
135
+
136
+ # Matches any method to the route
137
+ # @api public
138
+ def self.any(path, opts={}, &block)
139
+ define_published_action(:any, path, opts, block)
140
+ end
141
+
142
+ private
143
+ # Defines an action on the inner Controller class of this stack.
144
+ # Also sets it as published if it's not already published.
145
+ #
146
+ # @param [Symbol] method - a smbol specifying the HTTP method
147
+ # @param [String] path - a string specifying the path to map the url to
148
+ # @api private
149
+ # @author Daniel Neighman
150
+ def self.define_published_action(method, path, opts, block)
151
+ self::Controller.publish unless @published
152
+ @published = nil
153
+
154
+ action_name = controller_method_name(method,path)
155
+ attach_action(action_name, block)
156
+ attach_route(method, path, action_name, opts)
157
+ end
158
+
159
+ # Does the work of actually defining the action on the Controller Class
160
+ #
161
+ # @param [String] - the name of the method to create on the Controller class
162
+ # @api private
163
+ # @author Daniel Neighman
164
+ def self.attach_action(method_name, block)
165
+ self::Controller.class_eval do
166
+ define_method(method_name, &block)
167
+ end
168
+ end
169
+
170
+ # Supplies the path as a route to the stack router
171
+ #
172
+ # @param method [Symbol]
173
+ #
174
+ # @example
175
+ # attach_route(:get, "/foo/bar", "get__foo_bar")
176
+ #
177
+ # @api private
178
+ # @author Daniel Neighman
179
+ def self.attach_route(method, path, action_name, options)
180
+ name = options.delete(:name)
181
+ r = router.add(path, options)
182
+ r.send(method) unless method == :any
183
+ r.name(name) if name
184
+ r.to(:action => action_name)
185
+ r
186
+ end
187
+
188
+ # provides for methods of the following form on Controller
189
+ # :<method>_<sanitized_path>
190
+ #
191
+ # @param [Symbol] method - the HTTP method to look for
192
+ # @param [String] path - the url path expression to encode into the method name
193
+ #
194
+ # @return [String] - The actual controller method name to use for the action
195
+ #
196
+ # @api private
197
+ # @author Daniel Neighman
198
+ def self.controller_method_name(method, path)
199
+ "#{method}_#{sanitize_path(path)}"
200
+ end
201
+
202
+ # sanitizes a path so it's able to be used as a method name
203
+ #
204
+ # @param [String] path - the path to sanitize
205
+ #
206
+ # @return [String] the sanitized version of the path safe to use as a method name
207
+ # @api private
208
+ # @author Daniel Neighman
209
+ def self.sanitize_path(path)
210
+ path.gsub(/\W/, "_")
211
+ end
212
+ end # ShortStack
213
+
@@ -0,0 +1,185 @@
1
+ class ShortStack < Pancake::Stack
2
+ inheritable_inner_classes :Controller
3
+
4
+ class Controller
5
+ extend Pancake::Mixins::Publish
6
+ include Pancake::Mixins::Render
7
+ include Pancake::Mixins::RequestHelper
8
+ include Pancake::Mixins::ResponseHelper
9
+ include Pancake::Mixins::StackHelper
10
+
11
+ inheritable_inner_classes :ViewContext
12
+
13
+ class self::ViewContext
14
+ include Pancake::Mixins::RequestHelper
15
+ include AnyView
16
+
17
+ # No way to get the env into the view context... this is not good :(
18
+ def env
19
+ _view_context_for.env
20
+ end
21
+
22
+ def self.template(name_or_template, opts = {})
23
+ opts[:format] ||= content_type
24
+ super
25
+ end
26
+
27
+ def template(name_or_template, opts={})
28
+ opts[:format] ||= content_type
29
+ super
30
+ end
31
+
32
+ def _template_name_for(name, opts = {})
33
+ opts[:format] ||= :html
34
+ "#{name}.#{opts[:format]}"
35
+ end
36
+ end
37
+
38
+ extlib_inheritable_accessor :_handle_exception
39
+
40
+ push_paths(:views, ["app/views", "views"], "**/*")
41
+
42
+ DEFAULT_EXCEPTION_HANDLER = lambda do |error|
43
+ if layout = env['layout']
44
+ layout.content = render :error, :error => error
45
+ layout
46
+ else
47
+ render :error, :error => error
48
+ end
49
+ end unless defined?(DEFAULT_EXCEPTION_HANDLER)
50
+
51
+ # @api private
52
+ def self.call(env)
53
+ app = new(env)
54
+ app.dispatch!
55
+ end
56
+
57
+ def layout
58
+ env['layout']
59
+ end
60
+
61
+ # @api public
62
+ attr_accessor :status
63
+
64
+ def initialize(env)
65
+ @env, @request = env, Rack::Request.new(env)
66
+ @status = 200
67
+ end
68
+
69
+ # Provides access to the request params
70
+ # @api public
71
+ def params
72
+ request.params
73
+ end
74
+
75
+ attr_writer :action
76
+ def action
77
+ @action ||= begin
78
+ rr = request.env['router.response']
79
+ action = rr && rr.dest && rr.dest[:action]
80
+ end
81
+ end
82
+
83
+ # Dispatches to an action based on the params["action"] parameter
84
+ def dispatch!
85
+ if logger
86
+ logger.info "Request: #{request.path}"
87
+ logger.info "Params: #{params.inspect}"
88
+ end
89
+
90
+
91
+ # Check that the action is available
92
+ raise Pancake::Errors::NotFound, "No Action Found" unless allowed_action?(action)
93
+
94
+ @action_opts = actions[action]
95
+
96
+ negotiate_content_type!(@action_opts.formats, params)
97
+
98
+ # Set the layout defaults before the action is rendered
99
+ if layout && stack_class.default_layout
100
+ layout.template_name = stack_class.default_layout
101
+ end
102
+
103
+ layout.format = params['format'] if layout
104
+
105
+ logger.info "Dispatching to #{action.inspect}" if logger
106
+
107
+ result = catch(:halt){ self.send(action) }
108
+
109
+ case result
110
+ when Array
111
+ result
112
+ when Rack::Response
113
+ result.finish
114
+ when String
115
+ out = if layout
116
+ layout.content = result
117
+ layout
118
+ else
119
+ result
120
+ end
121
+ Rack::Response.new(out, status, headers).finish
122
+ else
123
+ Rack::Response.new((result || ""), status, headers).finish
124
+ end
125
+
126
+ rescue Pancake::Errors::HttpError => e
127
+ if logger && log_http_error?(e)
128
+ logger.error "Exception: #{e.message}"
129
+ logger.error e.backtrace.join("\n")
130
+ end
131
+ handle_request_exception(e)
132
+ rescue Exception => e
133
+ if Pancake.handle_errors?
134
+ server_error = Pancake::Errors::Server.new(e.message)
135
+ server_error.exceptions << e
136
+ server_error.set_backtrace e.backtrace
137
+ else
138
+ server_error = e
139
+ end
140
+ handle_request_exception(server_error)
141
+ end
142
+
143
+ def log_http_error?(error)
144
+ true
145
+ end
146
+
147
+ def self.handle_exception(&block)
148
+ if block_given?
149
+ self._handle_exception = block
150
+ else
151
+ self._handle_exception || DEFAULT_EXCEPTION_HANDLER
152
+ end
153
+ end
154
+
155
+ def handle_request_exception(error)
156
+ raise(error.class, error.message, error.backtrace) unless Pancake.handle_errors?
157
+ self.status = error.code
158
+ result = instance_exec error, &self.class.handle_exception
159
+ Rack::Response.new(result, status, headers).finish
160
+ end
161
+
162
+ private
163
+ def allowed_action?(action)
164
+ self.class.actions.include?(action.to_s)
165
+ end
166
+
167
+ public
168
+ def self.roots
169
+ stack_class.roots
170
+ end
171
+
172
+ def self._template_name_for(name, opts)
173
+ opts[:format] ||= :html
174
+ [
175
+ "#{name}.#{opts[:format]}",
176
+ "#{name}"
177
+ ]
178
+ end
179
+
180
+ def _tempate_name_for(name, opts = {})
181
+ opts[:format] ||= content_type
182
+ self.class._template_name_for(name, opts)
183
+ end
184
+ end # Controller
185
+ end # Short