pancake 0.1.29 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/README.textile +0 -4
  2. data/Rakefile +1 -32
  3. data/TODO.textile +19 -0
  4. data/bin/pancake-gen +1 -0
  5. data/lib/pancake.rb +26 -39
  6. data/lib/pancake/bootloaders.rb +2 -2
  7. data/lib/pancake/configuration.rb +1 -1
  8. data/lib/pancake/core_ext/class.rb +3 -3
  9. data/lib/pancake/errors.rb +6 -8
  10. data/lib/pancake/generators.rb +1 -0
  11. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/environments/development.rb.tt +0 -3
  12. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/environments/production.rb.tt +0 -3
  13. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/environments/staging.rb.tt +0 -3
  14. data/lib/pancake/hooks/inheritable_inner_classes.rb +9 -10
  15. data/lib/pancake/hooks/on_inherit.rb +7 -7
  16. data/lib/pancake/logger.rb +1 -14
  17. data/lib/pancake/master.rb +7 -45
  18. data/lib/pancake/middleware.rb +11 -64
  19. data/lib/pancake/mixins/publish.rb +20 -20
  20. data/lib/pancake/mixins/render.rb +59 -69
  21. data/lib/pancake/mixins/render/template.rb +1 -1
  22. data/lib/pancake/mixins/render/view_context.rb +143 -7
  23. data/lib/pancake/mixins/request_helper.rb +38 -1
  24. data/lib/pancake/mixins/stack_helper.rb +2 -2
  25. data/lib/pancake/paths.rb +1 -1
  26. data/lib/pancake/router.rb +10 -2
  27. data/lib/pancake/stack/bootloader.rb +12 -1
  28. data/lib/pancake/stack/router.rb +2 -1
  29. data/lib/pancake/stack/stack.rb +60 -2
  30. data/lib/pancake/stacks/short/controller.rb +58 -3
  31. data/lib/pancake/stacks/short/default/views/base.html.haml +1 -1
  32. data/lib/pancake/stacks/short/default/views/error.html.haml +12 -0
  33. data/lib/pancake/stacks/short/stack.rb +1 -0
  34. data/spec/pancake/fixtures/render_templates/alternate.foo_env.html.haml +1 -0
  35. data/spec/pancake/fixtures/render_templates/alternate.html.haml +1 -0
  36. data/spec/pancake/fixtures/render_templates/view_context/capture_erb.erb +1 -1
  37. data/spec/pancake/fixtures/render_templates/view_context/capture_haml.haml +1 -1
  38. data/spec/pancake/fixtures/render_templates/view_context/concat_erb.erb +1 -1
  39. data/spec/pancake/fixtures/render_templates/view_context/concat_haml.haml +2 -1
  40. data/spec/pancake/middleware_spec.rb +4 -26
  41. data/spec/pancake/mixins/render/view_context_spec.rb +15 -23
  42. data/spec/pancake/mixins/render_spec.rb +54 -0
  43. data/spec/pancake/pancake_spec.rb +0 -22
  44. data/spec/spec.opts +2 -0
  45. data/spec/spec_helper.rb +1 -0
  46. metadata +193 -108
  47. data/TODO +0 -7
  48. data/bin/jeweler +0 -19
  49. data/lib/pancake/mixins/render/render.rb +0 -197
@@ -14,7 +14,7 @@ module Pancake
14
14
  @renderer = Tilt.new(path)
15
15
  end
16
16
 
17
- def render(context = Object.new, opts = {})
17
+ def render(context = ViewContext.new, opts = {})
18
18
  @renderer.render(context, opts)
19
19
  end
20
20
  end #Template
@@ -1,20 +1,156 @@
1
- require 'pancake/mixins/render/render.rb'
1
+ require 'any_view'
2
2
  module Pancake
3
3
  module Mixins
4
4
  module Render
5
5
  class ViewContext
6
6
  # These are included as modules not for modularization, but because super can be called for the module versions
