orange-core 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. data/README.markdown +18 -27
  2. data/lib/orange-core/application.rb +7 -2
  3. data/lib/orange-core/carton.rb +17 -9
  4. data/lib/orange-core/core.rb +25 -8
  5. data/lib/orange-core/magick.rb +3 -0
  6. data/lib/orange-core/middleware/abstract_format.rb +46 -0
  7. data/lib/orange-core/middleware/database.rb +5 -2
  8. data/lib/orange-core/middleware/globals.rb +1 -1
  9. data/lib/orange-core/middleware/restful_router.rb +40 -11
  10. data/lib/orange-core/middleware/root_dir.rb +19 -0
  11. data/lib/orange-core/middleware/show_exceptions.rb +23 -6
  12. data/lib/orange-core/middleware/static.rb +6 -6
  13. data/lib/orange-core/middleware/template.rb +8 -0
  14. data/lib/orange-core/resource.rb +25 -1
  15. data/lib/orange-core/resources/mapper.rb +20 -2
  16. data/lib/orange-core/resources/model_resource.rb +84 -2
  17. data/lib/orange-core/resources/parser.rb +3 -2
  18. data/lib/orange-core/stack.rb +10 -12
  19. data/lib/orange-core/templates/500.haml +2 -0
  20. data/lib/orange-core/views/default_resource/create.haml +1 -1
  21. data/lib/orange-core/views/default_resource/edit.haml +1 -1
  22. data/lib/orange-core/views/not_found/404.haml +1 -1
  23. data/spec/orange-core/application_spec.rb +1 -0
  24. data/spec/orange-core/core_spec.rb +16 -1
  25. data/spec/orange-core/middleware/restful_router_spec.rb +98 -1
  26. data/spec/orange-core/mock/mock_mixins.rb +1 -1
  27. data/spec/orange-core/mock/mock_model_resource.rb +13 -0
  28. data/spec/orange-core/mock/mock_plugin.rb +7 -0
  29. data/spec/orange-core/orange_spec.rb +11 -0
  30. data/spec/orange-core/resources/model_resource_spec.rb +16 -7
  31. data/spec/orange-core/spec_helper.rb +3 -0
  32. data/spec/orange-core/stack_spec.rb +2 -2
  33. metadata +11 -72
data/README.markdown CHANGED
@@ -2,12 +2,15 @@ Orange
2
2
  ======
3
3
 
4
4
  Orange is intended to be a middle ground between the simplicity of Sinatra
5
- and the power of Rails. Orange is being developed by Orange Sparkle Ball, inc
6
- for our own use.
5
+ and the power of Rails. It also throws a bit of django's auto admin interface
6
+ into the mix. Orange is being developed by Orange Sparkle Ball, inc
7
+ for developing websites for our clients quickly and efficiently, but certainly
8
+ could be adapted to other uses... if you come up with something creative, let
9
+ us know.
7
10
 
8
11
  orange-core represents the core dependencies for orange applications. If you want
