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 CHANGED
@@ -1,16 +1,14 @@
1
- = ResourceFull 0.7.5
1
+ = ResourceFull
2
2
 
3
- * http://github.com/bguthrie/resource_full/
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) 2009 Brian Guthrie
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.rb'
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'
@@ -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
- # * :allow_nils => true : Indicates that a nil value for a parameter should be taken to literally indicate
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 = []
@@ -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 ([Rails::VERSION::MAJOR, Rails::VERSION::MINOR] <=> [2,1]) >= 0 # if the rails version is 2.1 or greater...Í
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 => status_for(model_object.errors)
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 => status_for(model_object.errors)
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 => status_for(model_object.errors)
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 => status_for(model_object.errors)
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
@@ -2,7 +2,7 @@ module ResourceFull #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 7
5
- TINY = 8
5
+ TINY = 9
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  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
- - 8
9
- version: 0.7.8
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-04-20 00:00:00 +05:30
17
+ date: 2010-05-05 00:00:00 +05:30
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency