orange 0.0.3 → 0.0.4

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 (80) hide show
  1. data/lib/orange/application.rb +1 -1
  2. data/lib/orange/carton.rb +55 -27
  3. data/lib/orange/cartons/site_carton.rb +4 -1
  4. data/lib/orange/core.rb +10 -2
  5. data/lib/orange/magick.rb +11 -0
  6. data/lib/orange/middleware/access_control.rb +16 -7
  7. data/lib/orange/middleware/base.rb +40 -1
  8. data/lib/orange/middleware/flex_router.rb +59 -0
  9. data/lib/orange/middleware/route_context.rb +2 -2
  10. data/lib/orange/middleware/route_site.rb +1 -1
  11. data/lib/orange/middleware/show_exceptions.rb +3 -1
  12. data/lib/orange/middleware/site_load.rb +14 -1
  13. data/lib/orange/middleware/static.rb +0 -2
  14. data/lib/orange/middleware/template.rb +3 -1
  15. data/lib/orange/resources/admin_resource.rb +25 -0
  16. data/lib/orange/resources/mapper.rb +3 -0
  17. data/lib/orange/resources/model_resource.rb +15 -9
  18. data/lib/orange/resources/page_parts.rb +0 -7
  19. data/lib/orange/resources/parser.rb +5 -4
  20. data/lib/orange/resources/singleton_model_resource.rb +7 -0
  21. data/lib/orange/resources/sitemap_resource.rb +96 -0
  22. data/lib/orange/stack.rb +5 -5
  23. data/spec/{application_spec.rb → orange/application_spec.rb} +0 -0
  24. data/spec/orange/carton_spec.rb +136 -0
  25. data/spec/{core_spec.rb → orange/core_spec.rb} +10 -0
  26. data/spec/{magick_spec.rb → orange/magick_spec.rb} +11 -0
  27. data/spec/orange/middleware/access_control_spec.rb +3 -0
  28. data/spec/orange/middleware/base_spec.rb +37 -0
  29. data/spec/orange/middleware/database_spec.rb +3 -0
  30. data/spec/orange/middleware/globals_spec.rb +3 -0
  31. data/spec/orange/middleware/recapture_spec.rb +3 -0
  32. data/spec/orange/middleware/rerouter_spec.rb +3 -0
  33. data/spec/orange/middleware/restful_router_spec.rb +3 -0
  34. data/spec/orange/middleware/route_context_spec.rb +3 -0
  35. data/spec/orange/middleware/route_site_spec.rb +3 -0
  36. data/spec/orange/middleware/show_exceptions_spec.rb +3 -0
  37. data/spec/orange/middleware/site_load_spec.rb +26 -0
  38. data/spec/orange/middleware/static_file_spec.rb +3 -0
  39. data/spec/orange/middleware/static_spec.rb +3 -0
  40. data/spec/orange/middleware/template_spec.rb +3 -0
  41. data/spec/{mock → orange/mock}/mock_app.rb +0 -0
  42. data/spec/orange/mock/mock_carton.rb +43 -0
  43. data/spec/{mock → orange/mock}/mock_core.rb +0 -0
  44. data/spec/{mock → orange/mock}/mock_middleware.rb +8 -0
  45. data/spec/{mock → orange/mock}/mock_mixins.rb +0 -0
  46. data/spec/{mock → orange/mock}/mock_model_resource.rb +4 -0
  47. data/spec/{mock → orange/mock}/mock_pulp.rb +0 -0
  48. data/spec/{mock → orange/mock}/mock_resource.rb +0 -0
  49. data/spec/{mock → orange/mock}/mock_router.rb +0 -0
  50. data/spec/{orange_spec.rb → orange/orange_spec.rb} +0 -0
  51. data/spec/{packet_spec.rb → orange/packet_spec.rb} +0 -0
  52. data/spec/{resource_spec.rb → orange/resource_spec.rb} +0 -0
  53. data/spec/orange/resources/admin_resource_spec.rb +16 -0
  54. data/spec/{resources → orange/resources}/mapper_spec.rb +0 -0
  55. data/spec/{resources → orange/resources}/model_resource_spec.rb +104 -0
  56. data/spec/{resources → orange/resources}/parser_spec.rb +0 -0
  57. data/spec/{resources → orange/resources}/routable_resource_spec.rb +0 -0
  58. data/spec/orange/resources/singleton_model_resource_spec.rb +4 -0
  59. data/spec/{resources/flex_router_spec.rb → orange/resources/sitemap_resource_spec.rb} +1 -1
  60. data/spec/orange/spec_helper.rb +51 -0
  61. data/spec/{stack_spec.rb → orange/stack_spec.rb} +0 -0
  62. metadata +45 -40
  63. data/lib/orange/resources/flex_router.rb +0 -13
  64. data/spec/carton_spec.rb +0 -5
  65. data/spec/middleware/access_control_spec.rb +0 -0
  66. data/spec/middleware/base_spec.rb +0 -0
  67. data/spec/middleware/database_spec.rb +0 -0
  68. data/spec/middleware/globals_spec.rb +0 -0
  69. data/spec/middleware/recapture_spec.rb +0 -0
  70. data/spec/middleware/rerouter_spec.rb +0 -0
  71. data/spec/middleware/restful_router_spec.rb +0 -0
  72. data/spec/middleware/route_context_spec.rb +0 -0
  73. data/spec/middleware/route_site_spec.rb +0 -0
  74. data/spec/middleware/show_exceptions_spec.rb +0 -0
  75. data/spec/middleware/site_load_spec.rb +0 -0
  76. data/spec/middleware/static_file_spec.rb +0 -0
  77. data/spec/middleware/static_spec.rb +0 -0
  78. data/spec/middleware/template_spec.rb +0 -0
  79. data/spec/mock/mock_carton.rb +0 -15
  80. data/spec/spec_helper.rb +0 -20