9
- a full featured Orange-based CMS, we're working on splitting that into the Sparkles
10
- project.
12
+ a full featured Orange-based CMS (where the automatic admin interface comes in),
13
+ look at [orange-sparkles](http://github.com/orange-project/orange-sparkles).
11
14
 
12
15
  **Note**: Orange is still in a "beta" stage. Test coverage is lack-luster at best. The library is being used on a few currently live (low traffic) sites, so it might be stable enough to use on your own projects let us know if you have issues.
13
16
 
@@ -32,8 +35,8 @@ Should I Use Orange?
32
35
  "I want to create a quick RESTful web service that does one thing and does it well"
33
36
 
34
37
  Sinatra is probably better for this kind of thing. It's perfect for creating quick web apps based on RESTful
35
- ideals. Or perhaps use a Sinatra clone built on Orange, so you can incorporate orange plugins...
36
- but such a clone doesn't exist (yet).
38
+ ideals. Or perhaps use a Sinatra clone built on Orange, so you can incorporate orange plugins and built in
39
+ database support... but such a clone doesn't exist [does it](http://github.com/therabidbanana/orange-juice)?
37
40
 
38
41
  "I want to create a powerful web application that needs to be rock solid and use a
39
42
  well-tested foundation"
@@ -43,43 +46,31 @@ for building web applications that gives you everything you need for the lifecyc
43
46
  application
44
47
 
45
48
  "I want to deploy a website on Ruby that has some dynamic elements, maybe allowing me
46
- to create parts of the page in a plugin if necessary."
49
+ to create my own plugin without jumping through too many hoops."
47
50
 
48
51
  Yes. This is what orange was designed for - we're building it to be able to quickly deploy
49
- websites that can have a Ruby base without the heavy-weight Ruby on Rails backend, but without
50
- feeling like you have to start from scratch like it feels in Sinatra.
52
+ websites that can have a Ruby base without the heavy-weight Ruby on Rails backend, but also
53
+ without feeling like you have to start from scratch like it feels in Sinatra.
51
54
 
52
55
 
53
56
  Required Gems
54
57
  -------------
55
58
 
56
- To be updated... (this is full list for orange-core and orange-more, we're looking to minimize
57
- orange-core dependencies).
59
+ orange-core tries to stay light on the dependencies.
58
60
 
59
- * dm-core (+ do_[sqlite3|mysql|...] )
60
- * dm-more
61
- * dm-is-awesome_set
61
+ * dm-core (+ dm-[sqlite3|mysql|...]-adapter )
62
+ * dm-migrations
62
63
  * rack
63
64
  * haml
64
- * rack-abstract-format (github)
65
- * ruby-openid
66
- * rack-openid
67
- * openid_dm_store
68
- * radius
69
65
  * crack
70
- * eventbright
71
- * spreedly
72
- * hominid
73
- * mail
74
- * tlsmail (If Ruby version <= 1.8.6)
75
- * gattica
76
66
 
77
67
  All dependencies should be loaded if you install the gem except for the datamapper
78
68
  adapter relevant to your set up. If, for example, you want to use a mysql database,
79
- you'll need to install do_mysql, and for an sqlite3 database, you'll need do_sqlite3
69
+ you'll need to install dm-mysql-adapter, and for an sqlite3 database, you'll need dm-sqlite-adapter
80
70
 
81
71
 
82
- Also, you'll need a web server of some kind and need to set it up for rack.
72
+ Also, you'll need a web server of some kind and need to set it up for rack. Rack supports
73
+ WEBrick out of the box if you just want to play around though.
83
74
 
84
75
  **Testing**
85
76
 
@@ -106,11 +106,15 @@ module Orange
106
106
  # Returns an instance of Orange::Stack to be run by Rack
107
107
  #
108
108
  # Usually, you'll call this in the rackup file: `run MyApplication.app`
109
- def self.app(c = false)
109
+ def self.app(c = false, &block)
110
110
  if c
111
111
  self.core = c
112
112
  else
113
- self.core ||= Orange::Core.new
113
+ if block_given?
114
+ self.core = Orange::Core.new(&block)
115
+ else
116
+ self.core ||= Orange::Core.new
117
+ end
114
118
  end
115
119
  return self.core.stack unless self.core.stack.blank?
116
120
  if self.stack_block.instance_of?(Proc)
@@ -125,6 +129,7 @@ module Orange
125
129
  #
126
130
  # Each call to stack overrides the previous one.
127
131
  def self.stack(core = false, &block)
132
+ self.core.stack = nil if self.core # Wipe old stack from core
128
133
  self.core = core if core
129
134
  self.stack_block = Proc.new # pulls in the block and makes it a proc
130
135
  end
@@ -37,13 +37,10 @@ module Orange
37
37
  HEREDOC
38
38
  end
39
39
 
40
- # Include DataMapper types (required to be able to use Serial)
41
- include DataMapper::Types
42
-
43
40
  # Do setup of object and declare an id
44
41
  def self.id
45
42
  include DataMapper::Resource
46
- property(:id, Serial)
43
+ property(:id, DataMapper::Property::Serial)
47
44
  self.scaffold_properties ||= []
48
45
  self.init
49
46
  end
@@ -53,8 +50,19 @@ module Orange
53
50
  end
54
51
 
55
52
  # Return properties that should be shown for a given context
56
- def self.form_props(context = :live)
57
- self.scaffold_properties.select{|p| p[:levels].include?(context) }
53
+ def self.form_props(context = :live, mode = :any)
54
+ self.scaffold_properties.select{|p| should_use?(p, context, mode) }
55
+ end
56
+
57
+ def self.should_use?(property, context, mode = :any )
58
+ if(mode == :any || mode.blank?)
59
+ property[:levels].include?(context)
60
+ else
61
+ (property[:levels].include?(context) &&
62
+ (property[:lazy] == false ||
63
+ property[:lazy] == mode ||
64
+ (property[:lazy].is_a?(Array) && property[:lazy].include?(mode))))
65
+ end
58
66
  end
59
67
 
60
68
  # Helper to wrap properties into admin level
@@ -79,7 +87,7 @@ module Orange
79
87
  end
80
88
 
81
89
  def self.add_scaffold(name, type, dm_type, opts)
82
- self.scaffold_properties << {:name => name, :type => type, :levels => @levels}.merge(opts) if @levels || opts.has_key?(:levels)
90
+ self.scaffold_properties << {:name => name, :type => type, :levels => @levels, :lazy => false}.merge(opts) if @levels || opts.has_key?(:levels)
83
91
  opts = opts.delete_if{|k,v| SCAFFOLD_OPTIONS.include?(k)} # DataMapper doesn't like arbitrary opts
84
92
  self.property(name, dm_type, opts)
85
93
  end
@@ -111,13 +119,13 @@ module Orange
111
119
  # Define a helper for fulltext type database stuff
112
120
  # Show in a context if wrapped in one of the helpers
113
121
  def self.fulltext(name, opts = {})
114
- add_scaffold(name, :fulltext, Text, opts)
122
+ add_scaffold(name, :fulltext, DataMapper::Property::Text, opts.with_defaults(:lazy => true))
115
123
  end
116
124
 
117
125
  # Define a helper for boolean type database stuff
118
126
  # Show in a context if wrapped in one of the helpers
119
127
  def self.boolean(name, opts = {})
120
- add_scaffold(name, :boolean, Boolean, opts)
128
+ add_scaffold(name, :boolean, DataMapper::Property::Boolean, opts.with_defaults(:lazy => true))
121
129
  end
122
130
 
123
131
  # Define a helper for input type="text" type database stuff
@@ -1,4 +1,6 @@
1
1
  require 'dm-core'
2
+ require 'extlib/mash'
3
+ require 'extlib/inflection'
2
4
  require 'dm-migrations'
3
5
  require 'rack'
4
6
  require 'rack/builder'
@@ -12,8 +14,15 @@ module Orange
12
14
  attr_accessor :plugins
13
15
 
14
16
  # Support for plugins
15
- def self.plugins
17
+ def self.plugins(plugin_list = false)
16
18
  @plugins ||= []
19
+ return @plugins unless plugin_list
20
+ @plugins.select do |p|
21
+ plugin_list.include?(
22
+ Extlib::Inflection::underscore(
23
+ Extlib::Inflection::demodulize(p.class.to_s)
24
+ ).to_sym)
25
+ end
17
26
  end
18
27
 
19
28
  # Allows adding plugins
@@ -46,8 +55,7 @@ module Orange
46
55
  {
47
56
  :contexts => [:live, :admin, :orange],
48
57
  :default_context => :live,
49
- :default_resource => :not_found,
50
- :default_database => 'sqlite3::memory:'
58
+ :default_resource => :not_found
51
59
  } unless defined?(DEFAULT_CORE_OPTIONS)
52
60
 
53
61
  # Args will be set to the @options array.
@@ -64,7 +72,7 @@ module Orange
64
72
  # This method calls afterLoad when it is done. Subclasses can override
65
73
  # the afterLoad method for initialization needs.
66
74
  def initialize(*args, &block)
67
- @options = Options.new(*args, &block).hash.with_defaults(DEFAULT_CORE_OPTIONS)
75
+ @options = Mash.new(Options.new(*args, &block).hash.with_defaults(DEFAULT_CORE_OPTIONS))
68
76
  @resources = {}
69
77
  @application = false
70
78
  @stack = false
@@ -75,14 +83,15 @@ module Orange
75
83
  load(Orange::Mapper.new, :mapper)
76
84
  load(Orange::Scaffold.new, :scaffold)
77
85
  load(Orange::PageParts.new, :page_parts)
78
- Orange.plugins.each{|p| p.resources.each{|args| load(*args)} if p.has_resources?}
86
+
87
+ Orange.plugins(@options['plugins']).each{|p| p.resources.each{|args| load(*args)} if p.has_resources?}
79
88
  self.register(:stack_loaded) do |s|
80
89
  @middleware.each{|m| m.stack_init if m.respond_to?(:stack_init)}
81
90
  @application.stack_init if @application
82
91
  end
83
92
  self.register(:stack_reloading){|s| @middleware = []} # Dump middleware on stack reload
84
- # load(Orange::AdminResource.new, :admin)
85
93
  afterLoad
94
+ options[:development_mode] = true if ENV['RACK_ENV'] && ENV['RACK_ENV'] == 'development'
86
95
  self
87
96
  end
88
97
 
@@ -160,12 +169,15 @@ module Orange
160
169
  # Takes an instance of Orange::Stack and saves it.
161
170
  def stack(new_stack = false)
162
171
  @stack = new_stack if new_stack
172
+ @middleware = [] if new_stack
163
173
  @stack
164
174
  end
165
175
 
166
176
  # Takes an instance of Orange::Stack and saves it.
167
177
  def stack=(new_stack)
168
178
  @stack = new_stack
179
+ @middleware = []
180
+ @stack
169
181
  end
170
182
 
171
183
  # Convenience self for consistent naming across middleware
@@ -215,8 +227,9 @@ module Orange
215
227
 
216
228
  # Returns options of the orange core
217
229
  #
218
- # @return [Hash] Hash of options
219
- def options
230
+ # @return [Mash] Hash-like mash of options
231
+ def options(*args, &block)
232
+ @options.merge(Options.new(*args, &block).hash) if (args.size > 0 || block_given?)
220
233
  @options
221
234
  end
222
235
 
@@ -263,5 +276,9 @@ module Orange
263
276
  def inspect
264
277
  "#<Orange::Core:0x#{self.object_id.to_s(16)}>"
265
278
  end
279
+
280
+ def plugins
281
+ Orange.plugins(options['plugins'])
282
+ end
266
283
  end
267
284
  end
@@ -1,3 +1,6 @@
1
+ # This file is a set of crazy monkey patches and other support oddities.
2
+ #
3
+
1
4
  # Monkey Patch the extract_options! stolen from ActiveSupport
2
5
  # @private
3
6
  class ::Array #:nodoc:
@@ -0,0 +1,46 @@
1
+ require 'orange-core/middleware/base'
2
+ require 'pathname'
3
+
4
+ module Orange::Middleware
5
+ # This is an adapted Orange version of mynyml's rack-abstract-format
6
+ # available at http://github.com/mynyml/rack-abstract-format
7
+ class AbstractFormat < Base
8
+ def init(opts = {})
9
+ end
10
+
11
+ def packet_call(packet)
12
+ path_info = packet['route.path'] || packet.env['PATH_INFO']
13
+ path = Pathname(path_info)
14
+ packet['route.path'] = path.to_s.sub(/#{path.extname}$/,'') if Rack::Mime::MIME_TYPES.include?(path.extname)
15
+ packet.env['HTTP_ACCEPT'] = concat(packet.env['HTTP_ACCEPT'], Rack::Mime.mime_type(path.extname))
16
+
17
+ pass packet
18
+ end
19
+
20
+ private
21
+ def concat(accept, type)
22
+ (accept || '').split(',').unshift(type).compact.join(',')
23
+ end
24
+ end
25
+ end
26
+
27
+ #
28
+ # Copyright © 2009 Martin Aumont (mynyml)
29
+ #
30
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
31
+ # this software and associated documentation files (the "Software"), to deal in
32
+ # the Software without restriction, including without limitation the rights to
33
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
34
+ # of the Software, and to permit persons to whom the Software is furnished to do
35
+ # so, subject to the following conditions:
36
+ #
37
+ # The above copyright notice and this permission notice shall be included in all
38
+ # copies or substantial portions of the Software.
39
+ #
40
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
43
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
44
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
45
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
46
+ # SOFTWARE.
@@ -1,5 +1,6 @@
1
1
  require 'orange-core/middleware/base'
2
-
2
+ require 'dm-core'
3
+ require 'dm-migrations'
3
4
  module Orange::Middleware
4
5
 
5
6
  class Database < Base
@@ -11,13 +12,15 @@ module Orange::Middleware
11
12
 
12
13
  def stack_init
13
14
  unless orange.options.has_key?('database') && orange.options['database'] == false
14
- db = orange.options['database'] || 'sqlite3::memory:'
15
+ db = ENV["DATABASE_URL"] || orange.options['database'] || "sqlite3://#{orange.app_dir('dev_db.sqlite')}"
16
+ orange.options['database'] = db
15
17
  orange.load_db!(db)
16
18
  orange.upgrade_db! unless @options[:no_auto_upgrade] || orange.options['db_no_auto_upgrade']
17
19
  end
18
20
  end
19
21
 
20
22
  def packet_call(packet)
23
+ packet["database.url"] = orange.options["database"]
21
24
  path = packet['route.path'] || packet.request.path_info
22
25
  if @options[:migration_url] && @options[:migration_url] == path
23
26
  orange.migrate_db!
@@ -8,10 +8,10 @@ module Orange::Middleware
8
8
  @globals = orange[:parser].yaml(@file) || {}
9
9
  @globals.each{|k,v| orange.options[k] = v}
10
10
  end
11
+
11
12
  def packet_call(packet)
12
13
  packet['orange.globals'] ||= orange.options
13
14
  pass packet
14
15
  end
15
-
16
16
  end
17
17
  end
@@ -20,13 +20,15 @@ module Orange::Middleware
20
20
  parts = route_parts(packet)
21
21
  if(should_route?(packet, parts))
22
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]
23
+ first_part = parts.pop
26
24
 
27
- # Take remainder and set to resource_path
28
- packet['route.resource_path'] = parts[:remainder]
25
+ packet['route.resource'] = first_part[:resource] if first_part[:resource]
26
+ packet['route.resource_id'] = first_part[:resource_id] if first_part[:resource_id]
27
+ packet['route.resource_action'] = first_part[:resource_action] if first_part[:resource_action]
29
28
 
29
+ # Take remainder and set to resource_path
30
+ packet['route.resource_path'] = first_part[:remainder] if first_part[:remainder]
31
+ packet['route.nesting'] = parts
30
32
  # Set self as router if resource was found
31
33
  if(packet['route.resource', false])
32
34
  packet['route.router'] = self
@@ -39,24 +41,26 @@ module Orange::Middleware
39
41
  pass packet
40
42
  end
41
43
 
42
- def route_parts(packet)
44
+ def get_parts(path, nested_in = nil)
43
45
  return_parts = {}
44
- path = packet['route.path'] || packet.request.path_info
45
46
  parts = path.split('/')
46
47
  pad = parts.shift
47
48
  if !parts.empty?
48
49
  resource = parts.shift
49
- if orange.loaded?(resource.to_sym)
50
+ if orange.loaded?(resource.to_sym) && (!nested_in || orange[nested_in].nests.keys.include?(resource.to_sym))
50
51
  return_parts[:resource] = resource.to_sym
51
52
  if !parts.empty?
52
53
  second = parts.shift
53
54
  if second =~ /^\d+$/
54
55
  return_parts[:resource_id] = second
55
- if !parts.empty?
56
+ if !(parts.empty? || orange[resource.to_sym].nests.keys.include?(parts.first.to_sym))
56
57
  return_parts[:resource_action] = parts.shift.to_sym
57
58
  else
58
59
  return_parts[:resource_action] = :show
59
60
  end
61
+ elsif orange[resource.to_sym].nests.keys.include?(second.to_sym)
62
+ # we're nesting if the action is the same as a resource name
63
+ return_parts[:resource_action] = :show
60
64
  else
61
65
  return_parts[:resource_action] = second.to_sym
62
66
  end
@@ -71,15 +75,40 @@ module Orange::Middleware
71
75
  return_parts
72
76
  end
73
77
 
78
+ def route_parts(packet)
79
+ path = packet['route.path'] || packet.request.path_info
80
+ my_parts = []
81
+ my_parts << get_parts(path)
82
+ new_path = my_parts.last[:remainder]
83
+ nested = my_parts.last[:resource]
84
+ until (new_path.blank? || new_path == "/" )
85
+ parts = get_parts(new_path, nested)
86
+ break if new_path == parts[:remainder]
87
+ my_parts << parts
88
+ new_path = parts[:remainder]
89
+ nested = my_parts.last[:resource]
90
+ end
91
+ my_parts
92
+ end
93
+
74
94
  def should_route?(packet, parts)
75
95
  return false unless @exposed.has_key?(packet['route.context'])
76
- action_exposed?(@exposed[packet['route.context']], parts)
96
+ if parts.first[:resource].blank? || !(orange[parts.first[:resource]].respond_to?(:exposed))
97
+ action_exposed?(@exposed[packet['route.context']], parts.first)
98
+ else
99
+ # This allows ModelResources to expose their own action.
100
+ # (Other resources too, but those ones have to explicitly define
101
+ # the #exposed(packet) method to work)
102
+ new_parts = parts.first.dup
103
+ new_parts.delete(:resource)
104
+ action_exposed?(orange[parts.first[:resource]].exposed(packet), new_parts)
105
+ end
77
106
  end
78
107
 
79
108
  def action_exposed?(list, route_parts)
80
109
  return true if list == :all
81
110
  return true if list == route_parts[:resource_action]
82
- return true if list.is_a?(Array) && list.include?(route_parts[:resource_action])
111
+ return true if list.is_a?(Array) && (list.include?(route_parts[:resource_action]) || list.include?(:all))
83
112
  if list.is_a?(Hash)
84
113
  all = list.has_key?(:all) ? action_exposed?(list[:all], route_parts) : false
85
114
  one = list.has_key?(route_parts[:resource]) ? action_exposed?(list[route_parts[:resource]], route_parts) : false
@@ -0,0 +1,19 @@
1
+ require 'orange-core/middleware/base'
2
+
3
+ module Orange::Middleware
4
+ # This middleware handles sites in subdirs
5
+ class RootDir < Base
6
+ def packet_call(packet)
7
+ request = packet.request
8
+ path_info = packet['route.path'] || packet.env['PATH_INFO']
9
+ unless(orange.options['root_dir'].blank? || packet['route.root_dir'])
10
+ root = orange.options['root_dir']
11
+ packet['route.root_dir'] = root
12
+ my_regex = root.kind_of?(Regexp) ? root : /^#{orange.options['root_dir']}/
13
+ path_info = path_info.sub(my_regex, "/")
14
+ packet['route.path'] = path_info.blank? ? "/" : path_info
15
+ end
16
+ pass packet
17
+ end
18
+ end
19
+ end
@@ -20,17 +20,30 @@ module Orange::Middleware
20
20
  def call(env)
21
21
  @app.call(env)
22
22
  rescue Exception => e
23
- backtrace = pretty(env, e)
23
+ if(orange.options[:development_mode])
24
+ backtrace = pretty(env, e)
24
25
 
25
- [500,
26
- {"Content-Type" => "text/html",
27
- "Content-Length" => backtrace.join.size.to_s},
28
- backtrace]
26
+ return [500,
27
+ {"Content-Type" => "text/html",
28
+ "Content-Length" => backtrace.join.size.to_s},
29
+ backtrace]
30
+ else
31
+ page = error_page(env)
32
+ return [500, {"Content-Type" => "text/html",
33
+ "Content-Length" => page.join.size.to_s},
34
+ page]
35
+ end
29
36
  end
30
37
 
31
38
  def packet_call(packet)
32
39
  backtrace = pretty()
33
40
  end
41
+
42
+ def error_page(env)
43
+ packet = Orange::Packet.new(@core, env)
44
+ parse = orange[:parser].haml("500.haml", packet, :template => true)
45
+ [parse]
46
+ end
34
47
 
35
48
  def pretty(env, exception)
36
49
  req = Rack::Request.new(env)
@@ -73,7 +86,11 @@ module Orange::Middleware
73
86
  when String
74
87
  Rack::Utils.escape_html(obj)
75
88
  else
76
- Rack::Utils.escape_html(obj.inspect)
89
+ begin
90
+ Rack::Utils.escape_html(obj.inspect)
91
+ rescue Exception => e
92
+ "Object #{obj.class.to_s} could not be inspected"
93
+ end
77
94
  end
78
95
  end
79
96
  end
@@ -35,11 +35,11 @@ module Orange::Middleware
35
35
 
36
36
  def initialize(app, core, options={})
37
37
  @lib_urls = {'_orange_' => File.join(core.core_dir, 'assets') }
38
- Orange.plugins.each{|p| @lib_urls[p.assets_name] = p.assets if p.has_assets?}
38
+ core.plugins.each{|p| @lib_urls[p.assets_name] = p.assets if p.has_assets?}
39
39
 
40
40
  @app = app
41
41
  @core = core
42
- @urls = options[:urls] || ["/favicon.ico", "/assets/public", "/assets/uploaded"]
42
+ @urls = options[:asset_urls] || ["/favicon.ico", "/assets/public", "/assets/uploaded"]
43
43
  @root = options[:root] || File.join(orange.app_dir, 'assets')
44
44
  @file_server = Orange::Middleware::StaticFile.new(@root)
45
45
  end
@@ -50,14 +50,14 @@ module Orange::Middleware
50
50
  path.index(File.join('', 'assets', url)) == 0
51
51
  }.first