7
- include Capture
8
- include Renderer
9
- include ContentInheritance
7
+ include ::Tilt::CompileSite
8
+ include ::AnyView::TiltBase
9
+
10
+ class << self
11
+ def capture_method_for(item)
12
+ key = case item
13
+ when Template
14
+ item.renderer.class
15
+ when Tilt::Template
16
+ item
17
+ end
18
+ AnyView::TiltBase.capture_methods[key]
19
+ end
20
+
21
+ def concat_method_for(item)
22
+ key = case item
23
+ when Template
24
+ item.renderer.class
25
+ when Tilt::Template
26
+ item
27
+ end
28
+ AnyView::TiltBase.concat_methods[key]
29
+ end
30
+ end
10
31
 
11
32
  attr_reader :_view_context_for
12
- def initialize(env, renderer_for = nil )
13
- super
14
- @env = env
33
+
34
+ def initialize(renderer_for = nil, opts = {})
35
+ opts.keys.each do |k,v|
36
+ instance_variable_set("@#{k}", v)
37
+ end
38
+ @_inherit_helper = InheritanceHelper.new
15
39
  @_view_context_for = renderer_for
16
40
  end
17
41
 
42
+ def inherits_from(ntos, name_or_opts = nil, opts = {})
43
+ name_or_template = case ntos
44
+ when String, Symbol
45
+ if ntos == :default!
46
+ begin
47
+ if @format
48
+ Pancake.default_base_template(:format => @format)
49
+ else
50
+ Pancake.default_base_template
51
+ end
52
+ rescue
53
+ :base
54
+ end
55
+ else
56
+ ntos
57
+ end
58
+ when Pancake::Mixins::Render::Template
59
+ ntos
60
+ else
61
+ if name_or_opts.kind_of?(Hash)
62
+ opts = name_or_opts
63
+ name_or_opts = nil
64
+ end
65
+ name_or_opts ||= ntos.base_template_name
66
+ ntos.template(name_or_opts, opts)
67
+ end
68
+ @_inherit_helper.inherits_from = name_or_template
69
+ end
70
+
71
+ def content_block(label = nil, &block)
72
+ return self if label.nil?
73
+ current_label = @_inherit_helper.current_label
74
+ @_inherit_helper.current_label = label
75
+ capture_method = ViewContext.capture_method_for(_current_renderer)
76
+
77
+ @_inherit_helper.blocks[label] << [block, capture_method]
78
+ if @_inherit_helper.inherits_from.nil?
79
+ result = _capture_content_block(label)
80
+ send(ViewContext.concat_method_for(_current_renderer), result)
81
+ end
82
+ @_inherit_helper.current_label = current_label
83
+ end
84
+
85
+ def super
86
+ @_inherit_helper.increment_super!
87
+ result = _capture_content_block(@_inherit_helper.current_label)
88
+ @_inherit_helper.decrement_super!
89
+ result
90
+ end
91
+
92
+ def render(template, opts = {}, &blk)
93
+ template = _view_context_for.template(template)
94
+ raise TemplateNotFound unless template
95
+ result = _with_renderer template do
96
+ _current_renderer.render(self, opts, &blk) # only include the block once
97
+ end
98
+
99
+ if @_inherit_helper.inherits_from
100
+ next_template = template.owner.template(@_inherit_helper.inherits_from)
101
+ @_inherit_helper.inherits_from = nil
102
+ result = _with_renderer next_template do
103
+ render(next_template, opts)
104
+ end
105
+ end
106
+ result
107
+ end
108
+
109
+ def partial(*args)
110
+ _view_context_for.partial(*args)
111
+ end
112
+
113
+ def _with_renderer(renderer)
114
+ orig_renderer = @_current_renderer
115
+ @_current_renderer = renderer
116
+ result = yield
117
+ @_current_renderer = orig_renderer
118
+ result
119
+ end
120
+
121
+ def _current_renderer
122
+ @_current_renderer
123
+ end
124
+
125
+ private
126
+ def _capture_content_block(label)
127
+ blk, meth = @_inherit_helper.block_for(label)
128
+ send(meth, &blk)
129
+ end
130
+
131
+
132
+ class InheritanceHelper
133
+ attr_accessor :inherits_from, :current_label
134
+ attr_reader :blocks, :super_index
135
+
136
+ def initialize
137
+ @blocks = Hash.new{|h,k| h[k] = []}
138
+ @super_index = 0
139
+ end
140
+
141
+ def block_for(label)
142
+ @blocks[label][@super_index]
143
+ end
144
+
145
+ def increment_super!
146
+ @super_index += 1
147
+ end
148
+
149
+ def decrement_super!
150
+ @super_index -= 1
151
+ end
152
+
153
+ end
18
154
  end