@@ -81,7 +81,7 @@ module Orange
81
81
  # The intent is for the application subclass to override this method
82
82
  # and use it to handle packets not routed by Stack middleware.
83
83
  def route(packet)
84
- raise 'default response from Orange::Application.route'
84
+ raise "default response from Orange::Application.route"
85
85
  end
86
86
 
87
87
  # Used to set optional values at class level. Will be merged into the options
@@ -7,7 +7,9 @@ module Orange
7
7
  #
8
8
  # All subclasses should start by declaring the "id" attribute. All models
9
9
  # are assumed to have an id attribute by most everything else, so it's
10
- # a good idea to have one.
10
+ # a good idea to have one. Also, we use this to tie in initialization before
11
+ # using the other dsl-ish methods (since these other methods are class level
12
+ # and would happen before initialize ever gets called)
11
13
  #
12
14
  # Orange::Carton adds many shortcut methods for adding various datatypes
13
15
  # to the model in a more declarative style (`id` vs `property :id, Serial`)
@@ -15,8 +17,14 @@ module Orange
15
17
  # For classes that don't need anything but scaffolding, there's the
16
18
  # as_resource method, which automatically creates a scaffolding resource
17
19
  # for the model.
20
+ #
21
+ # A model that doesn't need scaffolded at all could optionally forgo the carton
22
+ # class and just include DataMapper::Resource. All carton methods are to
23
+ # improve scaffolding capability.
18
24
  class Carton
19
- #
25
+ SCAFFOLD_OPTIONS = [:display_name] unless defined?(SCAFFOLD_OPTIONS)
26
+ # Declares a ModelResource subclass that scaffolds this carton
27
+ # The Subclass will have the name of the carton followed by "_Resource"
20
28
  def self.as_resource
21
29
  name = self.to_s
22
30
  eval <<-HEREDOC
@@ -26,22 +34,28 @@ module Orange
26
34
  HEREDOC
27
35
  end
28
36
 
29
- # Info for
37
+ # Include DataMapper types (required to be able to use Serial)
30
38
  include DataMapper::Types
31
39
 
40
+ # Do setup of object and declare an id
32
41
  def self.id
33
42
  include DataMapper::Resource
34
- self.property(:id, Serial)
43
+ property(:id, Serial)
35
44
  @scaffold_properties = []
36
45
  init
37
46
  end
38
47
 
48
+ def self.scaffold_properties
49
+ @scaffold_properties ||= []
50
+ end
51
+
52
+ # Stub init method
39
53
  def self.init
40
54
  end
41
55
 
42
56
  # Return properties that should be shown for a given context
