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,96 @@
1
+ require 'orange-core/packet'
2
+
3
+ # Orange Middleware is a bit more complex than Rack middleware.
4
+ # Initializing it requires both a link to the downstream app and
5
+ # the core, and calling the app often requires an Orange::Packet
6
+ #
7
+ # Orange::Middleware::Base takes care of these basic tasks.
8
+ # Subclasses override the init method for extra initialization
9
+ # and the packet_call for a call with a packet, rather than
10
+ # a basic call
11
+ module Orange::Middleware
12
+ class Base
13
+ # Initialize will set the core and downstream app, then call init
14
+ # subclasses should override init instead of initialize.
15
+ # If the subclass defines a stack_init, then it will be registered
16
+ # as a stack_loaded event.
17
+ # @param [Object] app a downstream app
18
+ # @param [Orange::Core] core the orange core
19
+ # @param [optional, Array] args any arguments
20
+ def initialize(app, core, *args)
21
+ @app = app
22
+ @core = core
23
+ init(*args)
24
+ core.middleware(self)
25
+ end
26
+
27
+ # A stub method that subclasses can override to handle initialization
28
+ # For initialization
29
+ # @return [void]
30
+ def init(*args)
31
+ end
32
+
33
+ # The standard Rack "call". By default, Orange Middleware wraps the env into
34
+ # an Orange::Packet and passes it on to #packet_call. Subclasses will typically
35
+ # override packet_call rather than overriding call directly.
36
+ #
37
+ # Orange Middleware
38
+ # should expect to have this method ignored by upstream Orange-aware apps in
39
+ # favor of calling packet_call directly.
40
+ # @param [Hash] env the hash of environment variables given by the rack interface.
41
+ # @return [Array] the standard Rack striplet of status, headers and content
42
+ def call(env)
43
+ packet = Orange::Packet.new(@core, env)
44
+ packet_call(packet)
45
+ end
46
+
47
+ # Like the standard call, but with the env hash already wrapped into a Packet
48
+ # This is called automatically as part of #call, so subclasses can have a packet
49
+ # without having to initialize it. It will be called directly by Orange-aware
50
+ # upstream middleware, skipping the step of initializing the packet during #call.
51
+ #
52
+ # Passing the packet downstream should be done with #pass rather than the Rack
53
+ # standard @app.call, since #pass will take the packet and do a #packet_call
54
+ # if possible.
55
+ # @param [Orange::Packet] packet the packet corresponding to this env
56
+ # @return [Array] the standard Rack striplet of status, headers and content
57
+ def packet_call(packet)
58
+ pass packet
59
+ end
60
+
61
+ # Pass will sent the packet to the downstream app by calling call or packet call.
62
+ # Calling pass on a packet is the preferred way to call downstream apps, as it
63
+ # will call packet_call directly if possible (to avoid reinitializing the packet)
64
+ # @param [Orange::Packet] packet the packet to pass to downstream apps
65
+ # @return [Array] the standard Rack striplet of status, headers and content
66
+ def pass(packet)
67
+ if @app.respond_to?(:packet_call)
68
+ @app.packet_call(packet)
69
+ else
70
+ recapture(@app.call(packet.env), packet)
71
+ end
72
+ end
73
+
74
+ # After the pass has been completed, we should recapture the contents and make
75
+ # sure they are placed in the packet, in case the downstream app is not Orange aware.
76
+ # @param [Array] the standard Rack striplet of status, headers and content
77
+ # @param [Orange::Packet] packet the packet to pass to downstream apps
78
+ # @return [Array] the standard Rack striplet of status, headers and content
79
+ def recapture(response, packet)
80
+ packet[:status] = response[0]
81
+ packet[:headers] = response[1]
82
+ packet[:content] = response[2].first
83
+ response
84
+ end
85
+
86
+ # Accessor for @core, which is the stack's instance of Orange::Core
87
+ # @return [Orange::Core] the stack's instance of Orange::Core
88
+ def orange; @core; end
89
+
90
+ # Help stack traces
91
+ # @return [String] string representing this middleware (#to_s)
92
+ def inspect
93
+ self.to_s
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,45 @@
1
+ require 'orange-core/middleware/base'
2
+ module Orange::Middleware
3
+
4
+ class Database < Base
5
+ def init(opts = {})
6
+ opts = opts.with_defaults(:migration_url => (orange.options[:development_mode] ? '/__ORANGE_DB__/migrate' : false), :no_auto_upgrade => false)
7
+ orange.mixin Orange::Mixins::DBLoader
8
+ @options = opts
9
+ end
10
+
11
+ def stack_init
12
+ unless orange.options.has_key?('database') && orange.options['database'] == false
13
+ db = orange.options['database'] || 'sqlite3::memory:'
14
+ orange.load_db!(db)
15
+ orange.upgrade_db! unless @options[:no_auto_upgrade] || orange.options['db_no_auto_upgrade']
16
+ end
17
+ end
18
+
19
+ def packet_call(packet)
20
+ path = packet['route.path'] || packet.request.path_info
21
+ if @options[:migration_url] && @options[:migration_url] == path
22
+ orange.migrate_db!
23
+ after = packet.flash('redirect_to') || '/'
24
+ packet.reroute(after)
25
+ end
26
+ pass packet
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ module Orange::Mixins::DBLoader
33
+ def load_db!(url)
34
+ DataMapper.setup(:default, url)
35
+ end
36
+
37
+ def migrate_db!
38
+ DataMapper.auto_migrate!
39
+ end
40
+
41
+ def upgrade_db!
42
+ DataMapper.auto_upgrade!
43
+ end
44
+
45
+ end
@@ -0,0 +1,45 @@
1
+ require 'orange-core/middleware/base'
2
+
3
+ module Orange::Middleware
4
+ # The FlexRouter middleware takes a resource that can route paths and
5
+ # then intercepts routes for that resource. By default,
6
+ # it uses the Orange::SitemapResource.
7
+ #
8
+ # The resource is automatically loaded into the core as
9
+ # :sitemap. The resource must respond to "route?(path)"
10
+ # and "route(packet)".
11
+ #
12
+ # Pass a different routing resource using the :resource arg
13
+ class FourOhFour < Base
14
+ def init(opts = {})
15
+ @resource = opts[:resource] || Orange::NotFound
16
+ orange.load @resource.new, :not_found
17
+ orange.add_pulp Orange::Pulp::NotFoundHelper
18
+ end
19
+
20
+ # Sets the sitemap resource as the router if the resource can accept
21
+ # the path.
22
+ def packet_call(packet)
23
+ packet['route.router'] = orange[:not_found] unless packet['route.router']
24
+ begin
25
+ pass packet
26
+ rescue Orange::NotFoundException
27
+ orange[:not_found].route(packet)
28
+ packet.finish
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+
35
+ module Orange
36
+ class NotFoundException < Exception
37
+
38
+ end
39
+
40
+ module Pulp::NotFoundHelper
41
+ def not_found
42
+ raise Orange::NotFoundException.new
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,17 @@
1
+ require 'orange-core/middleware/base'
2
+ module Orange::Middleware
3
+
4
+ class Globals < Base
5
+ def init(opts = {})
6
+ opts = opts.with_defaults(:file => "__ORANGE__/config.yml")
7
+ @file = opts[:file].gsub('__ORANGE__', orange.app_dir)
8
+ @globals = orange[:parser].yaml(@file) || {}
9
+ @globals.each{|k,v| orange.options[k] = v}
10
+ end
11
+ def packet_call(packet)
12
+ packet['orange.globals'] ||= orange.options
13
+ pass packet
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ require 'orange-core/middleware/base'
2
+ module Orange::Middleware
3
+ class Loader < Base
4
+ def init(*args)
5
+ Dir.glob(File.join(orange.app_dir, 'resources', '*.rb')).each do |f|
6
+ require f
7
+ orange.load Orange::Inflector.constantize(Orange::Inflector.camelize(File.basename(f, '.rb'))).new
8
+ end
9
+ Dir.glob(File.join(orange.app_dir, 'cartons', '*.rb')).each { |f| require f }
10
+ Dir.glob(File.join(orange.app_dir, 'middleware', '*.rb')).each { |f| require f }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,53 @@
1
+ require 'orange-core/middleware/base'
2
+ module Orange::Middleware
3
+
4
+ class Rerouter < Base
5
+ def init(*args)
6
+ orange.add_pulp Orange::Pulp::Packet_Reroute
7
+ end
8
+
9
+ def packet_call(packet)
10
+ begin
11
+ pass packet
12
+ rescue Orange::Reroute
13
+ packet.finish
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ module Orange
20
+
21
+ module Pulp::Packet_Reroute
22
+ def reroute(url, type = :real, *args)
23
+ packet['reroute.to'] = url
24
+ packet['reroute.type'] = type
25
+ packet['reroute.args'] = *args if args
26
+ raise Reroute.new(self), 'Unhandled reroute'
27
+ end
28
+ end
29
+
30
+ class Reroute < Exception
31
+ def initialize(packet)
32
+ @packet = packet
33
+ @packet[:headers] = {"Content-Type" => 'text/html', "Location" => self.url}
34
+ @packet[:status] = 302
35
+ end
36
+
37
+ def url
38
+ case packet['reroute.type']
39
+ when :real
40
+ packet['reroute.to']
41
+ # Parsing for orange urls or something
42
+ when :orange
43
+ packet.route_to(packet['reroute.to'], *packet['reroute.args', []])
44
+ else
45
+ packet['reroute.to']
46
+ end
47
+ end
48
+
49
+ def packet
50
+ @packet
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,99 @@
1
+ require 'orange-core/middleware/base'
2
+
3
+ module Orange::Middleware
4
+ class RestfulRouter < Base
5
+ def init(*args)
6
+ opts = args.extract_options!.with_defaults(:restful_contexts => [:admin, :orange], :not_found => false, :exposed_actions => {:admin => :all, :orange => :all})
7
+ @exposed = opts[:exposed_actions]
8
+ @contexts = opts[:restful_contexts]
9
+ @not_found = opts[:not_found]
10
+ end
11
+
12
+ # sets resource, resource_id, resource_action and resource_path
13
+ # /resource/id/action/[resource/path/if/any]
14
+ # /resource/action/[resource/path/if/any]
15
+ #
16
+ # In future - support for nested resources
17
+ def packet_call(packet)
18
+ return (pass packet) if packet['route.router'] # Don't route if other middleware
19
+ # already has
20
+ parts = route_parts(packet)
21
+ if(should_route?(packet, parts))
22
+ # Take parts of route and set packet info
23
+ packet['route.resource'] = parts[:resource] if parts[:resource]
24
+ packet['route.resource_id'] = parts[:resource_id] if parts[:resource_id]
25
+ packet['route.resource_action'] = parts[:resource_action] if parts[:resource_action]
26
+
27
+ # Take remainder and set to resource_path
28
+ packet['route.resource_path'] = parts[:remainder]
29
+
30
+ # Set self as router if resource was found
31
+ if(packet['route.resource', false])
32
+ packet['route.router'] = self
33
+ elsif(@not_found)
34
+ packet['route.resource'] = @not_found
35
+ packet['route.router'] = self
36
+ end
37
+ end
38
+
39
+ pass packet
40
+ end
41
+
42
+ def route_parts(packet)
43
+ return_parts = {}
44
+ path = packet['route.path'] || packet.request.path_info
45
+ parts = path.split('/')
46
+ pad = parts.shift
47
+ if !parts.empty?
48
+ resource = parts.shift
49
+ if orange.loaded?(resource.to_sym)
50
+ return_parts[:resource] = resource.to_sym
51
+ if !parts.empty?
52
+ second = parts.shift
53
+ if second =~ /^\d+$/
54
+ return_parts[:resource_id] = second
55
+ if !parts.empty?
56
+ return_parts[:resource_action] = parts.shift.to_sym
57
+ else
58
+ return_parts[:resource_action] = :show
59
+ end
60
+ else
61
+ return_parts[:resource_action] = second.to_sym
62
+ end
63
+ else
64
+ return_parts[:resource_action] = :list
65
+ end # end check for second part
66
+ else
67
+ parts.unshift(resource)
68
+ end # end check for loaded resource
69
+ end # end check for nonempty route
70
+ return_parts[:remainder] = parts.unshift(pad).join('/')
71
+ return_parts
72
+ end
73
+
74
+ def should_route?(packet, parts)
75
+ return false unless @exposed.has_key?(packet['route.context'])
76
+ action_exposed?(@exposed[packet['route.context']], parts)
77
+ end
78
+
79
+ def action_exposed?(list, route_parts)
80
+ return true if list == :all
81
+ return true if list == route_parts[:resource_action]
82
+ return true if list.is_a?(Array) && list.include?(route_parts[:resource_action])
83
+ if list.is_a?(Hash)
84
+ all = list.has_key?(:all) ? action_exposed?(list[:all], route_parts) : false
85
+ one = list.has_key?(route_parts[:resource]) ? action_exposed?(list[route_parts[:resource]], route_parts) : false
86
+ return all || one
87
+ end
88
+ false
89
+ end
90
+
91
+ def route(packet)
92
+ resource = packet['route.resource']
93
+ raise 'resource not found' unless orange.loaded? resource
94
+ mode = packet['route.resource_action'] ||
95
+ (packet['route.resource_id'] ? :show : :list)
96
+ packet[:content] = orange[resource].view packet
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,39 @@
1
+ require 'orange-core/middleware/base'
2
+
3
+ module Orange::Middleware
4
+ # This middleware handles setting orange.env['route.context']
5
+ # to a value based on the route, if any. The route is then
6
+ # trimmed before continuing on.
7
+ class RouteContext < Base
8
+ def initialize(app, core, *args)
9
+ opts = args.extract_options!
10
+ opts.with_defaults!(:contexts => [:preview, :live, :admin, :orange],
11
+ :default => :live,
12
+ :urls => {})
13
+ @app = app
14
+ @core = core
15
+ @contexts = opts[:contexts]
16
+ @default = opts[:default]
17
+ @urls = opts[:urls]
18
+ end
19
+ def packet_call(packet)
20
+ path_info = packet['route.path'] || packet.env['PATH_INFO']
21
+ path = path_info.split('/')
22
+ pad = path.shift # Shift off empty first part
23
+ if @urls[packet.request.host]
24
+ packet['route.context'] = urls[packet.request.host]
25
+ elsif path.empty?
26
+ packet['route.context'] = @default
27
+ else
28
+ if(@contexts.include?(path.first.to_sym))
29
+ packet['route.context'] = path.shift.to_sym
30
+ path.unshift(pad)
31
+ packet['route.path'] = path.join('/')
32
+ else
33
+ packet['route.context'] = @default
34
+ end
35
+ end
36
+ pass packet
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,51 @@
1
+ require 'orange-core/middleware/base'
2
+
3
+ module Orange::Middleware
4
+ # This middleware handles setting orange.env['route.site_url']
5
+ # to a value based on the route, if any. The route is then
6
+ # trimmed before continuing on.
7
+ #
8
+ # Options -
9
+ # :multi - does Orange need to handle multiple urls
10
+ # :fake_it - host url(s) that Orange will fake requests on
11
+ # ex: :multi => true, :fake_it => 'localhost'
12
+ # will fake hostnames as first component of url
13
+ # only on localhost
14
+ class RouteSite < Base
15
+ def initialize(app, core, *args)
16
+ opts = args.extract_options!
17
+ opts.with_defaults!(:multi => false, :fake_it => ['localhost'])
18
+ @app = app
19
+ @core = core
20
+ @multi = opts[:multi]
21
+ # Put fake_it into an array, if necessary
22
+ @fake_it = opts[:fake_it].respond_to?(:include?) ?
23
+ opts[:fake_it] : [opts[:fake_it]]
24
+ end
25
+
26
+ def packet_call(packet)
27
+ request = packet.request
28
+ path_info = packet['route.path'] || packet.env['PATH_INFO']
29
+ path = path_info.split('/')
30
+ pad = path.shift # Shift off empty first part
31
+ packet['route.faked_site'] = false
32
+ if @multi
33
+ if path.empty?
34
+ packet['route.site_url'] = request.host
35
+ else
36
+ if @fake_it.include?(request.host)
37
+ packet['route.site_url'] = path.shift
38
+ packet['route.faked_site'] = true
39
+ else
40
+ packet['route.site_url'] = request.host
41
+ end
42
+ path.unshift(pad)
43
+ packet['route.path'] = path.join('/')
44
+ end
45
+ else
46
+ packet['route.site_url'] = request.host
47
+ end
48
+ pass packet
49
+ end
50
+ end
51
+ end