19
155
  end
20
156
  end
@@ -3,7 +3,7 @@ module Pancake
3
3
  # Some helpers for requests that come in handy for applications that
4
4
  # are part of stacks
5
5
  module RequestHelper
6
- VARS_KEY = 'pancake.request.vars'
6
+ VARS_KEY = 'request.variables'
7
7
 
8
8
  # A data area that allows you to carry data accross middlewares, controller / views etc.
9
9
  # Stores the data in session for the length of the request.
@@ -125,6 +125,43 @@ module Pancake
125
125
  def logger
126
126
  env[Pancake::Constants::ENV_LOGGER_KEY]
127
127
  end
128
+
129
+ def negotiate_content_type!(*allowed_types)
130
+ return content_type if content_type
131
+
132
+ allowed_types = allowed_types.flatten
133
+ opts = allowed_types.pop if allowed_types.last.kind_of?(Hash)
134
+ opts ||= {}
135
+ if opts[:format]
136
+ cont, ct, mt = Pancake::MimeTypes.negotiate_by_extension(opts[:format].to_s, allowed_types)
137
+ else
138
+ env["HTTP_ACCEPT"] ||= "*/*"
139
+ cont, ct, mt = Pancake::MimeTypes.negotiate_accept_type(env["HTTP_ACCEPT"], allowed_types)
140
+ end
141
+
142
+ raise Errors::NotAcceptable unless cont
143
+
144
+ headers["Content-Type"] = ct
145
+ self.mime_type = mt
146
+ self.content_type = cont
147
+ cont
148
+ end
149
+
150
+ def content_type
151
+ env['pancake.request.format']
152
+ end
153
+
154
+ def content_type=(format)
155
+ env['pancake.request.format'] = format
156
+ end
157
+
158
+ def mime_type
159
+ env['pancake.request.mime']
160
+ end
161
+
162
+ def mime_type=(mime)
163
+ env['pancake.request.mime'] = mime
164
+ end
128
165
  end # RequestHelper
129
166
  end # Mixins
130
167
  end # Pancake
@@ -2,7 +2,7 @@ module Pancake
2
2
  module Mixins
3
3
  module StackHelper
4
4
  def self.included(base)
5
- base.class_inheritable_accessor :_stack_class
5
+ base.extlib_inheritable_accessor :_stack_class
6
6
  base.extend ClassMethods
7
7
  base.class_eval do
8
8
  include ::Pancake::Mixins::StackHelper::InstanceMethods
@@ -19,7 +19,7 @@ module Pancake
19
19
  end
20
20
  ns = name.split("::")
21
21
  until ns.empty? || klass
22
- r = Object.full_const_get(ns.join("::"))
22
+ r = ns.join("::").constantize
23
23
  if r.ancestors.include?(::Pancake::Stack)
24
24
  klass = r
25
25
  else
@@ -41,7 +41,7 @@ module Pancake
41
41
  def self.extended(base) #:nodoc:#
42
42
  base.class_eval do
43
43
  deep_copy_class_inheritable_reader :_load_paths, :roots