43
- def self.form_props(context)
44
- @scaffold_properties.select{|p| p[:levels].include?(context) }
57
+ def self.form_props(context = :live)
58
+ scaffold_properties.select{|p| p[:levels].include?(context) }
45
59
  end
46
60
 
47
61
  # Helper to wrap properties into admin level
@@ -65,25 +79,40 @@ module Orange
65
79
  @levels = false
66
80
  end
67
81
 
82
+ def self.add_scaffold(name, type, dm_type, opts)
83
+ scaffold_properties << {:name => name, :type => type, :levels => @levels}.merge(opts) if @levels
84
+ opts = opts.delete_if{|k,v| SCAFFOLD_OPTIONS.include?(k)} # DataMapper doesn't like arbitrary opts
85
+ self.property(name, dm_type, opts)
86
+ end
87
+
68
88
  # Define a helper for title type database stuff
69
89
  # Show in a context if wrapped in one of the helpers
70
90
  def self.title(name, opts = {})
71
- @scaffold_properties << {:name => name, :type => :title, :levels => @levels}.merge(opts) if @levels
72
- self.property(name, String, opts)
91
+ add_scaffold(name, :title, String, opts)
73
92
  end
74
93
 
75
94
  # Define a helper for fulltext type database stuff
76
95
  # Show in a context if wrapped in one of the helpers
77
96
  def self.fulltext(name, opts = {})
78
- @scaffold_properties << {:name => name, :type => :fulltext, :levels => @levels, :opts => opts} if @levels
79
- self.property(name, Text, opts)
97
+ add_scaffold(name, :fulltext, Text, opts)
98
+ end
99
+
100
+ # Define a helper for boolean type database stuff
101
+ # Show in a context if wrapped in one of the helpers
102
+ def self.boolean(name, opts = {})
103
+ add_scaffold(name, :boolean, Boolean, opts)
80
104
  end
81
105
 
82
106
  # Define a helper for input type="text" type database stuff
83
107
  # Show in a context if wrapped in one of the helpers
84
108
  def self.text(name, opts = {})
85
- @scaffold_properties << {:name => name, :type => :text, :levels => @levels, :opts => opts} if @levels
86
- self.property(name, String, opts)
109
+ add_scaffold(name, :text, String, opts)
110
+ end
111
+
112
+ # Define a helper for type database stuff
113
+ # Show in a context if wrapped in one of the helpers
114
+ def self.expose(name, opts = {})
115
+ scaffold_properties << {:name => name, :type => :text, :levels => @levels, :opts => opts} if @levels
87
116
  end
88
117
 
89
118
  # Define a helper for input type="text" type database stuff
@@ -93,35 +122,34 @@ module Orange
93
122
  end
94
123
 
95
124
  # Override DataMapper to include context sensitivity (as set by helpers)
96
- def self.property(name, type, opts = {})
125
+ def self.scaffold_property(name, type, opts = {})
97
126
  my_type = type.to_s.downcase.to_sym
98
- @scaffold_properties << {:name => name, :type => my_type, :levels => @levels}.merge(opts) if @levels
99
- property(name, type, opts)
127
+ add_scaffold(name, my_type, type, opts)
100
128
  end
101
129
 
102
130
 
103
- # For more generic cases, use same syntax as DataMapper
104
- # This will make it an admin property though.
131
+ # For more generic cases, use same syntax as DataMapper does.
132
+ # The difference is that this will make it an admin property.
105
133
  def self.admin_property(name, type, opts = {})
106
134
  my_type = type.to_s.downcase.to_sym
107
- @scaffold_properties << {:name => name, :type => my_type, :levels => [:admin, :orange]}.merge(opts)
108
- property(name, type, opts)
135
+ opts[:levels] = [:admin, :orange]
136
+ add_scaffold(name, my_type, type, opts)
109
137
  end
110
138
 
111
- # For more generic cases, use same syntax as DataMapper
112
- # This will make it a front property though.
139
+ # For more generic cases, use same syntax as DataMapper does.
140
+ # The difference is that this will make it a front property.
113
141
  def self.front_property(name, type, opts = {})
114
142
  my_type = type.to_s.downcase.to_sym
