resource_full 0.7.8 → 0.7.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|