44
- @_load_paths = Dictionary.new
44
+ @_load_paths = ActiveSupport::OrderedHash.new
45
45
  @roots = []
46
46
  end
47
47
  end
@@ -38,14 +38,16 @@ module Pancake
38
38
  # @since 0.1.2
39
39
  # @author Daniel Neighman
40
40
  class Router < Usher::Interface::Rack
41
- attr_writer :router
41
+ attr_writer :router
42
+ attr_accessor :stack
42
43
 
43
44
  CONFIGURATION_KEY = "pancake.request.configuration".freeze
44
45
  ROUTE_KEY = "pancake.request.matching_route".freeze
46
+ LAYOUT_KEY = "pancake.apply_layout".freeze
45
47
 
46
48
  class RackApplicationExpected < ArgumentError; end
47
49
  attr_accessor :configuration
48
- class_inheritable_accessor :mounted_applications
50
+ extlib_inheritable_accessor :mounted_applications
49
51
  self.mounted_applications = []
50
52
 
51
53
  class MountedApplication
@@ -141,13 +143,18 @@ module Pancake
141
143
  end
142
144
 
143
145
  def call(env)
146
+ apply_layout = env[LAYOUT_KEY]
147
+ env[LAYOUT_KEY] = true if stack.use_layout?
148
+
144
149
  orig_config = env[CONFIGURATION_KEY]
145
150
  orig_route = env[ROUTE_KEY]
146
151
  env[CONFIGURATION_KEY] = configuration
152
+
147
153
  super(env)
148
154
  ensure
149
155
  env[CONFIGURATION_KEY] = orig_config
150
156
  env[ROUTE_KEY] = orig_route
157
+ env[LAYOUT_KEY] = apply_layout
151
158
  end
152
159
 
153
160
  private
@@ -166,6 +173,7 @@ module Pancake
166
173
  # the usher.params into the rack request.params
167
174
  # @api private
168
175
  def after_match(request, response)
176
+ request.env['rack.request.query_hash'] = request.params.dup
169
177
  super
170
178
  consume_path!(request, response) if !response.partial_match? && response.path.route.consuming
171
179
  request.params.merge!(request.env['usher.params']) unless request.env['usher.params'].empty?
@@ -29,6 +29,11 @@ Pancake::Stack::BootLoader.add(:load_mounted_inits, :level => :init) do
29
29
  end
30
30
  end
31
31
 
32
+ Pancake::Stack::BootLoader.add(:before_stack_loads) do
33
+ def run!
34
+ stack_class.before_stack_loads.each{|blk| blk.call}
35
+ end
36
+ end
32
37
 
33
38
  Pancake::Stack::BootLoader.add(:load_application, :level => :init) do
34
39
  def run!
@@ -61,6 +66,12 @@ Pancake::Stack::BootLoader.add(:before_mount_applicaions) do
61
66
  end
62
67
  end
63
68
 
69
+ Pancake::Stack::BootLoader.add(:load_middlewares) do
70
+ def run!
71
+ stack_class.paths_for(:middlewares).each{|f| require f.join}
72
+ end
73
+ end
74
+
64
75
  Pancake::Stack::BootLoader.add(:mount_applications) do
65
76
  def run!
66
77
  stack_class.router.mount_applications!
@@ -87,7 +98,7 @@ end
87
98
 
88
99
  Pancake::Stack::BootLoader.add(:build_stack) do
89
100
  def run!
90
- mwares = stack_class.middlewares(Pancake.stack_labels)
101
+ mwares = stack_class.middlewares
91
102
  app = Pancake::Middleware.build(config[:app], mwares)
92
103
  app_config = Pancake.configuration.configs(config[:app_name])
93
104
  app_config.app = app
@@ -12,9 +12,11 @@ module Pancake
12
12
  unless self == Pancake::Stack
13
13
  r.router = superclass._router.router.dup
14
14
  end
15
+ r.stack = self
15
16
  r
