resource_full 0.7.6

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 (35) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +100 -0
  3. data/Rakefile +19 -0
  4. data/lib/resource_full/base.rb +140 -0
  5. data/lib/resource_full/controllers/resources.rb +16 -0
  6. data/lib/resource_full/controllers/resources_controller.rb +26 -0
  7. data/lib/resource_full/controllers/routes_controller.rb +16 -0
  8. data/lib/resource_full/core_extensions/api.rb +26 -0
  9. data/lib/resource_full/core_extensions/exception.rb +25 -0
  10. data/lib/resource_full/core_extensions/from_json.rb +13 -0
  11. data/lib/resource_full/core_extensions/module.rb +13 -0
  12. data/lib/resource_full/dispatch.rb +196 -0
  13. data/lib/resource_full/models/resourced_route.rb +84 -0
  14. data/lib/resource_full/query.rb +337 -0
  15. data/lib/resource_full/render/html.rb +74 -0
  16. data/lib/resource_full/render/json.rb +107 -0
  17. data/lib/resource_full/render/xml.rb +106 -0
  18. data/lib/resource_full/render.rb +63 -0
  19. data/lib/resource_full/retrieve.rb +87 -0
  20. data/lib/resource_full/version.rb +9 -0
  21. data/lib/resource_full.rb +31 -0
  22. data/spec/resource_full/base_spec.rb +88 -0
  23. data/spec/resource_full/controllers/resources_spec.rb +30 -0
  24. data/spec/resource_full/dispatch_spec.rb +274 -0
  25. data/spec/resource_full/models/resourced_route_spec.rb +62 -0
  26. data/spec/resource_full/query/parameter_spec.rb +61 -0
  27. data/spec/resource_full/query_spec.rb +462 -0
  28. data/spec/resource_full/render/html_spec.rb +4 -0
  29. data/spec/resource_full/render/json_spec.rb +258 -0
  30. data/spec/resource_full/render/xml_spec.rb +378 -0
  31. data/spec/resource_full/render_spec.rb +5 -0
  32. data/spec/resource_full/retrieve_spec.rb +184 -0
  33. data/spec/spec.opts +4 -0
  34. data/spec/spec_helper.rb +134 -0
  35. metadata +156 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Brian Guthrie
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,100 @@
1
+ = ResourceFull 0.7.5
2
+
3
+ * http://github.com/bguthrie/resource_full/
4
+
5
+ == DESCRIPTION
6
+
7
+ ResourceFull integrated with ActionController to provide a comprehensive
8
+ RESTful resource modeling and querying framework. It provides parameter
9
+ queryability, paging, sorting, separation of controller concerns, multiple
10
+ formats (HTML, XML, JSON), CRUD access permissions, and API metadata
11
+ surrounding the resource itself. It's opinionated but is intended to provide
12
+ you with as much as possible without limiting your ability to customize its
13
+ behavior.
14
+
15
+ == GOALS
16
+
17
+ The major distinguishing features of ResourceFull are:
18
+
19
+ * Queryability: the ability to designate certain parameters as queryable,
20
+ and map them to columns and SQL queries in the underlying model.
21
+ These queries chain together multiple named or unnamed scopes and can be
22
+ used either for a SQL SELECT or SELECT COUNT.
23
+ This functionality may be moved into a separate plugin in the future.
24
+ * Pagination and orderability: it automatically responds to requests for
25
+ limit, offset, order_by, and order_dir.
26
+ * Implementation: Default implementations for HTML, XML, and JSON controller requests.
27
+ * API documentation: ResourceFull-enabled Rails apps are able to provide automatic
28
+ documentation of the resources they expose, up to a point. (This is enabled
29
+ in large part by the queryability functionality and other resource-level
30
+ descriptors.) It's my hope that this can eventually be consumed by a Rails
31
+ resource registrar that acts as the single source of record for multiple REST
32
+ engines within an organization.
33
+
34
+ == EXAMPLE
35
+
36
+ class UsersController < ResourceFull::Base
37
+ identified_by :username, :unless => lambda { |id| id =~ /^[0-9]+$/ }
38
+
39
+ queryable_with :city, :state, :from => :address
40
+ queryable_with :name, :columns => [:first_name, :last_name]
41
+ queryable_with :email_address, :fuzzy => true
42
+ queryable_with :is_active, :scope => :active
43
+
44
+ orderable_by :city, :from => :address
45
+
46
+ responds_to :html
47
+ responds_to :xml, :only => [:read, :update]
48
+ end
49
+
50
+ class AddressesController < ResourceFull::Base
51
+ nests_within :users
52
+ queryable_with :city, :state
53
+ end
54
+
55
+ This allows for the following:
56
+
57
+ /users/bguthrie.xml
58
+ /users?name=Guthrie
59
+ /users?email_address=gmail
60
+ /users.xml?city=Chicago&name=Brian,Paul,Alicia&order_by=city&order_dir=asc
61
+ /users.xml?limit=10&offset=30
62
+ /users/bguthrie/addresses
63
+
64
+ >> UsersController.to_xml
65
+ => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<resource>\n <parameters type=\"array\"/>..."
66
+
67
+ == API DOCUMENTATION
68
+
69
+ To enable ResourceFull to document your resources at /resources.xml and /resources/<name>/routes.xml,
70
+ add +map.api+ to your +routes.rb+ file.
71
+
72
+ == CONTRIBUTORS
73
+
74
+ * {Vijay Aravamudhan}[http://github.com/vraravam]
75
+ * {Dirk Elmendorf}[http://github.com/economysizegeek]
76
+
77
+ == LICENSE
78
+
79
+ (The MIT License)
80
+
81
+ Copyright (c) 2009 Brian Guthrie
82
+
83
+ Permission is hereby granted, free of charge, to any person obtaining
84
+ a copy of this software and associated documentation files (the
85
+ 'Software'), to deal in the Software without restriction, including
86
+ without limitation the rights to use, copy, modify, merge, publish,
87
+ distribute, sublicense, and/or sell copies of the Software, and to
88
+ permit persons to whom the Software is furnished to do so, subject to
89
+ the following conditions:
90
+
91
+ The above copyright notice and this permission notice shall be
92
+ included in all copies or substantial portions of the Software.
93
+
94
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
95
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
96
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
97
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
98
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
99
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
100
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require File.dirname(__FILE__) + '/../../../config/environment'
2
+
3
+ require 'rake'
4
+ require 'spec/rake/spectask'
5
+ require 'rcov/rcovtask'
6
+
7
+ task :default => :spec
8
+
9
+ Spec::Rake::SpecTask.new do |t|
10
+ t.spec_opts = ['--options', 'spec/spec.opts']
11
+ end
12
+
13
+ require 'rcov/version'
14
+
15
+ Rcov::RcovTask.new do |t|
16
+ t.pattern = "spec/**/_spec.rb"
17
+ t.rcov_opts = [ "--spec-only" ]
18
+ t.output_dir = "coverage"
19
+ end
@@ -0,0 +1,140 @@
1
+ module ResourceFull
2
+ class ResourceNotFound < Exception; end
3
+
4
+ class Base < ActionController::Base
5
+ if ActionPack::VERSION::STRING < "2.3.0"
6
+ session :off, :if => lambda { |request| request.format.xml? || request.format.json? }
7
+ end
8
+
9
+ def model_name; self.class.model_name; end
10
+ def model_class; self.class.model_class; end
11
+
12
+ class << self
13
+ # Returns the list of all resources handled by ResourceFull.
14
+ def all_resources
15
+ ActionController::Routing.possible_controllers.map do |possible_controller|
16
+ controller_for(possible_controller)
17
+ end.select do |controller_class|
18
+ controller_class.ancestors.include?(self)
19
+ end
20
+ end
21
+
22
+ # Returns the controller for the given resource.
23
+ def controller_for(resource)
24
+ return resource if resource.is_a?(Class) && resource.ancestors.include?(ActionController::Base)
25
+ "#{resource.to_s.underscore}_controller".classify.constantize
26
+ rescue NameError
27
+ raise ResourceFull::ResourceNotFound, "not found: #{resource}"
28
+ end
29
+
30
+ private
31
+
32
+ def inherited(controller)
33
+ super(controller)
34
+ controller.send :extend, ClassMethods
35
+ controller.send :include,
36
+ ResourceFull::Retrieve,
37
+ ResourceFull::Query,
38
+ ResourceFull::Dispatch,
39
+ ResourceFull::Render
40
+ controller.send :alias_retrieval_methods!
41
+ end
42
+ end
43
+ end
44
+
45
+ module ClassMethods
46
+ attr_accessor_with_default :paginatable, true
47
+ attr_accessor_with_default :resource_identifier, :id
48
+
49
+ # Returns true if this resource is paginatable, which is to say, it recognizes and honors
50
+ # the :limit and :offset parameters if present in a query. True by default.
51
+ def paginatable?; paginatable; end
52
+
53
+ # The name of the model exposed by this resource. Derived from the name of the controller
54
+ # by default. See +exposes+.
55
+ def model_name
56
+ @model_class ? @model_class.name.demodulize.underscore : self.controller_name.singularize
57
+ end
58
+
59
+ # Indicates that this resource is identified by a database column other than the default
60
+ # :id.
61
+ # TODO This should honor the model's primary key column but needn't be bound by it.
62
+ # TODO Refactor this.
63
+ # TODO Improve the documentation.
64
+ def identified_by(*args, &block)
65
+ opts = args.extract_options!
66
+ column = args.first
67
+ if !block.nil?
68
+ self.resource_identifier = block
69
+ elsif !column.nil?
70
+ if !opts.empty? && ( opts.has_key?(:if) || opts.has_key?(:unless) )
71
+ if opts[:unless] == :id_numeric
72
+ opts[:unless] = lambda { |id| id =~ /^[0-9]+$/ }
73
+ end
74
+
75
+ # Negate the condition to generate an :if from an :unless.
76
+ condition = opts[:if] || lambda { |id| not opts[:unless].call(id) }
77
+
78
+ self.resource_identifier = lambda do |id|
79
+ if condition.call(id)
80
+ column
81
+ else :id end
82
+ end
83
+ else
84
+ self.resource_identifier = column
85
+ end
86
+ else
87
+ raise ArgumentError, "identified_by expects either a block or a column name and some options"
88
+ end
89
+ end
90
+
91
+ # The class of the model exposed by this resource. Derived from the model name. See +exposes+.
92
+ def model_class
93
+ @model_class ||= model_name.camelize.constantize
94
+ end
95
+
96
+ # Indicates that the CRUD methods should be called on the given class. Accepts
97
+ # either a class object or the name of the desired model.
98
+ def exposes(model_class)
99
+ remove_retrieval_methods!
100
+ @model_class = model_class.to_s.singularize.camelize.constantize
101
+ alias_retrieval_methods!
102
+ end
103
+
104
+ # Renders the resource as XML.
105
+ def to_xml(opts={})
106
+ { :name => self.controller_name,
107
+ :parameters => self.queryable_params,
108
+ :identifier => self.xml_identifier
109
+ }.to_xml(opts.merge(:root => "resource"))
110
+ end
111
+
112
+ protected
113
+
114
+ def xml_identifier
115
+ (self.resource_identifier.is_a?(Proc) ? self.resource_identifier.call(nil) : self.resource_identifier).to_s
116
+ end
117
+
118
+ private
119
+
120
+ def alias_retrieval_methods!
121
+ define_method("new_#{model_name}") { new_model_object }
122
+ define_method("find_#{model_name}") { find_model_object }
123
+ define_method("create_#{model_name}") { create_model_object }
124
+ define_method("update_#{model_name}") { update_model_object }
125
+ define_method("destroy_#{model_name}") { destroy_model_object }
126
+ define_method("find_all_#{model_name.pluralize}") { find_all_model_objects }
127
+ define_method("count_all_#{model_name.pluralize}") { count_all_model_objects }
128
+ end
129
+
130
+ def remove_retrieval_methods!
131
+ remove_method "new_#{model_name}" if method_defined? "new_#{model_name}"
132
+ remove_method "find_#{model_name}" if method_defined? "find_#{model_name}"
133
+ remove_method "create_#{model_name}" if method_defined? "create_#{model_name}"
134
+ remove_method "update_#{model_name}" if method_defined? "update_#{model_name}"
135
+ remove_method "destroy_#{model_name}" if method_defined? "destroy_#{model_name}"
136
+ remove_method "find_all_#{model_name.pluralize}" if method_defined? "find_all_#{model_name.pluralize}"
137
+ remove_method "count_all_#{model_name.pluralize}" if method_defined? "count_all_#{model_name.pluralize}"
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,16 @@
1
+ module ResourceFull
2
+ module Controllers
3
+ class ResourcesController < ResourceFull::Base
4
+ responds_to :xml
5
+
6
+ protected
7
+ def find_all_resources
8
+ ResourceFull::Base.all_resources
9
+ end
10
+
11
+ def find_resource
12
+ ResourceFull::Base.controller_for(params[:id].pluralize)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ module ResourceFull
2
+ module Controllers
3
+ class ResourcesController < ResourceFull::Base
4
+ responds_to :xml, :only => [ :read ]
5
+
6
+ def index_xml
7
+ render :xml => find_all_resources.to_xml(:root => "resources")
8
+ end
9
+
10
+ def show_xml
11
+ render :xml => find_resource.to_xml
12
+ rescue ResourceFull::ResourceNotFound => e
13
+ render :xml => e.to_xml, :status => :not_found
14
+ end
15
+
16
+ protected
17
+ def find_all_resources
18
+ ResourceFull::Base.all_resources
19
+ end
20
+
21
+ def find_resource
22
+ ResourceFull::Base.controller_for(params[:id])
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ module ResourceFull
2
+ module Controllers
3
+ class RoutesController < ResourceFull::Base
4
+ exposes ResourceFull::Models::ResourcedRoute
5
+ responds_to :xml, :only => [ :read ]
6
+
7
+ def index_xml
8
+ render :xml => find_all_routes.to_xml(:root => "routes")
9
+ end
10
+
11
+ def find_all_routes
12
+ ResourceFull::Models::ResourcedRoute.find :all, params
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ module ResourceFull
2
+ module CoreExtensions
3
+ module API
4
+
5
+ # Generate ActionController routes for RESTful services documentation.
6
+ def api
7
+ with_options :format => 'xml', :conditions => { :method => :get } do |map|
8
+ map.named_route 'resource', '/resources/:id.xml',
9
+ :action => 'show', :controller => 'resource_full/controllers/resources'
10
+ map.named_route 'resources', '/resources.xml',
11
+ :action => 'index', :controller => 'resource_full/controllers/resources'
12
+ map.named_route 'resources_route', '/resources/:resource_id/routes/:id.xml',
13
+ :action => 'show', :controller => 'resource_full/controllers/routes'
14
+ map.named_route 'resources_routes', '/resources/:resource_id/routes.xml',
15
+ :action => 'index', :controller => 'resource_full/controllers/routes'
16
+ map.named_route 'routes', '/routes.xml',
17
+ :action => 'index', :controller => 'resource_full/controllers/routes'
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ class ActionController::Routing::RouteSet::Mapper
25
+ include ResourceFull::CoreExtensions::API
26
+ end
@@ -0,0 +1,25 @@
1
+ module ResourceFull
2
+ module CoreExtensions
3
+ module Exception
4
+ def to_xml(options = {})
5
+ options[:indent] ||= 2
6
+ options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
7
+ options[:builder].instruct! unless options[:skip_instruct]
8
+
9
+ options[:builder].errors {
10
+ options[:builder].error self.to_s
11
+ options[:builder].error self.backtrace if options[:include_backtrace] == true
12
+ }
13
+ end
14
+
15
+ def to_json(options = {})
16
+ {"error" => {:text => self.to_s,
17
+ :backtrace => self.backtrace}}.to_json
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ class Exception
24
+ include ResourceFull::CoreExtensions::Exception
25
+ end
@@ -0,0 +1,13 @@
1
+ module ResourceFull
2
+ module CoreExtensions
3
+ module Hash
4
+ def from_json(json)
5
+ ActiveSupport::JSON.decode json
6
+ end
7
+ end
8
+ end
9
+ end
10
+
11
+ class Hash
12
+ extend ResourceFull::CoreExtensions::Hash
13
+ end
@@ -0,0 +1,13 @@
1
+ module ResourceFull
2
+ module CoreExtensions
3
+ module Module
4
+ def simple_name
5
+ name.split("::").last
6
+ end
7
+ end
8
+ end
9
+ end
10
+
11
+ class Module
12
+ include ResourceFull::CoreExtensions::Module
13
+ end
@@ -0,0 +1,196 @@
1
+ module ResourceFull
2
+ module Dispatch
3
+ class << self
4
+ def included(controller)
5
+ super(controller)
6
+ controller.send :extend, ClassMethods
7
+ controller.before_filter :ensure_sets_format_when_ie7
8
+ controller.before_filter :ensure_responds_to_format
9
+ controller.before_filter :ensure_responds_to_method
10
+ end
11
+ end
12
+
13
+ module ClassMethods
14
+ DEFAULT_FORMATS = [ :xml, :html, :json ]
15
+
16
+ CRUD_METHODS_TO_ACTIONS = {
17
+ :create => [ :create, :new ],
18
+ :read => [ :show, :index, :count ],
19
+ :update => [ :update, :edit ],
20
+ :delete => [ :destroy ]
21
+ }
22
+
23
+ # Indicates that the controller responds to one of the requested CRUD (create, read,
24
+ # update, delete) methods. These correspond to controller methods in the following
25
+ # manner:
26
+ #
27
+ # * Create: create, new
28
+ # * Read: show, index, count
29
+ # * Update: update, edit
30
+ # * Delete: destroy
31
+ #
32
+ # By default, a format supports all of the above methods, unless you specify otherwise.
33
+ # Override these defaults by using the :only or :except options. For example,
34
+ #
35
+ # responds_to :xml, :only => [:create, :delete]
36
+ #
37
+ # A controller may be reset back to default responds (xml, html, all CRUD methods) by
38
+ # specifying responds_to :defaults.
39
+ def responds_to(*formats)
40
+ if formats.first == :defaults
41
+ @renderable_formats = default_responds
42
+ @renderable_formats_overridden = false
43
+ return
44
+ end
45
+
46
+ opts = formats.extract_options!
47
+
48
+ supported_crud_methods = if opts[:only]
49
+ [ opts[:only] ].flatten
50
+ elsif opts[:except]
51
+ possible_crud_methods - [ opts[:except] ].flatten
52
+ else
53
+ possible_crud_methods
54
+ end
55
+
56
+ unless renderable_formats_already_overridden?
57
+ @renderable_formats = {}
58
+ @renderable_formats_overridden = true
59
+ end
60
+
61
+ formats.each do |format|
62
+ renderable_formats[format] = supported_crud_methods
63
+ end
64
+ end
65
+
66
+ # A list of symbols of all allowed formats (e.g. :xml, :html)
67
+ def allowed_formats
68
+ renderable_formats.keys
69
+ end
70
+
71
+ # A list of symbols of all allowed CRUD methods (e.g. :create, :delete)
72
+ def allowed_methods(format=:html)
73
+ renderable_formats[format] || []
74
+ end
75
+
76
+ # A list of symbols of all allowed controller actions (e.g. :show, :destroy) derived from
77
+ # the allowed CRUD actions.
78
+ def allowed_actions(format=:html)
79
+ renderable_formats[format].sum {|crud_action| CRUD_METHODS_TO_ACTIONS[crud_action]}
80
+ end
81
+
82
+ # A list of all possible CRUD actions that this framework understands, which is to say,
83
+ # the core Rails actions plus +count+ (and perhaps others eventually).
84
+ def possible_actions
85
+ CRUD_METHODS_TO_ACTIONS.values.sum([])
86
+ end
87
+
88
+ # Returns true if the request format is an allowed format.
89
+ def responds_to_request_format?(request)
90
+ allowed_formats.include? extract_request_format(request)
91
+ end
92
+
93
+ # Returns true if the request action is an allowed action as defined by the allowed CRUD methods.
94
+ def responds_to_request_action?(request, action)
95
+ # TODO Consider using ActionController's +verify+ method in preference to this.
96
+ # TODO We don't verify custom methods yet, so ignore them.
97
+ return true unless possible_actions.include?(action.to_sym)
98
+ allowed_actions(extract_request_format(request)).include? action.to_sym
99
+ end
100
+
101
+ protected
102
+
103
+ def renderable_formats
104
+ @renderable_formats ||= default_responds
105
+ end
106
+
107
+ private
108
+
109
+ def possible_crud_methods
110
+ CRUD_METHODS_TO_ACTIONS.keys
111
+ end
112
+
113
+ def extract_request_format(request)
114
+ request.format.html? ? :html : request.format.to_sym
115
+ end
116
+
117
+ def renderable_formats_already_overridden?
118
+ @renderable_formats_overridden
119
+ end
120
+
121
+ def default_responds
122
+ returning({}) do |responses|
123
+ DEFAULT_FORMATS.each do |format|
124
+ responses[format] = CRUD_METHODS_TO_ACTIONS.keys.dup
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ [:index, :count, :show, :new, :create, :edit, :update, :destroy].each do |name|
131
+ define_method(name) { dispatch_to name }
132
+ end
133
+
134
+ protected
135
+
136
+ def model_object=(object)
137
+ instance_variable_set "@#{model_name}", object
138
+ end
139
+
140
+ def model_object
141
+ instance_variable_get "@#{model_name}"
142
+ end
143
+
144
+ def model_objects=(objects)
145
+ instance_variable_set "@#{model_name.pluralize}", objects
146
+ end
147
+
148
+ def model_objects
149
+ instance_variable_get "@#{model_name.pluralize}"
150
+ end
151
+
152
+ private
153
+
154
+ def ensure_sets_format_when_ie7
155
+ if user_agent_ie7?
156
+ if request_looks_like?('json', 'javascript')
157
+ request.format = 'json'
158
+ elsif request_looks_like?('xml')
159
+ request.format = 'xml'
160
+ else
161
+ request.format = 'html'
162
+ end
163
+ end
164
+ end
165
+
166
+ def user_agent_ie7?
167
+ request.headers["HTTP_USER_AGENT"] =~ /MSIE 7.0/
168
+ end
169
+
170
+ def request_looks_like?(*formats)
171
+ formats.any? do |format|
172
+ request.format.to_s =~ /#{format}/ || request.headers['REQUEST_URI'] =~ /\.#{format}[?]/
173
+ end
174
+ end
175
+
176
+ def ensure_responds_to_format
177
+ unless self.class.responds_to_request_format?(request)
178
+ render :text => "Resource does not have a representation in #{request.format.to_str} format", :status => :not_acceptable
179
+ end
180
+ end
181
+
182
+ def ensure_responds_to_method
183
+ unless self.class.responds_to_request_action?(request, params[:action])
184
+ render :text => "Resource does not allow #{params[:action]} action", :status => :method_not_allowed
185
+ end
186
+ end
187
+
188
+ def dispatch_to(method)
189
+ respond_to do |requested_format|
190
+ self.class.allowed_formats.each do |renderable_format|
191
+ requested_format.send(renderable_format) { send("#{method}_#{renderable_format}") }
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end