api_resource 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/.document +5 -0
  2. data/.rspec +3 -0
  3. data/Gemfile +29 -0
  4. data/Gemfile.lock +152 -0
  5. data/Guardfile +22 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.rdoc +19 -0
  8. data/Rakefile +49 -0
  9. data/VERSION +1 -0
  10. data/api_resource.gemspec +154 -0
  11. data/lib/api_resource.rb +129 -0
  12. data/lib/api_resource/association_activation.rb +19 -0
  13. data/lib/api_resource/associations.rb +169 -0
  14. data/lib/api_resource/associations/association_proxy.rb +115 -0
  15. data/lib/api_resource/associations/belongs_to_remote_object_proxy.rb +16 -0
  16. data/lib/api_resource/associations/dynamic_resource_scope.rb +23 -0
  17. data/lib/api_resource/associations/has_many_remote_object_proxy.rb +16 -0
  18. data/lib/api_resource/associations/has_one_remote_object_proxy.rb +24 -0
  19. data/lib/api_resource/associations/multi_argument_resource_scope.rb +15 -0
  20. data/lib/api_resource/associations/multi_object_proxy.rb +73 -0
  21. data/lib/api_resource/associations/related_object_hash.rb +12 -0
  22. data/lib/api_resource/associations/relation_scope.rb +30 -0
  23. data/lib/api_resource/associations/resource_scope.rb +34 -0
  24. data/lib/api_resource/associations/scope.rb +107 -0
  25. data/lib/api_resource/associations/single_object_proxy.rb +81 -0
  26. data/lib/api_resource/attributes.rb +162 -0
  27. data/lib/api_resource/base.rb +587 -0
  28. data/lib/api_resource/callbacks.rb +49 -0
  29. data/lib/api_resource/connection.rb +171 -0
  30. data/lib/api_resource/core_extensions.rb +7 -0
  31. data/lib/api_resource/custom_methods.rb +119 -0
  32. data/lib/api_resource/exceptions.rb +87 -0
  33. data/lib/api_resource/formats.rb +14 -0
  34. data/lib/api_resource/formats/json_format.rb +25 -0
  35. data/lib/api_resource/formats/xml_format.rb +36 -0
  36. data/lib/api_resource/local.rb +12 -0
  37. data/lib/api_resource/log_subscriber.rb +15 -0
  38. data/lib/api_resource/mocks.rb +269 -0
  39. data/lib/api_resource/model_errors.rb +86 -0
  40. data/lib/api_resource/observing.rb +29 -0
  41. data/lib/api_resource/railtie.rb +22 -0
  42. data/lib/api_resource/scopes.rb +45 -0
  43. data/spec/lib/associations_spec.rb +656 -0
  44. data/spec/lib/attributes_spec.rb +121 -0
  45. data/spec/lib/base_spec.rb +504 -0
  46. data/spec/lib/callbacks_spec.rb +68 -0
  47. data/spec/lib/connection_spec.rb +76 -0
  48. data/spec/lib/local_spec.rb +20 -0
  49. data/spec/lib/mocks_spec.rb +28 -0
  50. data/spec/lib/model_errors_spec.rb +29 -0
  51. data/spec/spec_helper.rb +36 -0
  52. data/spec/support/mocks/association_mocks.rb +46 -0
  53. data/spec/support/mocks/error_resource_mocks.rb +21 -0
  54. data/spec/support/mocks/test_resource_mocks.rb +43 -0
  55. data/spec/support/requests/association_requests.rb +14 -0
  56. data/spec/support/requests/error_resource_requests.rb +25 -0
  57. data/spec/support/requests/test_resource_requests.rb +31 -0
  58. data/spec/support/test_resource.rb +64 -0
  59. metadata +334 -0