52
52
  can_serve = @urls.any?{|url| path.index(url) == 0 }
53
- if can_serve_lib
53
+ if can_serve
54
+ packet['route.path'] = path.gsub(/^\/assets/, '')
55
+ @file_server.call(packet.env)
56
+ elsif can_serve_lib
54
57
  lib_url = can_serve_lib.first
55
58
  packet['file.root'] = can_serve_lib.last
56
59
  packet['route.path'] = path.split(lib_url, 2).last
57
60
  @file_server.call(packet.env)
58
- elsif can_serve
59
- packet['route.path'] = path.gsub(/^\/assets/, '')
60
- @file_server.call(packet.env)
61
61
  else
62
62
  pass packet
63
63
  end
@@ -44,6 +44,14 @@ module Orange::Pulp::Template
44
44
  content
45
45
  end
46
46
  end
47
+ def template(name)
48
+ name = name.to_s if name.kind_of?(Symbol)
49
+ name = name + ".haml" unless name =~ /\.haml$/
50
+ packet['template.file'] == name
51
+ end
52
+ def layout(name)
53
+ template(name)
54
+ end
47
55
  end
48
56
 
49
57
  module Orange::Mixins::Template
@@ -7,9 +7,20 @@ module Orange
7
7
  # Defines a model class as an inheritable class attribute and also an instance
