orange-core 0.5.3

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 (72) hide show
  1. data/README.markdown +145 -0
  2. data/lib/orange-core.rb +8 -0
  3. data/lib/orange-core/application.rb +132 -0
  4. data/lib/orange-core/assets/css/exceptions.css +50 -0
  5. data/lib/orange-core/assets/js/exceptions.js +44 -0
  6. data/lib/orange-core/carton.rb +178 -0
  7. data/lib/orange-core/core.rb +266 -0
  8. data/lib/orange-core/magick.rb +270 -0
  9. data/lib/orange-core/middleware/base.rb +96 -0
  10. data/lib/orange-core/middleware/database.rb +45 -0
  11. data/lib/orange-core/middleware/four_oh_four.rb +45 -0
  12. data/lib/orange-core/middleware/globals.rb +17 -0
  13. data/lib/orange-core/middleware/loader.rb +13 -0
  14. data/lib/orange-core/middleware/rerouter.rb +53 -0
  15. data/lib/orange-core/middleware/restful_router.rb +99 -0
  16. data/lib/orange-core/middleware/route_context.rb +39 -0
  17. data/lib/orange-core/middleware/route_site.rb +51 -0
  18. data/lib/orange-core/middleware/show_exceptions.rb +80 -0
  19. data/lib/orange-core/middleware/static.rb +67 -0
  20. data/lib/orange-core/middleware/static_file.rb +32 -0
  21. data/lib/orange-core/middleware/template.rb +60 -0
  22. data/lib/orange-core/packet.rb +232 -0
  23. data/lib/orange-core/plugin.rb +172 -0
  24. data/lib/orange-core/resource.rb +96 -0
  25. data/lib/orange-core/resources/mapper.rb +36 -0
  26. data/lib/orange-core/resources/model_resource.rb +228 -0
  27. data/lib/orange-core/resources/not_found.rb +10 -0
  28. data/lib/orange-core/resources/page_parts.rb +68 -0
  29. data/lib/orange-core/resources/parser.rb +113 -0
  30. data/lib/orange-core/resources/routable_resource.rb +16 -0
  31. data/lib/orange-core/resources/scaffold.rb +106 -0
  32. data/lib/orange-core/stack.rb +226 -0
  33. data/lib/orange-core/templates/exceptions.haml +111 -0
  34. data/lib/orange-core/views/default_resource/create.haml +4 -0
  35. data/lib/orange-core/views/default_resource/edit.haml +9 -0
  36. data/lib/orange-core/views/default_resource/list.haml +10 -0
  37. data/lib/orange-core/views/default_resource/show.haml +4 -0
  38. data/lib/orange-core/views/default_resource/table_row.haml +7 -0
  39. data/lib/orange-core/views/not_found/404.haml +2 -0
  40. data/spec/orange-core/application_spec.rb +183 -0
  41. data/spec/orange-core/carton_spec.rb +136 -0
  42. data/spec/orange-core/core_spec.rb +248 -0
  43. data/spec/orange-core/magick_spec.rb +96 -0
  44. data/spec/orange-core/middleware/base_spec.rb +38 -0
  45. data/spec/orange-core/middleware/globals_spec.rb +3 -0
  46. data/spec/orange-core/middleware/rerouter_spec.rb +3 -0
  47. data/spec/orange-core/middleware/restful_router_spec.rb +3 -0
  48. data/spec/orange-core/middleware/route_context_spec.rb +3 -0
  49. data/spec/orange-core/middleware/route_site_spec.rb +3 -0
  50. data/spec/orange-core/middleware/show_exceptions_spec.rb +3 -0
  51. data/spec/orange-core/middleware/static_file_spec.rb +3 -0
  52. data/spec/orange-core/middleware/static_spec.rb +3 -0
  53. data/spec/orange-core/mock/mock_app.rb +16 -0
  54. data/spec/orange-core/mock/mock_carton.rb +43 -0
  55. data/spec/orange-core/mock/mock_core.rb +2 -0
  56. data/spec/orange-core/mock/mock_middleware.rb +25 -0
  57. data/spec/orange-core/mock/mock_mixins.rb +19 -0
  58. data/spec/orange-core/mock/mock_model_resource.rb +47 -0
  59. data/spec/orange-core/mock/mock_pulp.rb +24 -0
  60. data/spec/orange-core/mock/mock_resource.rb +26 -0
  61. data/spec/orange-core/mock/mock_router.rb +10 -0
  62. data/spec/orange-core/orange_spec.rb +19 -0
  63. data/spec/orange-core/packet_spec.rb +203 -0
  64. data/spec/orange-core/resource_spec.rb +96 -0
  65. data/spec/orange-core/resources/mapper_spec.rb +5 -0
  66. data/spec/orange-core/resources/model_resource_spec.rb +246 -0
  67. data/spec/orange-core/resources/parser_spec.rb +5 -0
  68. data/spec/orange-core/resources/routable_resource_spec.rb +5 -0
  69. data/spec/orange-core/spec_helper.rb +53 -0
  70. data/spec/orange-core/stack_spec.rb +232 -0
  71. data/spec/stats.rb +182 -0
  72. metadata +227 -0