16
17
  end
17
18
  end
19
+
18
20
  # Resets the router to use the stacks namespaced router.
19
21
  # This allows a router to mixin a module, and have that module
20
22
  # mixed in to child stacks/routers. Effectively, this will reset the scope of inheritance so that a stack type can have particular route helpers
@@ -37,7 +39,6 @@ module Pancake
37
39
  yield router if block_given?
38
40
  router
39
41
  end
40
-
41
42
  end
42
43
  end
43
44
 
@@ -16,6 +16,7 @@ module Pancake
16
16
  push_paths(:rake_tasks, "tasks", "**/*.rake")
17
17
  push_paths(:public, "public", "**/*")
18
18
  push_paths(:mounts, "mounts", "*/pancake_init.rb")
19
+ push_paths(:middlewares, "config/middlewares", "**/*.rb")
19
20
 
20
21
 
21
22
  #Iterates the list of roots in the stack, and initializes the app found their
@@ -135,6 +136,56 @@ module Pancake
135
136
  end
136
137
  end
137
138
 
139
+ # Specify a layout to use for this stack and all children stacks
140
+ # Layouts are provided by Wrapt, and Pancake::Stacks::Short will use the layout when directed to by calling this method. Simply call it to activate layouts with the default options, or provide it with options to direct wrapt.
141
+ #
142
+ # You can specify a layout to use by name, provide it options or provide a block that you can use to set the layout via the environment.
143
+ #
144
+ # @params name [String] (optional) The name of the layout to use
145
+ # @params opts [Hash] Options for use with Wrapt
146
+ #
147
+ # @block The block is used to determine at runtime which layout to use. It is yielded the environment where appropriate action can be taken. It assumes that you will set the layout object directly
148
+ #
149
+ # @example
150
+ # # Direct the short stack to use a layout
151
+ # layout
152
+ #
153
+ # # Set the layout to a non-default one
154
+ # layout "non_standard"
155
+ #
156
+ # # Directly set the layout via the Wrapt::Layout object
157
+ # layout do |env|
158
+ # r = Rack::Request.new(env)
159
+ # l = env['layout']
160
+ # if r.params['foo'] == 'bar'
161
+ # l.template_name = "foo"
162
+ # end
163
+ # end
164
+ #
165
+ # @see Wrapt
166
+ # @see Wrapt::Layout
167
+ # @api public
168
+ def self.apply_layout(name = nil, &blk)
169
+ @use_layout = true
170
+ @default_layout = name
171
+ end
172
+
173
+ def self.use_layout?
174
+ if @use_layout
175
+ @use_layout
176
+ else
177
+ @use_layout = superclass.respond_to?(:use_layout?) && superclass.use_layout?
178
+ end
179
+ end
180
+
181
+ def self.default_layout
182
+ if @default_layout
183
+ @default_layout
184
+ else
185
+ @default_layout = superclass.respond_to?(:default_layout) && superclass.default_layout
186
+ end
187
+ end
188
+
138
189
  # Sets this as a master stack. This means that the "master" directory will be added to all existing stack roots
139
190
  def self.set_as_master!
140
191
  Pancake.master_stack ||= self
@@ -173,7 +224,7 @@ module Pancake
173
224
  # @api public
174
225
  def self.create_bootloader_hook(*hooks)
175
226
  hooks.each do |hook|
176
- class_inheritable_reader "_#{hook}"
227
+ extlib_inheritable_reader "_#{hook}"
177
228
  instance_variable_set("@_#{hook}", [])
178
229
 
179
230
  class_eval <<-RUBY
@@ -185,6 +236,13 @@ module Pancake
185
236
  end
186
237
  end
187
238
 
188
- create_bootloader_hook :before_build_stack, :before_mount_applications, :after_initialize_application, :after_build_stack
239
+ create_bootloader_hook :before_build_stack, :before_mount_applications, :after_initialize_application, :after_build_stack, :before_stack_loads
189
240
  end # Stack
