orange-core 0.6.0 → 0.7.0
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.
- 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)
|