@@ -0,0 +1,10 @@
1
+ require 'orange-core/resource'
2
+ module Orange
3
+ class NotFound < Orange::Resource
4
+ call_me :not_found
5
+ def route(packet)
6
+ packet[:content] = orange[:parser].haml("404.haml", packet, :resource => self)
7
+ packet[:status] = 404
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,68 @@
1
+ require 'orange-core/resource'
2
+ module Orange
3
+ class PageParts < Resource
4
+ def afterLoad
5
+ orange.add_pulp Orange::Pulp::PageParts
6
+ end
7
+
8
+ def part(packet)
9
+ unless packet[:page_parts, false]
10
+ packet[:page_parts] = DefaultHash.new
11
+ packet[:page_parts].default = ''
12
+ end
13
+ packet[:page_parts]
14
+ end
15
+
16
+
17
+ def add_css(packet, file, opts = {})
18
+ ie = opts[:ie] || false
19
+ mod = opts[:module] || 'public'
20
+ # module set to false gives the root assets dir
21
+ assets = File.join('assets', mod)
22
+ file = File.join('', assets, 'css', file)
23
+ unless packet[:css_files, []].include?(file)
24
+ if ie
25
+ part(packet)[:ie_css] = part(packet)[:ie_css] + "<link rel=\"stylesheet\" href=\"#{file}\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />"
26
+ else
27
+ part(packet)[:css] = part(packet)[:css] + "<link rel=\"stylesheet\" href=\"#{file}\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />"
28
+ end
29
+ packet[:css_files] ||= []
30
+ packet[:css_files].insert((opts[:position] ? opts[:position] : -1), file)
31
+ end
32
+ end
33
+
34
+ def add_js(packet, file, opts = {})
35
+ ie = opts[:ie] || false
36
+ mod = opts[:module] || 'public'
37
+ assets = File.join('assets', mod)
38
+ file = File.join('', assets, 'js', file)
39
+ unless packet[:js_files, []].include?(file)
40
+ if ie
41
+ part(packet)[:ie_js] = part(packet)[:ie_js] + "<script src=\"#{file}\" type=\"text/javascript\"></script>"
42
+ else
43
+ part(packet)[:js] = part(packet)[:js] + "<script src=\"#{file}\" type=\"text/javascript\"></script>"
44
+ end
45
+ packet[:js_files] ||= []
46
+ packet[:js_files].insert((opts[:position] ? opts[:position] : -1), file)
47
+ end
48
+ end
49
+ end
50
+
51
+ module Pulp::PageParts
52
+ def part
53
+ orange[:page_parts].part(packet)
54
+ end
55
+
56
+ # Feels like part should be plural, no?
57
+ def parts; part; end
58
+
59
+
60
+ def add_css(file, opts = {})
61
+ orange[:page_parts].add_css(packet, file, opts)
62
+ end
63
+
64
+ def add_js(file, opts = {})
65
+ orange[:page_parts].add_js(packet, file, opts)
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,113 @@
1
+ require 'orange-core/core'
2
+ require 'haml'
3
+ require 'yaml'
4
+ require 'crack'
5
+
6
+ module Orange
7
+ class Parser < Resource
8
+ def afterLoad
9
+ orange.add_pulp Orange::Pulp::ParserPulp
10
+ @template_dirs = [File.join(orange.core_dir, 'templates')]
11
+ @view_dirs = [File.join(orange.core_dir, 'views')]
12
+ Orange.plugins.each{|p| @template_dirs << p.templates if p.has_templates? }
13
+ Orange.plugins.each{|p| @view_dirs << p.views if p.has_views? }
14
+ end
15
+
16
+ def yaml(file)
17
+ return nil unless File.exists?(file)
18
+ string = File.read(file)
19
+ string.gsub!('__ORANGE__', orange.app_dir)
20
+ out = YAML::load(string)
21
+ end
22
+
23
+ def haml(file, packet_binding, *vars, &block)
24
+
25
+ opts = vars.extract_options!
26
+ # Initial info
27
+ temp = opts.delete(:template)
28
+ opts[:resource_name] = opts[:resource].orange_name.to_s if
29
+ opts[:resource] && opts[:resource].respond_to?(:orange_name)
30
+ resource = (opts[:resource_name] || '').downcase
31
+
32
+ if packet_binding.is_a? Orange::Packet
33
+ context = packet_binding['route.context'].to_s
34
+ unless temp
35
+ packet_binding['parser.haml-templates'] ||= {}
36
+ haml_engine = packet_binding['parser.haml-templates']["#{context}-#{resource}-#{file}"] || false
37
+ end
38
+ end
39
+ unless haml_engine
40
+ opts.merge :orange => orange
41
+
42
+ string = false
43
+ if temp
44
+ string ||= read_if_exists('templates', file)
45
+ @template_dirs.each do |templates_dir|
46
+ string ||= read_if_exists(templates_dir, file)
47
+ end unless string
48
+ end
49
+
50
+ if context
51
+ #Check for context specific overrides
52
+ string ||= read_if_exists('views', resource, context+"."+file) if resource
53
+ string ||= read_if_exists('views', context+"."+file)
54
+ @view_dirs.each do |views_dir|
55
+ string ||= read_if_exists(views_dir, resource, context+"."+file) if resource
56
+ string ||= read_if_exists(views_dir, context+"."+file)
57
+ end unless string
58
+ end
59
+
60
+ # Check for standard views
61
+ string ||= read_if_exists('views', resource, file) if resource
62
+ string ||= read_if_exists('views', file)
63
+ @view_dirs.each do |views_dir|
64
+ string ||= read_if_exists(views_dir, resource, file) if resource
65
+ string ||= read_if_exists(views_dir, file)
66
+ end unless string
67
+
68
+ # Check for default resource views
69
+ string ||= read_if_exists('views', 'default_resource', file)
70
+ @view_dirs.each do |views_dir|
71
+ string ||= read_if_exists(views_dir, 'default_resource', file) if resource
72
+ end unless string
73
+ raise LoadError, "Couldn't find haml file '#{file}'" unless string
74
+
75
+ haml_engine = Haml::Engine.new(string)
76
+ if packet_binding.is_a? Orange::Packet
77
+ packet_binding['parser.haml-templates']["#{context}-#{resource}-#{file}"] = haml_engine
78
+ end
79
+ end
80
+ out = haml_engine.render(packet_binding, opts, &block)
81
+ end
82
+
83
+ def read_if_exists(*args)
84
+ return File.read(File.join(*args)) if File.exists?(File.join(*args))
85
+ false
86
+ end
87
+
88
+ def hpricot(text)
89
+ require 'hpricot'
90
+ Hpricot(text)
91
+ end
92
+
93
+ def xml(text)
94
+ Crack::XML.parse(text)
95
+ end
96
+
97
+ def json(text)
98
+ Crack::JSON.parse(text)
99
+ end
100
+ end
101
+
102
+ module Pulp::ParserPulp
103
+ def html(&block)
104
+ if block_given?
105
+ unless(packet[:content].blank?)
106
+ doc = orange[:parser].hpricot(packet[:content])
107
+ yield doc
108
+ packet[:content] = doc.to_s
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,16 @@
1
+ require 'orange-core/resource'
2
+
3
+ module Orange
4
+ class RoutableResource < Resource
5
+ def routable; true; end
6
+
7
+ def route(path, packet)
8
+ parts = path.split('/')
9
+ first = parts[0].respond_to?(:to_sym) ? parts.shift.to_sym : :index
10
+ new_path = parts.join('/')
11
+ if self.respond_to?(first)
12
+ packet[:content] = self.__send__(first, new_path, packet)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,106 @@
1
+ require 'orange-core/core'
2
+
3
+ module Orange
4
+ class Scaffold < Resource
5
+ # Load the scaffold helpers
6
+ def afterLoad
7
+ orange.add_pulp Pulp::ScaffoldHelpers
8
+ Packet.meta_methods(/view_([a-zA-Z_]+)/) do |packet, match, args|
9
+ model = args.shift
10
+ args = args.extract_with_defaults(:mode => match[1].to_sym)
11
+ packet.view(model, args)
12
+ end
13
+ @scaffold_types = {}
14
+ add_scaffold_type(:boolean) do |name, val, opts|
15
+ if opts[:show]
16
+ val ? "true" : "false"
17
+ else
18
+ ret = "<input type='hidden' name='#{opts[:model_name]}[#{name}]' value='0' /><input type='checkbox' name='#{opts[:model_name]}[#{name}]' value='1' #{'checked="checked"' if (val && val != '')}/>"
19
+ ret = "<label for=''>#{opts[:display_name]}</label><br />" + ret if opts[:label]
20
+ end
21
+ end
22
+ end
23
+
24
+ def add_scaffold_type(type, &block)
25
+ @scaffold_types[type] = Proc.new
26
+ end
27
+
28
+ def scaffold_attribute(packet, prop, model_name, *args)
29
+ args = args.extract_options!
30
+ args.with_defaults!({:packet => packet, :value => '', :label => false, :show => false})
31
+ val = args[:value]
32
+ label = args[:label]
33
+ show = args[:show]
34
+ name = prop[:name]
35
+ human_readable_name = name.to_s.split('_').each{|w| w.capitalize!}.join(' ')
36
+ display_name = prop[:display_name] || human_readable_name
37
+ return @scaffold_types[prop[:type]].call(name, val, args.with_defaults!(:display_name => display_name, :model_name => model_name)) if @scaffold_types.has_key?(prop[:type])
38
+ unless show
39
+ case prop[:type]
40
+ when :title
41
+ val.gsub!('"', '&quot;')
42
+ ret = "<input class=\"title\" type=\"text\" value=\"#{val}\" name=\"#{model_name}[#{name}]\" />"
43
+ when :text
44
+ val.gsub!('"', '&quot;')
45
+ ret = "<input type=\"text\" value=\"#{val}\" name=\"#{model_name}[#{name}]\" />"
46
+ when :fulltext
47
+ ret = "<textarea name='#{model_name}[#{name}]'>#{val}</textarea>"
48
+ when :boolean
49
+ human_readable_name = human_readable_name + '?'
50
+ ret = "<input type='hidden' name='#{model_name}[#{name}]' value='0' /><input type='checkbox' name='#{model_name}[#{name}]' value='1' #{'checked="checked"' if (val && val != '')}/>"
51
+ when :date
52
+ val.gsub!('"', '&quot;')
53
+ ret = "<input class=\"date\" type=\"text\" value=\"#{val}\" name=\"#{model_name}[#{name}]\" />"
54
+ else
55
+ val.gsub!('"', '&quot;')
56
+ ret = "<input type=\"text\" value=\"#{val}\" name=\"#{model_name}[#{name}]\" />"
57
+ end
58
+ ret = "<label for=''>#{display_name}</label><br />" + ret if label
59
+ else
60
+ case prop[:type]
61
+ when :title
62
+ ret = "<h3 class='#{model_name}-#{name}'>#{val}</h3>"
63
+ when :text
64
+ ret = "<p class='#{model_name}-#{name}'>#{val}</p>"
65
+ when :fulltext
66
+ ret = "<div class='#{model_name}-#{name}'>#{val}</div>"
67
+ else
68
+ ret = "<div class='#{model_name}-#{name}'>#{val}</div>"
69
+ end
70
+ end
71
+ return ret
72
+ end
73
+ end
74
+
75
+ module Pulp::ScaffoldHelpers
76
+ # Creates a button that appears to be a link but
77
+ # does form submission with custom method (_method param in POST)
78
+ # This is to avoid issues of a destructive get.
79
+ # @param [String] text link text to show
80
+ # @param [String] link the actual href value of the link
81
+ # @param [String, false] confirm text of the javascript confirm (false for none [default])
82
+ # @param [optional, Array] args array of optional arguments, only opts[:method] defined
83
+ # @option opts [String] method method name (Should be 'DELETE', 'PUT' or 'POST')
84
+ def form_link(text, link, confirm = false, opts = {})
85
+ text = "<img src='#{opts[:img]}' alt='#{text}' />" if opts[:img]
86
+ css = opts[:class]? opts[:class] : 'form_button_link'
87
+ meth = (opts[:method]? "<input type='hidden' name='_method' value='#{opts[:method]}' />" : '')
88
+ if confirm
89
+ "<form action='#{link}' method='post' class='mini' onsubmit='return confirm(\"#{confirm}\")'><button class='link_button'><a href='#' class='#{css}'>#{text}</a></button>#{meth}</form>"
90
+ else
91
+ "<form action='#{link}' method='post' class='mini'><button class='link_button'><a href='#' class='#{css}'>#{text}</a></button>#{meth}</form>"
92
+ end
93
+ end
94
+
95
+ # Calls view for an orange resource.
96
+ def view(model_name, *args)
97
+ orange[model_name].view(self, *args)
98
+ end
99
+
100
+ # Returns a scaffolded attribute
101
+ def view_attribute(prop, model_name, *args)
102
+ orange[:scaffold].scaffold_attribute(self, prop, model_name, *args)
103
+ end
104
+ end
105
+
106
+ end
@@ -0,0 +1,226 @@
1
+ require 'orange-core/core'
2
+ require 'rack/builder'
3
+ module Orange
4
+ # Builds an orange stack of middleware
5
+ # Use in the rackup file as follows:
6
+ # app = Orange::Stack.new do
7
+ # stack Orange::DataMapper 'sqlite3::memory:' <= loads orange specific middleware
8
+ # use OtherMiddleware
9
+ # run SomeApp.new
10
+ # end
11
+ # run app
12
+ #
13
+ # All middleware placed inside the Orange::Stack will have access
14
+ # to the Orange Core (as long as it's been written to accept it as the second
15
+ # initialization argument) when added with the 'stack' method
16
+ #
17
+ # In general, Orange::Stack works like Rack::Builder.
18
+ class Stack
19
+
20
+ # Creates a new Orange::Stack out of the passed block.
21
+ #
22
+ # If a block is not passed, it will try to build one from scratch.
23
+ # The bare minimum will be `run app_class.new(@core)`, there are also
24
+ # other stacks that can be used.
25
+ #
26
+ # @param [Orange::Application] app_class the class of the main application
27
+ # @param [Orange::Core] core the orange core
28
+ # @param [Symbol] prebuilt the optional prebuilt stack, if one isn't passed as block
29
+ def initialize(app_class = nil, core = false, prebuilt = :none, &block)
30
+ @build = Rack::Builder.new
31
+ @core = core || Orange::Core.new
32
+ @core.stack = self # Set a back reference in the core.
33
+ @auto_reload = false
34
+ @app = false
35
+ @middleware = []
36
+ @recapture = true
37
+ @main_app = app_class
38
+ if block_given?
39
+ instance_eval(&block)
40
+ else
41
+ @main_app = app_class.new(@core) unless app_class.nil?
42
+ prebuild(prebuilt)
43
+ end
44
+ end
45
+
46
+ # Runs methods necessary to build a stack. Don't use if a stack
47
+ # has already been built by the initialize block.
48
+ #
49
+ # @todo Offer more choices for default stacks
50
+ def prebuild(choice)
51
+ case choice
52
+ when :none
53
+ run @main_app
54
+ else
55
+ run @main_app
56
+ end
57
+ end
58
+
59
+ # Returns the main application instance that was added by the
60
+ # run method. Obviously won't return anything useful if the
61
+ # middleware stack hasn't been set up with an explicit exit point,
62
+ # as could be the case for a pure orange middleware stack on
63
+ # top of a different exit application (like Sinatra or Rails)
64
+ def main_app
65
+ @main_app
66
+ end
67
+
68
+ # Adds middleware using the Rack::Builder#use method
69
+ # @param [Object] middleware A class of middleware that meets rack middleware requirements
70
+ def use(middleware, *args, &block)
71
+ @build.use(middleware, *args, &block)
72
+ end
73
+
74
+ # Loads resources into the core using the Orange::Core#load method
75
+ #
76
+ # all args are passed on
77
+ def load(*args, &block)
78
+ orange.load(*args, &block)
79
+ end
80
+
81
+ # Adds Orange-aware middleware using the Rack::Builder#use method, adding
82
+ # the orange core to the args passed on
83
+ def stack(middleware, *args, &block)
84
+ @build.use(middleware, @core, *args, &block)
85
+ end
86
+
87
+ # Set the auto_reload option, called without args, defaults to true,
88
+ # other option is to set it to false
89
+ def auto_reload!(val = true)
90
+ @auto_reload = val
91
+ end
92
+
93
+ # Shortcut for adding Orange::Middleware::ShowExceptions to the middleware
94
+ # stack
95
+ def use_exceptions
96
+ stack Orange::Middleware::ShowExceptions
97
+ end
98
+
99
+ # Alias for use_exceptions
100
+ def show_exceptions
101
+ use_exceptions
102
+ end
103
+
104
+ # Turn off recapture middleware, which is normally just on top of the exit
105
+ # point
106
+ # @see Orange::Middleware::Recapture
107
+ def no_recapture
108
+ @recapture = false
109
+ end
110
+
111
+ # A shortcut for adding many of the routing middleware options
112
+ # simultaneously. Includes:
113
+ # * Orange::Middleware::Rerouter
114
+ # * Orange::Middleware::Static
115
+ # * Rack::AbstractFormat
116
+ # * Orange::Middleware::RouteSite
117
+ # * Orange::Middleware::RouteContext
118
+ #
119
+ # All of these are passed the args hash to use as they will, except
120
+ # for Rack::AbstractFormat
121
+ #
122
+ def prerouting(*args)
123
+ opts = args.extract_options!
124
+ stack Orange::Middleware::Globals
125
+ stack Orange::Middleware::Loader
126
+ stack Orange::Middleware::Rerouter, opts.dup
127
+ stack Orange::Middleware::Static, opts.dup
128
+ use Rack::AbstractFormat unless opts[:no_abstract_format]
129
+ # Must be used before non-destructive route altering done by Orange,
130
+ # since all orange stuff is non-destructive
131
+ stack Orange::Middleware::RouteSite, opts.dup
132
+ stack Orange::Middleware::RouteContext, opts.dup
133
+ stack Orange::Middleware::Database
134
+ Orange.plugins.each{|p| p.middleware(:prerouting).each{|m| stack m, opts.dup} if p.has_middleware?}
135
+ end
136
+
137
+ # A shortcut for routing via Orange::Middleware::RestfulRouter and any plugins
138
+ #
139
+ # Any args are passed on to the middleware
140
+ def routing(opts ={})
141
+ stack Orange::Middleware::RestfulRouter, opts.dup
142
+ Orange.plugins.each{|p| p.middleware(:routing).each{|m| stack m, opts.dup} if p.has_middleware?}
143
+ end
144
+
145
+ def postrouting(opts ={})
146
+ Orange.plugins.each{|p| p.middleware(:postrouting).each{|m| stack m, opts.dup} if p.has_middleware?}
147
+ stack Orange::Middleware::Template
148
+ stack Orange::Middleware::FourOhFour, opts.dup # Last ditch, send route to 404 page.
149
+ end
150
+
151
+ def responders(opts ={})
152
+ Orange.plugins.each{|p| p.middleware(:responders).each{|m| stack m, opts.dup} if p.has_middleware?}
153
+ end
154
+
155
+ # # A shortcut to enable Rack::OpenID and Orange::Middleware::AccessControl
156
+ # #
157
+ # # Args will be passed on to Orange::Middleware::AccessControl
158
+ # def openid_access_control(*args)
159
+ # opts = args.extract_options!
160
+ #
161
+ # end
162
+
163
+ # Adds pulp to the core via the Orange::Core#add_pulp method
164
+ # @param [Orange::Mixin] mod a mixin to be included in the packet
165
+ def add_pulp(mod)
166
+ orange.add_pulp(mod)
167
+ end
168
+
169
+ # The exit point for the middleware stack,
170
+ # add the app to @main_app and then call Rack::Builder#run with the main app
171
+ def run(app, *args)
172
+ opts = args.extract_options!
173
+ @main_app = app
174
+ @build.run(app)
175
+ end
176
+
177
+ # Returns the Orange::Core
178
+ # @return [Orange::Core] The orange core
179
+ def orange
180
+ @core
181
+ end
182
+
183
+ # Passes through to Rack::Builder#map
184
+ # @todo Make this work - passing the block on to builder
185
+ # means we can't intercept anything, which will yield
186
+ # unexpected results
187
+ def map(path, &block)
188
+ raise 'not yet supported'
189
+ @build.map(path, &block)
190
+ end
191
+
192
+ # Builds the middleware stack (or uses a cached one)
193
+ #
194
+ # If auto_reload is enabled ({#auto_reload!}), builds every time
195
+ #
196
+ # @return [Object] a full stack of middleware and the exit application,
197
+ # conforming to Rack guidelines
198
+ def app
199
+ if @auto_reload
200
+ orange.fire(:stack_reloading, @app) if orange.stack # Alert we are rebuilding
201
+ @app = false # Rebuild no matter what if autoload
202
+ end
203
+ unless @app
204
+ @app = do_build # Build if necessary
205
+ orange.fire(:stack_loaded, @app)
206
+ end
207
+ @app
208
+ end
209
+
210
+ def do_build
211
+ @build.to_app
212
+ end
213
+
214
+ # Sets the core and then passes on to the stack, according to standard
215
+ # rack procedure
216
+ def call(env)
217
+ env['orange.core'] = @core
218
+ app.call(env)
219
+ end
220
+
221
+ # Debug helping
222
+ def inspect
223
+ "#<Orange::Stack:0x#{self.object_id.to_s(16)} @build=#{@build.inspect}, @core=#{@core.inspect}>"
224
+ end
225
+ end
226
+ end