190
241
  end # Pancake
242
+
243
+ require 'pancake/stack/configuration'
244
+ require 'pancake/stack/router'
245
+ require 'pancake/stack/bootloader'
246
+ require 'pancake/stack/app'
247
+ require 'pancake/defaults/middlewares'
248
+ require 'pancake/defaults/configuration'
@@ -2,7 +2,6 @@ module Pancake
2
2
  module Stacks
3
3
  class Short
4
4
  inheritable_inner_classes :Controller
5
-
6
5
  class Controller
7
6
  extend Mixins::Publish
8
7
  include Mixins::Render
@@ -10,12 +9,44 @@ module Pancake
10
9
  include Mixins::ResponseHelper
11
10
  include Mixins::StackHelper
12
11
 
13
- class_inheritable_accessor :_handle_exception
12
+ class self::ViewContext
13
+ include Mixins::RequestHelper
14
+ include AnyView
15
+
16
+ # No way to get the env into the view context... this is not good :(
17
+ def env
18
+ _view_context_for.env
19
+ end
20
+
21
+ def self.template(name_or_template, opts = {})
22
+ opts[:format] ||= content_type
23
+ super
24
+ end
25
+
26
+ def template(name_or_template, opts={})
27
+ opts[:format] ||= content_type
28
+ super
29
+ end
30
+
31
+ def _template_name_for(name, opts = {})
32
+ opts[:format] ||= :html
33
+ "#{name}.#{opts[:format]}"
34
+ end
35
+ end
36
+
37
+ extlib_inheritable_accessor :_handle_exception
14
38
 
15
39
  push_paths(:views, ["app/views", "views"], "**/*")
16
40
 
17
41
  DEFAULT_EXCEPTION_HANDLER = lambda do |error|
18
- "#{error.name}: #{error.description}"
42
+ use_layout = env[Router::LAYOUT_KEY]
43
+ if use_layout
44
+ layout = env['layout']
45
+ layout.content = render :error, :error => error
46
+ layout
47
+ else
48
+ render :error, :error => error
49
+ end
19
50
  end unless defined?(DEFAULT_EXCEPTION_HANDLER)
20
51
 
21
52
  # @api private
@@ -24,6 +55,10 @@ module Pancake
24
55
  app.dispatch!
25
56
  end
26
57
 
58
+ def layout
59
+ env['layout']
60
+ end
61
+
27
62
  # @api public
28
63
  attr_accessor :status
29
64
 
@@ -40,6 +75,7 @@ module Pancake
40
75
 
41
76
  # Dispatches to an action based on the params["action"] parameter
42
77
  def dispatch!
78
+
43
79
  params["action"] ||= params[:action]
44
80
  params[:format] ||= params["format"]
45
81
 
@@ -55,14 +91,33 @@ module Pancake
55
91
 
56
92
  negotiate_content_type!(@action_opts.formats, params)
57
93
 
94
+ # Setup the layout
95
+ use_layout = env[Router::LAYOUT_KEY]
96
+ layout = env['layout']
97
+
98
+ # Set the layout defaults before the action is rendered
99
+ if use_layout && stack_class.default_layout
100
+ layout.template_name = stack_class.default_layout
101
+ end
102
+ layout.format = params['format'] if use_layout
103
+
58
104
  logger.info "Dispatching to #{params["action"].inspect}" if logger
59
105
 
60
106
  result = catch(:halt){ self.send(params['action']) }
107
+
61
108
  case result
62
109
  when Array
63
110
  result
64
111
  when Rack::Response
65
112
  result.finish
113
+ when String
114
+ out = if use_layout
115
+ layout.content = result
116
+ layout
117
+ else
118
+ result
119
+ end
120
+ Rack::Response.new(out, status, headers).finish
66
121
  else
67
122
  Rack::Response.new((result || ""), status, headers).finish
68
123
  end