115
- @scaffold_properties << {:name => name, :type => my_type, :levels => [:live, :admin, :orange]}.merge(opts)
116
- property(name, type, opts)
143
+ opts[:levels] = [:live, :admin, :orange]
144
+ add_scaffold(name, my_type, type, opts)
117
145
  end
118
146
 
119
- # For more generic cases, use same syntax as DataMapper
120
- # This will make it an orange property though.
147
+ # For more generic cases, use same syntax as DataMapper does.
148
+ # The difference is that this will make it an orange property.
121
149
  def self.orange_property(name, type, opts = {})
122
150
  my_type = type.to_s.downcase.to_sym
123
- @scaffold_properties << {:name => name, :type => my_type, :levels => [:orange]}.merge(opts)
124
- property(name, type, opts)
151
+ opts[:levels] = [:orange]
152
+ add_scaffold(name, my_type, type, opts)
125
153
  end
126
154
 
127
155
  end
@@ -1,6 +1,9 @@
1
- require 'dm-core'
1
+ require 'orange/carton'
2
2
 
3
3
  module Orange
4
+ # Defines a carton that belongs to a specific site.
5
+ # (Subclasses should be sure to call super if they override init, since
6
+ # it is what defines the relationship)
4
7
  class SiteCarton < Carton
5
8
  def self.init
6
9
  belongs_to :orange_site, 'Orange::Site'
@@ -57,6 +57,7 @@ module Orange
57
57
  load(Orange::Parser.new, :parser)
58
58
  load(Orange::Mapper.new, :mapper)
59
59
  load(Orange::PageParts.new, :page_parts)
60
+ load(Orange::AdminResource.new, :admin)
60
61
  afterLoad
61
62
  self
62
63
  end
@@ -165,9 +166,16 @@ module Orange
165
166
  # Accesses resources array, stored as a hash {:short_name => Resource instance,...}
166
167
  #
167
168
  # @param [Symbol] name the short name for the requested resource
169
+ # @param [optional, Boolean] ignore Whether to ignore any calls to resource if not found
170
+ # (false is default). This will allow method calls to non-existent resources. Should be
171
+ # used with caution.
168
172
  # @return [Orange::Resource] the resource for the given short name
169
- def [](name)
170
- @resources[name]
173
+ def [](name, ignore = false)
174
+ if ignore && !loaded?(name)
175
+ Ignore.new
176
+ else
177
+ @resources[name]
178
+ end
171
179
  end
172
180
 
173
181
  # Includes module in the Packet class
@@ -64,6 +64,17 @@ module Orange
64
64
  end
65
65
  end
66
66
 
67
+ # This class acts as a simple sink for ignoring messages, it will return itself
68
+ # for any message call. Orange::Core can optionally return this when trying
69
+ # to access resources so that you can make method calls to a resource that
70
+ # might not be really there. It will silently swallow any errors that might arrise,
71
+ # so this should be used with caution.
72
+ class Ignore
73
+ def method_missing(name, *args, &block)
74
+ return self
75
+ end
76
+ end
77
+
67
78
  # Simple class for evaluating options and allowing us to access them.
68
79
  class Options
69
80
 
@@ -3,13 +3,23 @@ require 'orange/middleware/base'
3
3
 
4
4
 
5
5
  module Orange::Middleware
6
-
6
+ # This middleware locks down entire contexts and puts them behind an openid
7
+ # login system. Currently only supports a single user id.
8
+ #
9
+ #
7
10
  class AccessControl < Base
8
- def init(*args)
11
+ # Sets up the options for the middleware
12
+ # @param [Hash] opts hash of options
13
+ # @option opts [Boolean] :openid Whether to use openid logins or not (currently only option)
14
+ # @option opts [Boolean] :handle_login Whether the access control system should handle
15
+ # presenting the login form, or let other parts of the app do that.
16
+ # @option opts [Boolean] :config_id Whether to use the id set in a config file
17
+
18
+ def init(opts = {})
9
19
  defs = {:locked => [:admin, :orange], :login => '/login',
10
20
  :handle_login => true, :openid => true, :config_id => true}
11
- opts = args.extract_with_defaults(defs)
12
- @openid = opts.has_key?(:openid) ? opts[:openid] : false
21
+ opts = opts.with_defaults!(defs)
22
+ @openid = opts[:openid]
13
23
  @locked = opts[:locked]