8
8
  # attribute
9
9
  cattr_accessor :called
10
+ cattr_accessor :viewable_actions
11
+
12
+ def nests
13
+ {}
14
+ end
15
+
16
+ def self.viewable(*args)
17
+ self.viewable_actions ||= []
18
+ args.each{|arg| self.viewable_actions << arg}
19
+ end
10
20
 
11
21
  def initialize(*args, &block)
12
22
  @options = DefaultHash.new.merge!(Options.new(*args, &block).hash)
23
+ self.class.viewable_actions ||= []
13
24
  end
14
25
 
15
26
  def set_orange(orange, name)
@@ -44,7 +55,20 @@ module Orange
44
55
  end
45
56
 
46
57
  def view(packet = false, *args)
47
- ''
58
+ opts = args.extract_options!
59
+ my_action = packet['route.resource_action'] if packet
60
+ action = opts[:mode] || opts[:resource_action] || my_action || :index
61
+ viewable(packet, action, opts)
62
+ end
63
+
64
+ def viewable(packet, mode, opts={})
65
+ if(self.respond_to?(mode))
66
+ self.__send__(mode, packet, opts)
67
+ elsif(self.class.viewable_actions.include?(mode))
68
+ do_view(packet, mode, opts)
69
+ else
70
+ ''
71
+ end
48
72
  end
49
73
 
50
74
  def orange_name
@@ -7,6 +7,12 @@ module Orange
7
7
  orange.add_pulp Pulp::Packet_Mapper
8
8
  end
9
9
 
10
+ def root_url(packet)
11
+ root = ''
12
+ root += packet['route.root_dir'].gsub(/\/$/,'') if packet['route.root_dir']
13
+ root += '/'
14
+ end
15
+
10
16
  def route_to(packet, resource, *args)
11
17
  opts = args.extract_options!
12
18
  packet = DefaultHash.new unless packet
@@ -16,13 +22,25 @@ module Orange
16
22
  args.unshift(resource)
17
23
  args.unshift(context)
18
24
  args.unshift(site)
19
- '/'+args.compact.join('/')
25
+ root_url(packet) + args.compact.join('/')
20
26
  end
21
27
  end
22
28
 
23
29
  module Pulp::Packet_Mapper
24
30
  def route_to(resource, *args)
25
- orange[:mapper].route_to(self, resource, *args)
31
+ if resource.respond_to?(:full_path)
32
+ orange[:mapper].root_url(packet) + resource.full_path.gsub(/^\//,'')
33
+ else
34
+ orange[:mapper].route_to(self, resource, *args)
35
+ end
36
+ end
37
+
38
+ def root_url
39
+ orange[:mapper].root_url(packet)
40
+ end
41
+
42
+ def full_url(*args)
43
+ orange[:mapper].root_url(packet) + args.compact.join('/')
26
44
  end
27
45
 
28
46
  def reroute(url, type = :real, *args)