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.
- data/README.markdown +18 -27
- data/lib/orange-core/application.rb +7 -2
- data/lib/orange-core/carton.rb +17 -9
- data/lib/orange-core/core.rb +25 -8
- data/lib/orange-core/magick.rb +3 -0
- data/lib/orange-core/middleware/abstract_format.rb +46 -0
- data/lib/orange-core/middleware/database.rb +5 -2
- data/lib/orange-core/middleware/globals.rb +1 -1
- data/lib/orange-core/middleware/restful_router.rb +40 -11
- data/lib/orange-core/middleware/root_dir.rb +19 -0
- data/lib/orange-core/middleware/show_exceptions.rb +23 -6
- data/lib/orange-core/middleware/static.rb +6 -6
- data/lib/orange-core/middleware/template.rb +8 -0
- data/lib/orange-core/resource.rb +25 -1
- data/lib/orange-core/resources/mapper.rb +20 -2
- data/lib/orange-core/resources/model_resource.rb +84 -2
- data/lib/orange-core/resources/parser.rb +3 -2
- data/lib/orange-core/stack.rb +10 -12
- data/lib/orange-core/templates/500.haml +2 -0
- data/lib/orange-core/views/default_resource/create.haml +1 -1
- data/lib/orange-core/views/default_resource/edit.haml +1 -1
- data/lib/orange-core/views/not_found/404.haml +1 -1
- data/spec/orange-core/application_spec.rb +1 -0
- data/spec/orange-core/core_spec.rb +16 -1
- data/spec/orange-core/middleware/restful_router_spec.rb +98 -1
- data/spec/orange-core/mock/mock_mixins.rb +1 -1
- data/spec/orange-core/mock/mock_model_resource.rb +13 -0
- data/spec/orange-core/mock/mock_plugin.rb +7 -0
- data/spec/orange-core/orange_spec.rb +11 -0
- data/spec/orange-core/resources/model_resource_spec.rb +16 -7
- data/spec/orange-core/spec_helper.rb +3 -0
- data/spec/orange-core/stack_spec.rb +2 -2
- 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.
|
6
|
-
|
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
|
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 (
|
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
|
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
|
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
|
-
|
57
|
-
orange-core dependencies).
|
59
|
+
orange-core tries to stay light on the dependencies.
|
58
60
|
|
59
|
-
* dm-core (+
|
60
|
-
* dm-
|
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
|
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
|
-
|
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
|
data/lib/orange-core/carton.rb
CHANGED
@@ -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|
|
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
|
data/lib/orange-core/core.rb
CHANGED
@@ -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
|
-
|
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 [
|
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
|
data/lib/orange-core/magick.rb
CHANGED
@@ -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'] ||
|
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!
|
@@ -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
|
-
|
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
|
-
|
28
|
-
packet['route.
|
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
|
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
|
-
|
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
|
-
|
23
|
+
if(orange.options[:development_mode])
|
24
|
+
backtrace = pretty(env, e)
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
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
|
-
|
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[:
|
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
|
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
|
data/lib/orange-core/resource.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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)
|