14
24
  @login = opts[:login]
15
25
  @handle = opts[:handle_login]
@@ -65,15 +75,14 @@ module Orange::Middleware
65
75
  packet['user.openid.response'] = resp
66
76
 
67
77
  after = packet.session.has_key?('user.after_login') ?
68
- packet.session['user.after_login'] : false
78
+ packet.session['user.after_login'] : '/'
69
79
  packet.session['user.after_login'] = false
70
80
 
71
81
  # Save id into session if we have one.
72
82
  packet.session['user.id'] = packet['user.id']
73
83
 
74
84
  # If the user was supposed to be going somewhere, redirect there
75
- packet.reroute(after) if after
76
- packet.reroute('/')
85
+ packet.reroute(after)
77
86
  false
78
87
  else
79
88
  packet.session['flash.error'] = resp.status
@@ -10,30 +10,69 @@ require 'orange/packet'
10
10
  # a basic call
11
11
  module Orange::Middleware
12
12
  class Base
13
+ # Initialize will set the core and downstream app, then call init
14
+ # subclasses should override init instead of initialize
15
+ # @param [Object] app a downstream app
16
+ # @param [Orange::Core] core the orange core
17
+ # @param [optional, Array] args any arguments
13
18
  def initialize(app, core, *args)
14
19
  @app = app
15
20
  @core = core
16
21
  init(*args)
17
22
  end
18
23
 
24
+ # A stub method that subclasses can override to handle initialization
25
+ # @return [void]
19
26
  def init(*args)
20
27
  end
21
28
 
29
+ # The standard Rack "call". By default, Orange Middleware wraps the env into
30
+ # an Orange::Packet and passes it on to #packet_call. Subclasses will typically
31
+ # override packet_call rather than overriding call directly.
32
+ #
33
+ # Orange Middleware
34
+ # should expect to have this method ignored by upstream Orange-aware apps in
35
+ # favor of calling packet_call directly.
36
+ # @param [Hash] env the hash of environment variables given by the rack interface.
37
+ # @return [Array] the standard Rack striplet of status, headers and content
22
38
  def call(env)
23
39
  packet = Orange::Packet.new(@core, env)
24
40
  packet_call(packet)
25
41
  end
26
42
 
43
+ # Like the standard call, but with the env hash already wrapped into a Packet
44
+ # This is called automatically as part of #call, so subclasses can have a packet
45
+ # without having to initialize it. It will be called directly by Orange-aware
46
+ # upstream middleware, skipping the step of initializing the packet during #call.
47
+ #
48
+ # Passing the packet downstream should be done with #pass rather than the Rack
49
+ # standard @app.call, since #pass will take the packet and do a #packet_call
50
+ # if possible.
51
+ # @param [Orange::Packet] packet the packet corresponding to this env
52
+ # @return [Array] the standard Rack striplet of status, headers and content
27
53
  def packet_call(packet)
28
54
  pass packet
29
55
  end
30
56
 
57
+ # Pass will sent the packet to the downstream app by calling call or packet call.
58
+ # Calling pass on a packet is the preferred way to call downstream apps, as it
59
+ # will call packet_call directly if possible (to avoid reinitializing the packet)
60
+ # @param [Orange::Packet] packet the packet to pass to downstream apps
61
+ # @return [Array] the standard Rack striplet of status, headers and content
31
62
  def pass(packet)
32
- @app.call(packet.env)
63
+ if @app.respond_to?(:packet_call)
64
+ @app.packet_call(packet)
65
+ else
66
+ @app.call(packet.env)
67
+ end
33
68
  end
34
69
 
70
+ # Accessor for @core, which is the stack's instance of Orange::Core
71
+ # @return [Orange::Core] the stack's instance of Orange::Core
35
72
  def orange; @core; end
36
73
 
74
+ # Help stack traces
75
+ # @return [String] string representing this middleware (#to_s)
37
76
  def inspect
38
77
  self.to_s
39
78
  end
@@ -0,0 +1,59 @@
1
+ require 'orange/middleware/base'
2
+
3
+ module Orange::Middleware
4
+ class FlexRouter < Base
5
+ def init(*args)
6
+ opts = args.extract_options!.with_defaults(:contexts => [:admin, :orange], :root_resource => :not_found)
7
+ @contexts = opts[:contexts]
8
+ @root_resource = opts[:root_resource]
9
+ end
10
+
11
+ # sets resource, resource_id, resource_action and resource_path
12
+ # /resource/id/action/[resource/path/if/any]
13
+ # /resource/action/[resource/path/if/any]
14
+ #
15
+ # In future - support for nested resources
16
+ def packet_call(packet)
17
+ return (pass packet) if packet['route.router'] # Don't route if other middleware
18
+ # already has
19
+ if(@contexts.include?(packet['route.context']))
20
+ path = packet['route.path'] || packet.request.path_info
21
+ parts = path.split('/')
22
+ pad = parts.shift
23
+ if !parts.empty?
24
+ resource = parts.shift
25
+ if orange.loaded?(resource.to_sym)
26
+ packet['route.resource'] = resource.to_sym
27
+ if !parts.empty?
28
+ second = parts.shift
29
+ if second =~ /^\d+$/
30
+ packet['route.resource_id'] = second
31
+ if !parts.empty?
32
+ packet['route.resource_action'] = parts.shift.to_sym
33
+ end
34
+ else
35
+ packet['route.resource_action'] = second.to_sym
36
+ end
37
+ end # end check for second part
38
+ else
39
+ parts.unshift(resource)
40
+ end # end check for loaded resource
41
+ end # end check for nonempty route
42
+
43
+ packet['route.resource'] ||= @root_resource
44
+ packet['route.resource_path'] = parts.unshift(pad).join('/')
45
+ packet['route.router'] = self
46
+ end # End context match if
47
+
48
+ pass packet
49
+ end
50
+
51
+ def route(packet)
52
+ resource = packet['route.resource']
53
+ raise 'resource not found' unless orange.loaded? resource
54
+ mode = packet['route.resource_action'] ||
55
+ (packet['route.resource_id'] ? :show : :list)
56
+ packet[:content] = orange[resource].view packet
57
+ end
58
+ end
59
+ end
@@ -1,7 +1,7 @@
1
1
  require 'orange/middleware/base'
2
2
 
3
3
  module Orange::Middleware
4
- # This middleware handles setting orange.env[:context]
4
+ # This middleware handles setting orange.env['route.context']
5
5
  # to a value based on the route, if any. The route is then
6
6
  # trimmed before continuing on.
7
7
  class RouteContext < Base
@@ -33,7 +33,7 @@ module Orange::Middleware
33
33
  packet['route.context'] = @default
34
34
  end
35
35
  end
36
- @app.call(packet.env)
36
+ pass packet
37
37
  end
38
38
  end
39
39
  end
@@ -45,7 +45,7 @@ module Orange::Middleware
45
45
  else
46
46
  packet['route.site_url'] = request.host
47
47
  end
48
- @app.call(packet.env)
48
+ pass packet
49
49
  end
50
50
  end
51
51
  end
@@ -11,7 +11,9 @@ module Orange::Middleware
11
11
  #
12
12
  # Be careful when you use this on public-facing sites as it could
13
13
  # reveal information helpful to attackers.
14
-
14
+ #
15
+ # Orange::Middleware::ShowExceptions is a slightly modified
16
+ # version of Rack::ShowExceptions
15
17
  class ShowExceptions < Base
16
18
  CONTEXT = 7
17
19
 
@@ -11,7 +11,16 @@ module Orange::Middleware
11
11
  def packet_call(packet)
12
12
  url = packet['route.site_url']
13
13
  site = Orange::Site.first(:url.like => url)
14
- packet['site'] = site if site
14
+ if site
15
+ packet['site'] = site
16
+ elsif
17
+ nil
18
+ else
19
+ s = Orange::Site.new({:url => packet['route.site_url'],
20
+ :name => 'An Orange Site'})
21
+ s.save
22
+ packet['site'] = s
23
+ end
15
24
  pass packet
16
25
  end
17
26
  end
@@ -29,5 +38,9 @@ module Orange
29
38
 
30
39
  class SiteResource < ModelResource
31
40
  use Orange::Site
41
+ def afterLoad
42
+ orange[:admin, true].add_link('Settings', :resource => @my_orange_name,
43
+ :text => 'Site')
44
+ end
32
45
  end
33
46
  end