@@ -0,0 +1,68 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ include ApiResource
4
+
5
+ describe "Should put callbacks around save, create, update, and destroy by default" do
6
+
7
+ before(:all) do
8
+ # This defines all the callbacks to check and see if they are fired
9
+ TestResource.class_eval <<-EOE, __FILE__, __LINE__ + 1
10
+ attr_accessor :s_val, :c_val, :u_val, :d_val
11
+ before_save :bs_cb; after_save :as_cb
12
+ before_create :bc_cb; after_create :ac_cb
13
+ before_update :bu_cb; after_update :au_cb
14
+ before_destroy :bd_cb; after_destroy :ad_cb
15
+
16
+ def bs_cb
17
+ @s_val = 1
18
+ end
19
+ def as_cb
20
+ @s_val += 1
21
+ end
22
+ def bc_cb
23
+ @c_val = 1
24
+ end
25
+ def ac_cb
26
+ @c_val += 1
27
+ end
28
+ def bu_cb
29
+ @u_val = 1
30
+ end
31
+ def au_cb
32
+ @u_val += 1
33
+ end
34
+ def bd_cb
35
+ @d_val = 1
36
+ end
37
+ def ad_cb
38
+ @d_val += 1
39
+ end
40
+ EOE
41
+ end
42
+
43
+ it "should fire save and create callbacks when saving a new record" do
44
+ tr = TestResource.new(:name => "Ethan", :age => 20)
45
+ tr.save.should be_true
46
+ tr.s_val.should eql(2)
47
+ tr.c_val.should eql(2)
48
+ tr.u_val.should be_nil
49
+ end
50
+
51
+ it "should fire save and update callbacks when updating a record" do
52
+ tr = TestResource.new(:id => 1, :name => "Ethan", :age => 20)
53
+ tr.name = "Test"
54
+ tr.age = 21
55
+ tr.save.should be_true
56
+ tr.s_val.should eql(2)
57
+ tr.c_val.should be_nil
58
+ tr.u_val.should eql(2)
59
+ end
60
+
61
+ it "should only fire destroy callbacks when destroying a record" do
62
+ tr = TestResource.new(:id => 1, :name => "Ethan", :age => 20)
63
+ tr.destroy.should be_true
64
+ tr.d_val.should eql(2)
65
+ tr.s_val.should be_nil
66
+ end
67
+
68
+ end
@@ -0,0 +1,76 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ include ApiResource
4
+
5
+ describe Connection do
6
+
7
+ it "should be able to set the token directly on ApiResource" do
8
+ ApiResource.token = "123"
9
+ ApiResource::Base.token.should eql "123"
10
+ end
11
+
12
+ it "should be able to set a default token value, which is passed through each request" do
13
+ TestResource.connection.expects(:get).with("/test_resources/1.json", "Lifebooker-Token" => "abc")
14
+ ApiResource::Base.token = "abc"
15
+ TestResource.find(1)
16
+ end
17
+
18
+ it "should be able to set a token for a given block" do
19
+ ApiResource::Base.token = "123456"
20
+ begin
21
+ ApiResource.with_token("testing") do
22
+ ApiResource::Base.token.should eql "testing"
23
+ raise "AAAH"
24
+ end
25
+ rescue => e
26
+ # should still reset the token
27
+ end
28
+ ApiResource::Base.token.should eql "123456"
29
+ end
30
+
31
+ it "should provider a method to regenerate its connection" do
32
+ conn = ApiResource::Base.connection
33
+ conn.should be ApiResource::Base.connection
34
+ ApiResource.reset_connection
35
+ conn.should_not be ApiResource::Base.connection
36
+ end
37
+
38
+ context "No Mocks" do
39
+ before(:all) do
40
+ ApiResource::Mocks.remove
41
+ end
42
+ after(:all) do
43
+ ApiResource::Mocks.init
44
+ ApiResource.timeout = 10
45
+ ApiResource.open_timeout = 10
46
+ end
47
+ it "should be able to set a timeout for its connection" do
48
+ ApiResource.timeout = 1
49
+ ApiResource.timeout.should eql 1
50
+ ApiResource.open_timeout = 1
51
+ ApiResource.open_timeout.should eql 1
52
+
53
+ ApiResource::Base.connection.send(:http, "/test").options[:timeout].should eql 1
54
+ ApiResource::Base.connection.send(:http, "/test").options[:open_timeout].should eql 1
55
+
56
+ ApiResource.timeout = 100
57
+ ApiResource::Base.connection.send(:http, "/test").options[:timeout].should eql 100
58
+
59
+ end
60
+
61
+ it "should time out if RestClient takes too long" do
62
+
63
+ # hopefully google won't actually respond this fast :)
64
+ ApiResource.timeout = 0.001
65
+ ApiResource::Base.site = "http://www.google.com"
66
+ lambda{
67
+ ApiResource::Base.connection.get("/")
68
+ }.should raise_error(ApiResource::RequestTimeout)
69
+
70
+ end
71
+
72
+ end
73
+
74
+
75
+
76
+ end
@@ -0,0 +1,20 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'json'
3
+
4
+ include ApiResource
5
+
6
+ describe "Local" do
7
+
8
+ it "should not go to the server to fetch a resource definition" do
9
+ ApiResource::Connection.any_instance.expects(:get).never
10
+ class MyTestResource < ApiResource::Local
11
+ scope :test, {:test => true}
12
+ end
13
+ mtr = MyTestResource.new
14
+ # should still have scopes
15
+ MyTestResource.reload_class_attributes
16
+ mtr.scopes.should_not be_blank
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,28 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'json'
3
+
4
+ include ApiResource
5
+
6
+ describe Mocks do
7
+
8
+ # we set up the mocks in spec helper, so we can just assert this
9
+ it "should hijack the connection" do
10
+ ApiResource::Mocks::Interface.any_instance.expects(:get).once.returns(
11
+ ApiResource::Mocks::MockResponse.new("", {:headers => {"Content-type" => "application/json"}, :status_code => 200})
12
+ )
13
+ TestResource.reload_class_attributes
14
+ end
15
+
16
+ it "should allow the user to raise errors for invalid responsed" do
17
+ old_err_status = ApiResource.raise_missing_definition_error
18
+ ApiResource::Base.raise_missing_definition_error = true
19
+
20
+ lambda {
21
+ class MyNewInvalidResource < ApiResource::Base; end
22
+ MyNewInvalidResource.new
23
+ }.should raise_error(ApiResource::ResourceNotFound)
24
+
25
+ ApiResource.raise_missing_definition_error = old_err_status
26
+ end
27
+
28
+ end
@@ -0,0 +1,29 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ include ApiResource
4
+
5
+ describe "Saving Resources with errors" do
6
+
7
+ before(:all) do
8
+ ErrorResource.include_root_in_json = true
9
+ end
10
+
11
+ context "Remote Errors" do
12
+
13
+ it "should be able to handle errors as a hash" do
14
+ t = ErrorResource.new(:name => "Ethan", :age => 12)
15
+ t.save.should be_false
16
+ t.errors.should_not be_nil
17
+ t.errors['name'].should_not be_nil
18
+ end
19
+
20
+ it "should be able to handle errors as full messages" do
21
+ t = ErrorFullMessageResource.new(:name => "Ethan", :age => 12)
22
+ t.save.should be_false
23
+ t.errors.should_not be_nil
24
+ t.errors['name'].should_not be_nil
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,36 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+
4
+ require 'bundler'
5
+ require 'api_resource'
6
+ require 'simplecov'
7
+
8
+ # Requires supporting files with custom matchers and macros, etc,
9
+ # in ./support/ and its subdirectories.
10
+ Bundler.require(:default, :development)
11
+ Debugger.start
12
+
13
+ SimpleCov.start do
14
+ add_filter "/spec/"
15
+ end
16
+
17
+ SimpleCov.at_exit do
18
+ SimpleCov.result.format!
19
+ end
20
+
21
+ # Requires supporting files with custom matchers and macros, etc,
22
+ # in ./support/ and its subdirectories.
23
+ #ApiResource.load_mocks_and_factories
24
+ ApiResource.site = 'http://localhost:3000'
25
+ ApiResource.format = :json
26
+ ApiResource.load_mocks_and_factories
27
+
28
+ ApiResource.logger.level = Log4r::INFO
29
+
30
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
31
+
32
+
33
+
34
+ RSpec.configure do |config|
35
+ config.mock_with :mocha
36
+ end
@@ -0,0 +1,46 @@
1
+ include ApiResource
2
+
3
+ Mocks.define do
4
+
5
+ endpoint('/single_object_association') do
6
+ get(HashDealer.roll(:test_association_resource), :params => {})
7
+ get(HashDealer.roll(:active_test_association_resource), :params => {:active => true})
8
+ get(HashDealer.roll(:active_birthday_test_association_resource), :params => {:active => true, :birthday => true})
9
+ end
10
+
11
+ endpoint('/multi_object_association') do
12
+ get((0..4).to_a.collect{HashDealer.roll(:test_association_resource)}, :params => {})
13
+ get((0..4).to_a.collect{HashDealer.roll(:active_test_association_resource)}, :params => {:active => true})
14
+ get((0..4).to_a.collect{HashDealer.roll(:active_test_association_resource)}, :params => {:active => false})
15
+ get((0..4).to_a.collect{HashDealer.roll(:active_birthday_test_association_resource)}, :params => {:active => true, :birthday => true})
16
+ end
17
+
18
+ endpoint("/has_one_objects/new") do
19
+ get({})
20
+ end
21
+
22
+ endpoint("/has_many_objects/new") do
23
+ get({
24
+ "attributes" => {
25
+ "public" => ["name"]
26
+ }
27
+ })
28
+ end
29
+
30
+ endpoint("/belongs_to_objects/new") do
31
+ get({})
32
+ end
33
+
34
+ endpoint("/test_associations/new") do
35
+ get({})
36
+ end
37
+
38
+ endpoint("/inner_classes/new") do
39
+ get({})
40
+ end
41
+
42
+ endpoint("/childern/new") do
43
+ get({})
44
+ end
45
+
46
+ end
@@ -0,0 +1,21 @@
1
+ include ApiResource
2
+
3
+ Mocks.define do
4
+
5
+ endpoint("/error_resources/new") do
6
+ get(HashDealer.roll(:new_error_resource))
7
+ end
8
+
9
+ endpoint("/error_resources") do
10
+ post(HashDealer.roll(:error_resource_errors), :params => {:error_resource => HashDealer.roll(:error_resource).matcher}, :status_code => 422)
11
+ end
12
+
13
+ endpoint("/error_full_message_resources/new") do
14
+ get(HashDealer.roll(:new_error_resource))
15
+ end
16
+
17
+ endpoint("/error_full_message_resources") do
18
+ post(HashDealer.roll(:error_resource_full_message_errors), :params => {:error_full_message_resource => HashDealer.roll(:error_resource).matcher}, :status_code => 422)
19
+ end
20
+
21
+ end
@@ -0,0 +1,43 @@
1
+ include ApiResource
2
+
3
+ Mocks.define do
4
+
5
+ endpoint("/test_resources/new") do
6
+ get(HashDealer.roll(:new_test_object))
7
+ end
8
+
9
+ endpoint("/test_resources") do
10
+ post(HashDealer.roll(:test_resource).merge(:id => 1), :params => {:test_resource => HashDealer.roll(:test_resource).matcher})
11
+ get((0..4).to_a.collect{HashDealer.roll(:test_resource)})
12
+ get((0..4).to_a.collect{HashDealer.roll(:test_resource)}, :params => {:active => true})
13
+ end
14
+
15
+ endpoint("/test_resources/:id") do
16
+ get(HashDealer.roll(:test_resource)) do |params|
17
+ self.merge(params)
18
+ end
19
+ delete({})
20
+ put({}, :params => {:test_resource => HashDealer.roll(:test_resource).matcher})
21
+ end
22
+
23
+ endpoint("/child_test_resources/new") do
24
+ get({})
25
+ end
26
+
27
+ endpoint("/child_test_resource2s/new") do
28
+ get({})
29
+ end
30
+
31
+ endpoint("/another_test_resources/new") do
32
+ get({})
33
+ end
34
+
35
+ endpoint("/test_classes/new") do
36
+ get({})
37
+ end
38
+
39
+ endpoint("/children/new") do
40
+ get({})
41
+ end
42
+
43
+ end
@@ -0,0 +1,14 @@
1
+ HashDealer.define(:test_association_resource) do
2
+ id{Kernel.rand(99999)}
3
+ name{Faker::Name.first_name}
4
+ age{Kernel.rand(99999)}
5
+ active(false)
6
+ end
7
+
8
+ HashDealer.define(:active_test_association_resource, :parent => :test_association_resource) do
9
+ active(true)
10
+ end
11
+
12
+ HashDealer.define(:active_birthday_test_association_resource, :parent => :active_test_association_resource) do
13
+ birthday{Date.today}
14
+ end
@@ -0,0 +1,25 @@
1
+ HashDealer.define(:new_error_resource) do
2
+ attributes({
3
+ :protected => [:id],
4
+ :public => [:name, :age],
5
+ })
6
+ end
7
+
8
+ HashDealer.define(:error_resource) do
9
+ name("Name")
10
+ age("age")
11
+ end
12
+
13
+ HashDealer.define(:error_resource_errors) do
14
+ errors({
15
+ :name => ["must not be empty"],
16
+ :age => ["must be a valid integer"]
17
+ })
18
+ end
19
+
20
+ HashDealer.define(:error_resource_full_message_errors) do
21
+ errors([
22
+ "Name cannot be empty",
23
+ "Age must be a valid integer"
24
+ ])
25
+ end
@@ -0,0 +1,31 @@
1
+ HashDealer.define(:new_test_object) do
2
+ attributes({
3
+ :protected => [:id],
4
+ :public => [:name, :age],
5
+ })
6
+ scopes({
7
+ :active => {:active => true},
8
+ :paginate => {:paginate => true, :per_page => :per_page, :current_page => :current_page}
9
+ })
10
+ associations({
11
+ :has_many => {:has_many_objects => {}},
12
+ :belongs_to => {:belongs_to_object => {}, :custom_name => {:class_name => "BelongsToObject"}},
13
+ :has_one => {:has_one_object => {}},
14
+ })
15
+ # Think of a use case for this
16
+ options({
17
+
18
+ })
19
+ end
20
+
21
+ HashDealer.define(:test_resource) do
22
+ name("name")
23
+ age("age")
24
+ end
25
+
26
+ HashDealer.define(:test_resource_errors) do
27
+ errors({
28
+ :name => ["can't be blank"],
29
+ :age => ["must be a valid number"]
30
+ })
31
+ end