short_stack 0.1.1

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 (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