resource_full 0.7.8 → 0.7.9
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.rdoc +7 -9
- data/lib/resource_full.rb +1 -1
- data/lib/resource_full/query.rb +10 -1
- data/lib/resource_full/render.rb +9 -7
- data/lib/resource_full/render/json.rb +3 -3
- data/lib/resource_full/render/xml.rb +4 -3
- data/lib/resource_full/version.rb +1 -1
- data/spec/resource_full/query_spec.rb +52 -0
- data/spec/resource_full/render/json_spec.rb +1 -1
- data/spec/resource_full/render/xml_spec.rb +3 -3
- metadata +3 -3
data/README.rdoc
CHANGED
@@ -1,16 +1,14 @@
|
|
1
|
-
= ResourceFull
|
1
|
+
= ResourceFull
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
== DESCRIPTION
|
6
|
-
|
7
|
-
ResourceFull integrates with ActionController to provide a comprehensive
|
8
|
-
RESTful resource modeling and querying framework. It provides parameter
|
3
|
+
ResourceFull provides a fully-compliant ActiveResource server implementation
|
4
|
+
built on ActionController. Additionally, it provides RESTful parameter
|
9
5
|
queryability, paging, sorting, separation of controller concerns, multiple
|
10
6
|
formats (HTML, XML, JSON), CRUD access permissions, and API metadata
|
11
7
|
surrounding the resource itself. It's opinionated but is intended to provide
|
12
8
|
you with as much as possible without limiting your ability to customize its
|
13
|
-
behavior.
|
9
|
+
behavior. Unless overridden, it uses Rails' default to_xml and to_json methods
|
10
|
+
to provide object serialization, which ActiveResource expects but many REST
|
11
|
+
clients will not.
|
14
12
|
|
15
13
|
== GOALS
|
16
14
|
|
@@ -78,7 +76,7 @@ add +map.api+ to your +routes.rb+ file.
|
|
78
76
|
|
79
77
|
(The MIT License)
|
80
78
|
|
81
|
-
Copyright (c)
|
79
|
+
Copyright (c) 2010 Brian Guthrie
|
82
80
|
|
83
81
|
Permission is hereby granted, free of charge, to any person obtaining
|
84
82
|
a copy of this software and associated documentation files (the
|
data/lib/resource_full.rb
CHANGED
@@ -22,6 +22,6 @@ require File.dirname(__FILE__) + '/resource_full/version'
|
|
22
22
|
require File.dirname(__FILE__) + '/resource_full/base'
|
23
23
|
|
24
24
|
# REST API
|
25
|
-
require File.dirname(__FILE__) + '/resource_full/models/resourced_route
|
25
|
+
require File.dirname(__FILE__) + '/resource_full/models/resourced_route'
|
26
26
|
require File.dirname(__FILE__) + '/resource_full/controllers/resources_controller'
|
27
27
|
require File.dirname(__FILE__) + '/resource_full/controllers/routes_controller'
|
data/lib/resource_full/query.rb
CHANGED
@@ -253,15 +253,18 @@ module ResourceFull
|
|
253
253
|
# Indicates that the resource should be queryable with the given parameters, which will be pulled from
|
254
254
|
# the params hash on an index or count call. Accepts the following options:
|
255
255
|
#
|
256
|
+
# * :scope => (scope) : Use a scope. The value of this parameter may be a symbol (named scope), lambda, or hash.
|
257
|
+
# Most other parameter options build scopes internally.
|
256
258
|
# * :fuzzy => true : Use a LIKE query instead of =.
|
257
259
|
# * :columns / :column => ... : Override the default column, or provide a list of columns to query for this value.
|
258
260
|
# * :from => :join_name : Indicate that this value should be queried by joining on another model. Should use
|
259
261
|
# a valid relationship from this controller's exposed model (e.g., :account if belongs_to :account is specified.)
|
260
262
|
# * :resource_identifier => true : Try to look up the resource controller for this value and honor its
|
261
263
|
# specified resource identifier. Useful for nesting relationships.
|
262
|
-
# * :
|
264
|
+
# * :allow_nil => true : Indicates that a nil value for a parameter should be taken to literally indicate
|
263
265
|
# that null values should be returned. This may be changed in the future to expect the literal string 'null'
|
264
266
|
# or some other reasonable standin.
|
267
|
+
# * :default => (value) : Default to this value if the parameter is not physically present in the request.
|
265
268
|
#
|
266
269
|
# Examples:
|
267
270
|
#
|
@@ -283,6 +286,12 @@ module ResourceFull
|
|
283
286
|
end
|
284
287
|
end
|
285
288
|
|
289
|
+
def filter_with_scope(scope=nil, &block_scope)
|
290
|
+
raise ArgumentError, "must provide a scope name, standard scope definition, or block scope" unless (scope || block_scope)
|
291
|
+
# TODO These should not require the use of dummy parameter names.
|
292
|
+
queryable_with "__unused__", :default => true, :scope => scope || block_scope
|
293
|
+
end
|
294
|
+
|
286
295
|
# :nodoc:
|
287
296
|
def clear_queryable_params!
|
288
297
|
@queryable_params = []
|
data/lib/resource_full/render.rb
CHANGED
@@ -8,10 +8,18 @@ module ResourceFull
|
|
8
8
|
controller.rescue_from Exception, :with => :handle_generic_exception_with_correct_response_format
|
9
9
|
end
|
10
10
|
|
11
|
+
# Override this method to provide custom error handling for the errors generated by
|
12
|
+
# your model object.
|
13
|
+
def http_error_code_for(errors)
|
14
|
+
if errors.any? { |message| message.include? CONFLICT_MESSAGE }
|
15
|
+
:conflict
|
16
|
+
else :unprocessable_entity end
|
17
|
+
end
|
18
|
+
|
11
19
|
private
|
12
20
|
|
13
21
|
CONFLICT_MESSAGE = if defined?(ActiveRecord::Errors)
|
14
|
-
if
|
22
|
+
if ActiveRecord::VERSION::STRING >= '2.1.0' && defined?(I18n)
|
15
23
|
(I18n.translate 'activerecord.errors.messages')[:taken]
|
16
24
|
else
|
17
25
|
ActiveRecord::Errors.default_error_messages[:taken]
|
@@ -20,12 +28,6 @@ module ResourceFull
|
|
20
28
|
"has already been taken"
|
21
29
|
end
|
22
30
|
|
23
|
-
def status_for(errors)
|
24
|
-
if errors.any? { |message| message.include? CONFLICT_MESSAGE }
|
25
|
-
:conflict
|
26
|
-
else :unprocessable_entity end
|
27
|
-
end
|
28
|
-
|
29
31
|
def handle_generic_exception_with_correct_response_format(exception)
|
30
32
|
if request.format.xml?
|
31
33
|
if defined?(ExceptionNotifiable) && defined?(ExceptionNotifier) && self.is_a?(ExceptionNotifiable) && !(consider_all_requests_local || local_request?)
|
@@ -59,12 +59,12 @@ module ResourceFull
|
|
59
59
|
def create_json
|
60
60
|
self.model_object = transactional_create_model_object
|
61
61
|
if model_object.errors.empty?
|
62
|
-
render :json => model_object.to_json(create_json_options), :status => :created, :location => send("#{model_name}_url", model_object.id)
|
62
|
+
render :json => model_object.to_json(create_json_options), :status => :created, :location => send("#{model_name}_url", model_object.id, :format => :json)
|
63
63
|
else
|
64
64
|
json_data = model_object.attributes
|
65
65
|
json_data[:errors] = {:list => model_object.errors,
|
66
66
|
:full_messages => model_object.errors.full_messages}
|
67
|
-
render :json => {json_class_name(model_object) => json_data}.to_json, :status =>
|
67
|
+
render :json => {json_class_name(model_object) => json_data}.to_json, :status => http_error_code_for(model_object.errors)
|
68
68
|
end
|
69
69
|
rescue => e
|
70
70
|
handle_generic_error_in_json(e)
|
@@ -88,7 +88,7 @@ module ResourceFull
|
|
88
88
|
json_data = model_object.attributes
|
89
89
|
json_data[:errors] = {:list => model_object.errors,
|
90
90
|
:full_messages => model_object.errors.full_messages}
|
91
|
-
render :json => {json_class_name(model_object) => json_data}.to_json, :status =>
|
91
|
+
render :json => {json_class_name(model_object) => json_data}.to_json, :status => http_error_code_for(model_object.errors)
|
92
92
|
end
|
93
93
|
rescue ActiveRecord::RecordNotFound => e
|
94
94
|
render :json => e.to_json, :status => :not_found
|
@@ -53,9 +53,9 @@ module ResourceFull
|
|
53
53
|
def create_xml
|
54
54
|
self.model_object = transactional_create_model_object
|
55
55
|
if model_object.errors.empty?
|
56
|
-
render :xml => model_object.to_xml({:root => model_name}.merge(create_xml_options)), :status => :created, :location => send("#{model_name}_url", model_object.id)
|
56
|
+
render :xml => model_object.to_xml({:root => model_name}.merge(create_xml_options)), :status => :created, :location => send("#{model_name}_url", model_object.id, :format => :xml)
|
57
57
|
else
|
58
|
-
render :xml => model_object.errors.to_xml, :status =>
|
58
|
+
render :xml => model_object.errors.to_xml, :status => http_error_code_for(model_object.errors)
|
59
59
|
end
|
60
60
|
rescue => e
|
61
61
|
handle_generic_error_in_xml(e)
|
@@ -76,7 +76,7 @@ module ResourceFull
|
|
76
76
|
if model_object.errors.empty?
|
77
77
|
render :xml => model_object.to_xml({:root => model_name}.merge(update_xml_options))
|
78
78
|
else
|
79
|
-
render :xml => model_object.errors.to_xml, :status =>
|
79
|
+
render :xml => model_object.errors.to_xml, :status => http_error_code_for(model_object.errors)
|
80
80
|
end
|
81
81
|
rescue ActiveRecord::RecordNotFound => e
|
82
82
|
render :xml => e.to_xml, :status => :not_found
|
@@ -101,6 +101,7 @@ module ResourceFull
|
|
101
101
|
|
102
102
|
private
|
103
103
|
def handle_generic_error_in_xml(exception)
|
104
|
+
logger.error exception
|
104
105
|
render :xml => exception, :status => :internal_server_error
|
105
106
|
end
|
106
107
|
end
|
@@ -459,4 +459,56 @@ describe "ResourceFull::Query", :type => :controller do
|
|
459
459
|
end
|
460
460
|
end
|
461
461
|
|
462
|
+
describe "filter_with_scope" do
|
463
|
+
controller_name "resource_full_mock_users"
|
464
|
+
|
465
|
+
# def self.filter_with_scope(scope)
|
466
|
+
# queryable_with :__unused__, :default => true, :scope => scope
|
467
|
+
# end
|
468
|
+
|
469
|
+
it "should automatically apply the given named scope to the generated query" do
|
470
|
+
ResourceFullMockUsersController.filter_with_scope :rich
|
471
|
+
ResourceFullMockUser.named_scope :rich, :conditions => [ "income >= 50000" ]
|
472
|
+
|
473
|
+
bob = ResourceFullMockUser.create! :income => 30000
|
474
|
+
alice = ResourceFullMockUser.create! :income => 50000
|
475
|
+
snoop_dogg = ResourceFullMockUser.create! :income => 1034831909138
|
476
|
+
|
477
|
+
get :index
|
478
|
+
assigns(:resource_full_mock_users).should_not include(bob)
|
479
|
+
assigns(:resource_full_mock_users).should include(alice, snoop_dogg)
|
480
|
+
end
|
481
|
+
|
482
|
+
it "should automatically apply the given hash scope to the generated query" do
|
483
|
+
ResourceFullMockUsersController.filter_with_scope :conditions => [ "income >= 50000" ]
|
484
|
+
|
485
|
+
bob = ResourceFullMockUser.create! :income => 30000
|
486
|
+
alice = ResourceFullMockUser.create! :income => 50000
|
487
|
+
snoop_dogg = ResourceFullMockUser.create! :income => 1034831909138
|
488
|
+
|
489
|
+
get :index
|
490
|
+
assigns(:resource_full_mock_users).should_not include(bob)
|
491
|
+
assigns(:resource_full_mock_users).should include(alice, snoop_dogg)
|
492
|
+
end
|
493
|
+
|
494
|
+
it "should automatically apply the given block as the lambda for a scope" do
|
495
|
+
ResourceFullMockUsersController.filter_with_scope do
|
496
|
+
{ :conditions => [ "birthdate > ?", 2.days.ago ] }
|
497
|
+
end
|
498
|
+
|
499
|
+
bob = ResourceFullMockUser.create! :birthdate => 1.day.ago
|
500
|
+
alice = ResourceFullMockUser.create! :birthdate => 3.days.ago
|
501
|
+
snoop_dogg = ResourceFullMockUser.create! :birthdate => Date.parse("1971-10-20")
|
502
|
+
|
503
|
+
get :index
|
504
|
+
assigns(:resource_full_mock_users).should include(bob)
|
505
|
+
assigns(:resource_full_mock_users).should_not include(alice, snoop_dogg)
|
506
|
+
end
|
507
|
+
|
508
|
+
it "should raise an ArgumentError if neither an argument scope nor a block scope is provided" do
|
509
|
+
lambda do
|
510
|
+
ResourceFullMockUsersController.filter_with_scope
|
511
|
+
end.should raise_error(ArgumentError)
|
512
|
+
end
|
513
|
+
end
|
462
514
|
end
|
@@ -158,7 +158,7 @@ describe "ResourceFull::Render::JSON", :type => :controller do
|
|
158
158
|
it "creates a new model object and places the location of the new object in the Location header" do
|
159
159
|
put :create, :resource_full_mock_user => {}, :format => 'json'
|
160
160
|
|
161
|
-
response.headers['Location'].should == resource_full_mock_user_url(ResourceFullMockUser.find(:first))
|
161
|
+
response.headers['Location'].should == resource_full_mock_user_url(ResourceFullMockUser.find(:first), :format => :json)
|
162
162
|
end
|
163
163
|
|
164
164
|
it "renders appropriate errors if a model validation fails" do
|
@@ -51,7 +51,7 @@ describe "ResourceFull::Render::XML" , :type => :controller do
|
|
51
51
|
it "creates a new model object and places the location of the new object in the Location header" do
|
52
52
|
put :create, :resource_full_namespaced_mock_record => {}, :format => 'xml'
|
53
53
|
|
54
|
-
response.headers['Location'].should == resource_full_namespaced_mock_record_url(ResourceFullSpec::ResourceFullNamespacedMockRecord.find(:first))
|
54
|
+
response.headers['Location'].should == resource_full_namespaced_mock_record_url(ResourceFullSpec::ResourceFullNamespacedMockRecord.find(:first), :format => :xml)
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
@@ -118,7 +118,7 @@ describe "ResourceFull::Render::XML" , :type => :controller do
|
|
118
118
|
it "creates a new model object and places the location of the new object in the Location header" do
|
119
119
|
put :create, :resource_full_namespaced_mock_record => {}, :format => 'xml'
|
120
120
|
|
121
|
-
response.headers['Location'].should == resource_full_namespaced_mock_record_url(ResourceFullSpec::ResourceFullNamespacedMockRecord.find(:first))
|
121
|
+
response.headers['Location'].should == resource_full_namespaced_mock_record_url(ResourceFullSpec::ResourceFullNamespacedMockRecord.find(:first), :format => :xml)
|
122
122
|
end
|
123
123
|
end
|
124
124
|
|
@@ -269,7 +269,7 @@ describe "ResourceFull::Render::XML" , :type => :controller do
|
|
269
269
|
it "creates a new model object and places the location of the new object in the Location header" do
|
270
270
|
put :create, :resource_full_mock_user => {}, :format => 'xml'
|
271
271
|
|
272
|
-
response.headers['Location'].should == resource_full_mock_user_url(ResourceFullMockUser.find(:first))
|
272
|
+
response.headers['Location'].should == resource_full_mock_user_url(ResourceFullMockUser.find(:first), :format => :xml)
|
273
273
|
end
|
274
274
|
|
275
275
|
it "renders appropriate errors if a model validation fails" do
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 7
|
8
|
-
-
|
9
|
-
version: 0.7.
|
8
|
+
- 9
|
9
|
+
version: 0.7.9
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Brian Guthrie
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-05-05 00:00:00 +05:30
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|