active_rest_client 0.9.58

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.simplecov +4 -0
  5. data/Gemfile +4 -0
  6. data/Guardfile +9 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +585 -0
  9. data/Rakefile +3 -0
  10. data/active_rest_client.gemspec +34 -0
  11. data/lib/active_rest_client.rb +23 -0
  12. data/lib/active_rest_client/base.rb +128 -0
  13. data/lib/active_rest_client/caching.rb +84 -0
  14. data/lib/active_rest_client/configuration.rb +69 -0
  15. data/lib/active_rest_client/connection.rb +76 -0
  16. data/lib/active_rest_client/connection_manager.rb +21 -0
  17. data/lib/active_rest_client/headers_list.rb +47 -0
  18. data/lib/active_rest_client/instrumentation.rb +62 -0
  19. data/lib/active_rest_client/lazy_association_loader.rb +95 -0
  20. data/lib/active_rest_client/lazy_loader.rb +23 -0
  21. data/lib/active_rest_client/logger.rb +67 -0
  22. data/lib/active_rest_client/mapping.rb +65 -0
  23. data/lib/active_rest_client/proxy_base.rb +143 -0
  24. data/lib/active_rest_client/recording.rb +24 -0
  25. data/lib/active_rest_client/request.rb +412 -0
  26. data/lib/active_rest_client/request_filtering.rb +52 -0
  27. data/lib/active_rest_client/result_iterator.rb +66 -0
  28. data/lib/active_rest_client/validation.rb +60 -0
  29. data/lib/active_rest_client/version.rb +3 -0
  30. data/spec/lib/base_spec.rb +245 -0
  31. data/spec/lib/caching_spec.rb +179 -0
  32. data/spec/lib/configuration_spec.rb +105 -0
  33. data/spec/lib/connection_manager_spec.rb +36 -0
  34. data/spec/lib/connection_spec.rb +73 -0
  35. data/spec/lib/headers_list_spec.rb +61 -0
  36. data/spec/lib/instrumentation_spec.rb +59 -0
  37. data/spec/lib/lazy_association_loader_spec.rb +118 -0
  38. data/spec/lib/lazy_loader_spec.rb +25 -0
  39. data/spec/lib/logger_spec.rb +63 -0
  40. data/spec/lib/mapping_spec.rb +48 -0
  41. data/spec/lib/proxy_spec.rb +154 -0
  42. data/spec/lib/recording_spec.rb +34 -0
  43. data/spec/lib/request_filtering_spec.rb +72 -0
  44. data/spec/lib/request_spec.rb +471 -0
  45. data/spec/lib/result_iterator_spec.rb +104 -0
  46. data/spec/lib/validation_spec.rb +113 -0
  47. data/spec/spec_helper.rb +22 -0
  48. metadata +265 -0
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveRestClient::LazyLoader do
4
+ let(:request) { double("Request") }
5
+ let(:response) { double("Response") }
6
+
7
+ it "should not #call the passed in request during initialisation" do
8
+ request.should_not_receive(:call)
9
+ ActiveRestClient::LazyLoader.new(request)
10
+ end
11
+
12
+ it "should #call the passed in request if you check for response to a message" do
13
+ request.should_receive(:call)
14
+ loader = ActiveRestClient::LazyLoader.new(request)
15
+ loader.respond_to?(:each)
16
+ end
17
+
18
+ it "should #call the passed in request if you call a method and pass through the method" do
19
+ request.should_receive(:call).and_return(response)
20
+ response.should_receive(:valid).and_return(true)
21
+ loader = ActiveRestClient::LazyLoader.new(request)
22
+ expect(loader.valid).to be_true
23
+ end
24
+
25
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveRestClient::Instrumentation do
4
+ before :each do
5
+ ActiveRestClient::Logger.reset!
6
+ end
7
+
8
+ it "should log things to the Rails logger if available" do
9
+ class Rails
10
+ class << self
11
+ attr_accessor :logger
12
+ end
13
+ end
14
+
15
+ Rails.logger = double("Logger")
16
+ Rails.logger.should_receive(:debug)
17
+ Rails.logger.should_receive(:info)
18
+ Rails.logger.should_receive(:warn)
19
+ Rails.logger.should_receive(:error)
20
+ ActiveRestClient::Logger.debug("Hello world")
21
+ ActiveRestClient::Logger.info("Hello world")
22
+ ActiveRestClient::Logger.warn("Hello world")
23
+ ActiveRestClient::Logger.error("Hello world")
24
+ Object.send(:remove_const, :Rails)
25
+ end
26
+
27
+ it "should write to a logfile if one has been specified" do
28
+ ActiveRestClient::Logger.logfile = "/dev/null"
29
+ file = double('file')
30
+ File.should_receive(:open).with("/dev/null", "a").and_yield(file)
31
+ file.should_receive(:<<).with("Hello world")
32
+ ActiveRestClient::Logger.debug("Hello world")
33
+
34
+ file = double('file')
35
+ File.should_receive(:open).with("/dev/null", "a").and_yield(file)
36
+ file.should_receive(:<<).with("Hello info")
37
+ ActiveRestClient::Logger.info("Hello info")
38
+
39
+ file = double('file')
40
+ File.should_receive(:open).with("/dev/null", "a").and_yield(file)
41
+ file.should_receive(:<<).with("Hello error")
42
+ ActiveRestClient::Logger.error("Hello error")
43
+
44
+ file = double('file')
45
+ File.should_receive(:open).with("/dev/null", "a").and_yield(file)
46
+ file.should_receive(:<<).with("Hello warn")
47
+ ActiveRestClient::Logger.warn("Hello warn")
48
+ end
49
+
50
+ it "should append to its own messages list if neither Rails nor a logfile has been specified" do
51
+ File.should_not_receive(:open)
52
+ ActiveRestClient::Logger.debug("Hello world")
53
+ ActiveRestClient::Logger.info("Hello info")
54
+ ActiveRestClient::Logger.warn("Hello warn")
55
+ ActiveRestClient::Logger.error("Hello error")
56
+ expect(ActiveRestClient::Logger.messages.size).to eq(4)
57
+ expect(ActiveRestClient::Logger.messages[0]).to eq("Hello world")
58
+ expect(ActiveRestClient::Logger.messages[1]).to eq("Hello info")
59
+ expect(ActiveRestClient::Logger.messages[2]).to eq("Hello warn")
60
+ expect(ActiveRestClient::Logger.messages[3]).to eq("Hello error")
61
+ end
62
+
63
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ class MappingExampleBase
4
+ include ActiveRestClient::Mapping
5
+ end
6
+
7
+ class MappingExample < MappingExampleBase
8
+ get :test_get, "/get", tag:1, fake:"{result:true}", lazy:[:something]
9
+ put :test_put, "/put", tag:2
10
+ post :test_post, "/post", tag:3
11
+ delete :test_delete, "/delete", tag:4
12
+ end
13
+
14
+ describe ActiveRestClient::Mapping do
15
+ it "should support methods for get/put/post/delete for mapping" do
16
+ expect(EmptyExample).to respond_to(:get)
17
+ expect(EmptyExample).to respond_to(:put)
18
+ expect(EmptyExample).to respond_to(:post)
19
+ expect(EmptyExample).to respond_to(:delete)
20
+ end
21
+
22
+ it "should save URL for each mapped call" do
23
+ expect(MappingExample._calls[:test_get][:url]).to eq("/get")
24
+ expect(MappingExample._calls[:test_put][:url]).to eq("/put")
25
+ expect(MappingExample._calls[:test_post][:url]).to eq("/post")
26
+ expect(MappingExample._calls[:test_delete][:url]).to eq("/delete")
27
+ end
28
+
29
+ it "should save the correct method type for each mapped call" do
30
+ expect(MappingExample._calls[:test_get][:method]).to eq(:get)
31
+ expect(MappingExample._calls[:test_put][:method]).to eq(:put)
32
+ expect(MappingExample._calls[:test_post][:method]).to eq(:post)
33
+ expect(MappingExample._calls[:test_delete][:method]).to eq(:delete)
34
+ end
35
+
36
+ it "should remember options set for each mapped call" do
37
+ expect(MappingExample._calls[:test_get][:options][:fake]).to eq("{result:true}")
38
+ expect(MappingExample._calls[:test_get][:options][:lazy]).to eq([:something])
39
+ expect(MappingExample._calls[:test_get][:options][:tag]).to eq(1)
40
+ expect(MappingExample._calls[:test_put][:options][:tag]).to eq(2)
41
+ expect(MappingExample._calls[:test_post][:options][:tag]).to eq(3)
42
+ expect(MappingExample._calls[:test_delete][:options][:tag]).to eq(4)
43
+ end
44
+
45
+ it "should allow for mapped calls on the class" do
46
+ expect(MappingExample).to respond_to(:test_get)
47
+ end
48
+ end
@@ -0,0 +1,154 @@
1
+ require 'spec_helper'
2
+ require 'active_support/core_ext/hash'
3
+
4
+ class ProxyExample < ActiveRestClient::ProxyBase
5
+ get "/all" do
6
+ url.gsub!("/all", "/getAll")
7
+ passthrough
8
+ end
9
+
10
+ get "/old" do
11
+ url "/new"
12
+ passthrough
13
+ end
14
+
15
+ get "/list" do
16
+ get_params[:first_name] = get_params.delete(:fname)
17
+ get_params[:age] = 12
18
+ get_params.delete(:lname)
19
+ passthrough
20
+ end
21
+
22
+ post "/create" do
23
+ post_params[:first_name] = post_params.delete(:fname)
24
+ post_params[:age] = 12
25
+ post_params.delete(:lname)
26
+ passthrough
27
+ end
28
+
29
+ put "/update" do
30
+ body "MY-BODY-CONTENT"
31
+ passthrough
32
+ end
33
+
34
+ delete '/remove' do
35
+ passthrough
36
+ end
37
+
38
+ get "/change-format" do
39
+ response = passthrough
40
+ translate(response) do |body|
41
+ body["first_name"] = body.delete("fname")
42
+ body
43
+ end
44
+ end
45
+
46
+ get "/hal_test/:id" do
47
+ response = passthrough
48
+ translate(response) do |body|
49
+ body["_links"] = {"test" => {href:"/this/is/a/test"}}
50
+ body
51
+ end
52
+ end
53
+
54
+ get "/param/:id/:name" do
55
+ render "{\"id\":\"#{params[:id]}\", \"name\":\"#{params[:name]}\"}"
56
+ end
57
+
58
+ get "/fake" do
59
+ render "{\"id\":1234}"
60
+ end
61
+ end
62
+
63
+ class ProxyClientExample < ActiveRestClient::Base
64
+ proxy ProxyExample
65
+ base_url "http://www.example.com"
66
+
67
+ get :all, "/all"
68
+ get :old, "/old"
69
+ get :list, "/list"
70
+ get :fake, "/fake"
71
+ get :param, "/param/:id/:name"
72
+ get :change_format, "/change-format"
73
+ post :create, "/create"
74
+ put :update, "/update"
75
+ get :not_proxied, "/not_proxied"
76
+ delete :remove, "/remove"
77
+ get :hal_test, "/hal_test/:id"
78
+ end
79
+
80
+ describe ActiveRestClient::Base do
81
+ it "allows the URL to be changed" do
82
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/getAll?id=1", instance_of(Hash)).and_return(OpenStruct.new(body:"{\"result\":true}", status:200, headers:{}))
83
+ ProxyClientExample.all(id:1)
84
+ end
85
+
86
+ it "allows the URL to be replaced" do
87
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/new", instance_of(Hash)).and_return(OpenStruct.new(body:"{\"result\":true}", status:200, headers:{}))
88
+ ProxyClientExample.old
89
+ end
90
+
91
+ it "has access to the GET params and allow them to be changed/removed/added" do
92
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/list?age=12&first_name=John", instance_of(Hash)).and_return(OpenStruct.new(body:"{\"result\":true}", status:200, headers:{}))
93
+ ProxyClientExample.list(fname:"John", lname:"Smith")
94
+ end
95
+
96
+ it "has access to the POST params and allow them to be changed/removed/added" do
97
+ ActiveRestClient::Connection.any_instance.should_receive(:post).with("/create", {age:12, first_name:"John"}.to_query, instance_of(Hash)).and_return(OpenStruct.new(body:"{\"result\":true}", status:200, headers:{}))
98
+ ProxyClientExample.create(fname:"John", lname:"Smith")
99
+ end
100
+
101
+ it "has access to raw body content for requests" do
102
+ ActiveRestClient::Connection.any_instance.should_receive(:put).with("/update", "MY-BODY-CONTENT", instance_of(Hash)).and_return(OpenStruct.new(body:"{\"result\":true}", status:200, headers:{}))
103
+ ProxyClientExample.update(fname:"John", lname:"Smith")
104
+ end
105
+
106
+ it "handles DELETE requests" do
107
+ ActiveRestClient::Connection.any_instance.should_receive(:delete).with("/remove", instance_of(Hash)).and_return(OpenStruct.new(body:"{\"result\":true}", status:200, headers:{}))
108
+ ProxyClientExample.remove
109
+ end
110
+
111
+ it "can return fake JSON data and have this parsed in the normal way" do
112
+ ActiveRestClient::Connection.any_instance.should_not_receive(:get).with("/fake", instance_of(Hash))
113
+ ret = ProxyClientExample.fake
114
+ expect(ret.id).to eq(1234)
115
+ end
116
+
117
+ it "can intercept the response and parse the response, alter it and pass it on during the request" do
118
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/change-format", instance_of(Hash)).and_return(OpenStruct.new(body:"{\"fname\":\"Billy\"}", status:200, headers:{}))
119
+ ret = ProxyClientExample.change_format
120
+ expect(ret.first_name).to eq("Billy")
121
+ end
122
+
123
+ it "can continue with the request in the normal way, passing it on to the server" do
124
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/not_proxied?id=1", instance_of(Hash)).and_return(OpenStruct.new(body:"{\"result\":true}", status:200, headers:{}))
125
+ ProxyClientExample.not_proxied(id:1)
126
+ end
127
+
128
+ it "caches responses in the standard way" do
129
+ cache_store = double("CacheStore")
130
+ cache_store.stub(:read).with(any_args).and_return(nil)
131
+ ActiveRestClient::Base.stub(:cache_store).and_return(cache_store)
132
+ expiry = 10.minutes.from_now.rfc2822
133
+ ActiveRestClient::Connection.any_instance.should_receive(:put).with("/update", "MY-BODY-CONTENT", instance_of(Hash)).and_return(OpenStruct.new(body:"{\"result\":true}", status:200, headers:{"Expires" => expiry, "ETag" => "123456"}))
134
+ ActiveRestClient::Base.cache_store.should_receive(:write) do |key, object, options|
135
+ expect(key).to eq("ProxyClientExample:/update")
136
+ expect(object.etag).to eq("123456")
137
+ expect(object.expires).to eq(expiry)
138
+ end
139
+ ProxyClientExample.update(id:1)
140
+ end
141
+
142
+ it "can have parameters in the URL" do
143
+ ret = ProxyClientExample.param(id:1234, name:"Johnny")
144
+ expect(ret.id).to eq("1234")
145
+ expect(ret.name).to eq("Johnny")
146
+ end
147
+
148
+ it "can force the URL from a filter without it being passed through URL replacement" do
149
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/hal_test/1", instance_of(Hash)).and_return(OpenStruct.new(body:"{\"result\":true}", status:200, headers:{}))
150
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/this/is/a/test", instance_of(Hash)).and_return(OpenStruct.new(body:"{\"result\":true}", status:200, headers:{}))
151
+ expect(ProxyClientExample.hal_test(id:1).test.result).to eq(true)
152
+ end
153
+
154
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveRestClient::Recording do
4
+ it "can confirm if a recording callback is set" do
5
+ class MyObject1
6
+ include ActiveRestClient::Recording
7
+ end
8
+ expect(MyObject1.record_response?).to be_false
9
+ MyObject1.record_response do
10
+ puts "Hello world"
11
+ end
12
+ expect(MyObject1.record_response?).to be_true
13
+ end
14
+
15
+ it "remembers a block given to it to later be called back" do
16
+ class MyObject2
17
+ include ActiveRestClient::Recording
18
+ end
19
+ MyObject2.record_response do
20
+ puts "Hello world"
21
+ end
22
+ expect(MyObject2.instance_variable_get(:@record_response)).to_not be_nil
23
+ end
24
+
25
+ it "calls back to the block if record_response is given a url and response" do
26
+ class MyObject3
27
+ include ActiveRestClient::Recording
28
+ end
29
+ MyObject3.record_response do |url, response|
30
+ raise Exception.new("#{url}|#{response}")
31
+ end
32
+ expect{MyObject3.record_response("http://www.example.com/", "Hello world")}.to raise_error(Exception, 'http://www.example.com/|Hello world')
33
+ end
34
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ class RequestFilteringExample
4
+ include ActiveRestClient::RequestFiltering
5
+
6
+ before_request do |name, request|
7
+ request.get_params[:filter1] = "Hello"
8
+ end
9
+
10
+ before_request do |name, request|
11
+ request.post_params[:post_filter1] = "World"
12
+ end
13
+
14
+ before_request do |name, request|
15
+ request.headers["X-My-Header"] = "myvalue"
16
+ end
17
+
18
+ before_request :set_to_ssl
19
+ before_request :set_via_instance
20
+
21
+ private
22
+
23
+ def self.set_to_ssl(name, request)
24
+ request.url.gsub!("http://", "https://")
25
+ end
26
+
27
+ def set_via_instance(name, request)
28
+ request.url.gsub!("//www", "//new")
29
+ end
30
+ end
31
+
32
+ class SubClassedRequestFilteringExample < RequestFilteringExample
33
+ before_request do |name, request|
34
+ request.get_params[:api_key] = 1234
35
+ end
36
+ end
37
+
38
+ describe ActiveRestClient::RequestFiltering do
39
+ let(:request) { OpenStruct.new(get_params:{}, post_params:{}, url:"http://www.example.com", headers:ActiveRestClient::HeadersList.new) }
40
+
41
+ it "should call through to adjust the parameters" do
42
+ RequestFilteringExample._filter_request(:test, request)
43
+ expect(request.get_params).to have_key(:filter1)
44
+ end
45
+
46
+ it "should call through for more than one filter" do
47
+ RequestFilteringExample._filter_request(:test, request)
48
+ expect(request.get_params).to have_key(:filter1)
49
+ expect(request.post_params).to have_key(:post_filter1)
50
+ end
51
+
52
+ it "should allow adjusting the URL via a named filter" do
53
+ RequestFilteringExample._filter_request(:test, request)
54
+ expect(request.url).to match(/https:\/\//)
55
+ end
56
+
57
+ it "should allow adjusting the URL via a named filter as an instance method" do
58
+ RequestFilteringExample._filter_request(:test, request)
59
+ expect(request.url).to match(/\/\/new\./)
60
+ end
61
+
62
+ it "should allow filters to be set on the parent or on the child" do
63
+ SubClassedRequestFilteringExample._filter_request(:test, request)
64
+ expect(request.url).to match(/\/\/new\./)
65
+ expect(request.get_params[:api_key]).to eq(1234)
66
+ end
67
+
68
+ it "should allow filters to add custom headers" do
69
+ RequestFilteringExample._filter_request(:test, request)
70
+ expect(request.headers["X-My-Header"]).to eq("myvalue")
71
+ end
72
+ end
@@ -0,0 +1,471 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveRestClient::Request do
4
+ before :each do
5
+ class ExampleOtherClient < ActiveRestClient::Base ; end
6
+ class ExampleClient < ActiveRestClient::Base
7
+ base_url "http://www.example.com"
8
+
9
+ before_request do |name, request|
10
+ if request.method[:name] == :headers
11
+ request.headers["X-My-Header"] = "myvalue"
12
+ end
13
+ end
14
+
15
+ get :all, "/", :has_many => {:expenses => ExampleOtherClient}
16
+ get :babies, "/babies", :has_many => {:children => ExampleOtherClient}
17
+ get :headers, "/headers"
18
+ get :find, "/:id"
19
+ post :create, "/create"
20
+ put :update, "/put/:id"
21
+ delete :remove, "/remove/:id"
22
+ get :hal, "/hal", fake:"{\"_links\":{\"child\": {\"href\": \"/child/1\"}, \"other\": {\"href\": \"/other/1\"}, \"cars\":[{\"href\": \"/car/1\", \"name\":\"car1\"}, {\"href\": \"/car/2\", \"name\":\"car2\"}, {\"href\": \"/car/not-embed\", \"name\":\"car_not_embed\"} ], \"lazy\": {\"href\": \"/lazy/load\"}, \"invalid\": [{\"href\": \"/invalid/1\"}]}, \"_embedded\":{\"other\":{\"name\":\"Jane\"},\"child\":{\"name\":\"Billy\"}, \"cars\":[{\"_links\": {\"self\": {\"href\": \"/car/1\"} }, \"make\": \"Bugatti\", \"model\": \"Veyron\"}, {\"_links\": {\"self\": {\"href\": \"/car/2\"} }, \"make\": \"Ferrari\", \"model\": \"F458 Italia\"} ], \"invalid\": [{\"present\":true, \"_links\": {} } ] } }", has_many:{other:ExampleOtherClient}
23
+ get :fake, "/fake", fake:"{\"result\":true, \"list\":[1,2,3,{\"test\":true}], \"child\":{\"grandchild\":{\"test\":true}}}"
24
+ get :defaults, "/defaults", defaults:{overwrite:"no", persist:"yes"}
25
+ end
26
+
27
+ class LazyLoadedExampleClient < ExampleClient
28
+ lazy_load!
29
+ get :fake, "/fake", fake:"{\"result\":true, \"list\":[1,2,3,{\"test\":true}], \"child\":{\"grandchild\":{\"test\":true}}}"
30
+ get :lazy_test, "/does-not-matter", fake:"{\"people\":[\"http://www.example.com/some/url\"]}", :lazy => %i{people}
31
+ end
32
+
33
+ class VerboseExampleClient < ExampleClient
34
+ verbose!
35
+ get :all, "/all"
36
+ end
37
+
38
+ class FilteredBodyExampleClient < ExampleClient
39
+ base_url "http://www.example.com"
40
+ before_request do |name, request|
41
+ request.body = Oj.dump(request.post_params)
42
+ end
43
+
44
+ post :save, "/save"
45
+ end
46
+
47
+ ActiveRestClient::Request.any_instance.stub(:read_cached_response)
48
+ end
49
+
50
+ it "should get an HTTP connection when called" do
51
+ connection = double(ActiveRestClient::Connection).as_null_object
52
+ ActiveRestClient::ConnectionManager.should_receive(:get_connection).and_return(connection)
53
+ connection.should_receive(:get).with("/", an_instance_of(Hash)).and_return(OpenStruct.new(body:'{"result":true}', headers:{}))
54
+ ExampleClient.all
55
+ end
56
+
57
+ it "should get an HTTP connection when called and call get on it" do
58
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/", an_instance_of(Hash)).and_return(OpenStruct.new(body:'{"result":true}', headers:{}))
59
+ ExampleClient.all
60
+ end
61
+
62
+ it "should get an HTTP connection when called and call delete on it" do
63
+ ActiveRestClient::Connection.any_instance.should_receive(:delete).with("/remove/1", an_instance_of(Hash)).and_return(OpenStruct.new(body:'{"result":true}', headers:{}))
64
+ ExampleClient.remove(id:1)
65
+ end
66
+
67
+ it "should pass through get parameters" do
68
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/?debug=true", an_instance_of(Hash)).and_return(OpenStruct.new(body:'{"result":true}', headers:{}))
69
+ ExampleClient.all debug:true
70
+ end
71
+
72
+ it "should pass through get parameters, using defaults specified" do
73
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/defaults?overwrite=yes&persist=yes", an_instance_of(Hash)).and_return(OpenStruct.new(body:'{"result":true}', headers:{}))
74
+ ExampleClient.defaults overwrite:"yes"
75
+ end
76
+
77
+ it "should pass through url parameters" do
78
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/1234", an_instance_of(Hash)).and_return(OpenStruct.new(body:'{"result":true}', headers:{}))
79
+ ExampleClient.find id:1234
80
+ end
81
+
82
+ it "should accept an integer as the only parameter and use it as id" do
83
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/1234", an_instance_of(Hash)).and_return(OpenStruct.new(body:'{"result":true}', headers:{}))
84
+ ExampleClient.find(1234)
85
+ end
86
+
87
+ it "should accept a string as the only parameter and use it as id" do
88
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/1234", an_instance_of(Hash)).and_return(OpenStruct.new(body:'{"result":true}', headers:{}))
89
+ ExampleClient.find("1234")
90
+ end
91
+
92
+ it "should pass through url parameters and get parameters" do
93
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/1234?debug=true", an_instance_of(Hash)).and_return(OpenStruct.new(body:"{\"result\":true}", headers:{}))
94
+ ExampleClient.find id:1234, debug:true
95
+ end
96
+
97
+ it "should pass through url parameters and put parameters" do
98
+ ActiveRestClient::Connection.any_instance.should_receive(:put).with("/put/1234", "debug=true", an_instance_of(Hash)).and_return(OpenStruct.new(body:"{\"result\":true}", headers:{}))
99
+ ExampleClient.update id:1234, debug:true
100
+ end
101
+
102
+ it "should pass through custom headers" do
103
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/headers", hash_including("X-My-Header" => "myvalue")).and_return(OpenStruct.new(body:'{"result":true}', headers:{}))
104
+ ExampleClient.headers
105
+ end
106
+
107
+ it "should parse JSON to give a nice object" do
108
+ ActiveRestClient::Connection.any_instance.should_receive(:put).with("/put/1234", "debug=true", an_instance_of(Hash)).and_return(OpenStruct.new(body:"{\"result\":true, \"list\":[1,2,3,{\"test\":true}], \"created_at\":\"2012-03-04T01:02:03Z\", \"child\":{\"grandchild\":{\"test\":true}}}", headers:{}))
109
+ object = ExampleClient.update id:1234, debug:true
110
+ expect(object.result).to eq(true)
111
+ expect(object.list.first).to eq(1)
112
+ expect(object.list.last.test).to eq(true)
113
+ expect(object.created_at).to be_an_instance_of(DateTime)
114
+ expect(object.child.grandchild.test).to eq(true)
115
+ end
116
+
117
+ it "should parse JSON and return a nice object for faked responses" do
118
+ object = ExampleClient.fake id:1234, debug:true
119
+ expect(object.result).to eq(true)
120
+ expect(object.list.first).to eq(1)
121
+ expect(object.list.last.test).to eq(true)
122
+ expect(object.child.grandchild.test).to eq(true)
123
+ end
124
+
125
+ it "should return a lazy loader object if lazy loading is enabled" do
126
+ object = LazyLoadedExampleClient.fake id:1234, debug:true
127
+ expect(object).to be_an_instance_of(ActiveRestClient::LazyLoader)
128
+ end
129
+
130
+ it "should proxy through nice object for lazy loaded responses" do
131
+ object = LazyLoadedExampleClient.fake id:1234, debug:true
132
+ expect(object.result).to eq(true)
133
+ expect(object.list.first).to eq(1)
134
+ expect(object.list.last.test).to eq(true)
135
+ expect(object.child.grandchild.test).to eq(true)
136
+ end
137
+
138
+ it "should return a LazyAssociationLoader for lazy loaded properties" do
139
+ object = LazyLoadedExampleClient.lazy_test
140
+ expect(object.people.size).to eq(1)
141
+ expect(object.people).to be_an_instance_of(ActiveRestClient::LazyAssociationLoader)
142
+ end
143
+
144
+ it "should log faked responses" do
145
+ ActiveRestClient::Logger.stub(:debug)
146
+ ActiveRestClient::Logger.should_receive(:debug).with {|*args| args.first["Faked response found"]}
147
+ object = ExampleClient.fake id:1234, debug:true
148
+ end
149
+
150
+ it "should parse an array within JSON to be a result iterator" do
151
+ ActiveRestClient::Connection.any_instance.should_receive(:put).with("/put/1234", "debug=true", an_instance_of(Hash)).and_return(OpenStruct.new(body:"[{\"first_name\":\"Johnny\"}, {\"first_name\":\"Billy\"}]", status:200, headers:{}))
152
+ object = ExampleClient.update id:1234, debug:true
153
+ expect(object).to be_instance_of(ActiveRestClient::ResultIterator)
154
+ expect(object.first.first_name).to eq("Johnny")
155
+ expect(object[1].first_name).to eq("Billy")
156
+ expect(object._status).to eq(200)
157
+ end
158
+
159
+ it "should instantiate other classes using has_many when required to do so" do
160
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/", an_instance_of(Hash)).and_return(OpenStruct.new(body:"{\"first_name\":\"Johnny\", \"expenses\":[{\"amount\":1}, {\"amount\":2}]}", status:200, headers:{}))
161
+ object = ExampleClient.all
162
+ expect(object.expenses.first).to be_instance_of(ExampleOtherClient)
163
+ end
164
+
165
+ it "should instantiate other classes using has_many even if nested off the root" do
166
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/babies", an_instance_of(Hash)).and_return(OpenStruct.new(body:"{\"first_name\":\"Johnny\", \"children\":{\"eldest\":[{\"name\":\"Billy\"}]}}", status:200, headers:{}))
167
+ object = ExampleClient.babies
168
+ expect(object.children.eldest.first).to be_instance_of(ExampleOtherClient)
169
+ end
170
+
171
+ it "should assign new attributes to the existing object if possible" do
172
+ ActiveRestClient::Connection.
173
+ any_instance.
174
+ should_receive(:post).
175
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
176
+ and_return(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", headers:{}))
177
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
178
+ object.create
179
+ expect(object.first_name).to eq("John")
180
+ expect(object.should_disappear).to eq(nil)
181
+ expect(object.id).to eq(1234)
182
+ end
183
+
184
+ it "should clearly pass through 200 status responses" do
185
+ ActiveRestClient::Connection.
186
+ any_instance.
187
+ should_receive(:post).
188
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
189
+ and_return(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", headers:{}, status:200))
190
+ ActiveRestClient::Logger.should_receive(:info).with {|*args| args.first[%r{Requesting http://www.example.com/create}]}
191
+ ActiveRestClient::Logger.should_receive(:debug).with {|*args| args.first[/Response received \d+ bytes/]}
192
+
193
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
194
+ object.create
195
+ expect(object._status).to eq(200)
196
+ end
197
+
198
+ it "should debug log 200 responses" do
199
+ ActiveRestClient::Connection.
200
+ any_instance.
201
+ should_receive(:post).
202
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
203
+ and_return(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", headers:{}, status:200))
204
+ ActiveRestClient::Logger.should_receive(:info).with {|*args| args.first[%r{Requesting http://www.example.com/create}]}
205
+ ActiveRestClient::Logger.should_receive(:debug).with {|*args| args.first[/Response received \d+ bytes/]}
206
+
207
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
208
+ object.create
209
+ end
210
+
211
+ it "should verbose log if enabled" do
212
+ connection = double(ActiveRestClient::Connection).as_null_object
213
+ ActiveRestClient::ConnectionManager.should_receive(:get_connection).and_return(connection)
214
+ connection.should_receive(:get).with("/all", an_instance_of(Hash)).and_return(OpenStruct.new(body:'{"result":true}', headers:{"Content-Type" => "application/json", "Connection" => "close"}))
215
+ ActiveRestClient::Logger.should_receive(:debug).with("ActiveRestClient Verbose Log:")
216
+ ActiveRestClient::Logger.should_receive(:debug).with(/ > /).at_least(:twice)
217
+ ActiveRestClient::Logger.should_receive(:debug).with(/ < /).at_least(:twice)
218
+ ActiveRestClient::Logger.stub(:debug).with(any_args)
219
+ VerboseExampleClient.all
220
+ end
221
+
222
+ it "should raise an unauthorised exception for 401 errors" do
223
+ ActiveRestClient::Connection.
224
+ any_instance.
225
+ should_receive(:post).
226
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
227
+ and_return(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", headers:{}, status:401))
228
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
229
+ begin
230
+ object.create
231
+ rescue ActiveRestClient::HTTPUnauthorisedClientException => e
232
+ e
233
+ end
234
+ expect(e).to be_instance_of(ActiveRestClient::HTTPUnauthorisedClientException)
235
+ expect(e.status).to eq(401)
236
+ expect(e.result.first_name).to eq("John")
237
+ end
238
+
239
+ it "should raise a forbidden client exception for 403 errors" do
240
+ ActiveRestClient::Connection.
241
+ any_instance.
242
+ should_receive(:post).
243
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
244
+ and_return(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", headers:{}, status:403))
245
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
246
+ begin
247
+ object.create
248
+ rescue ActiveRestClient::HTTPForbiddenClientException => e
249
+ e
250
+ end
251
+ expect(e).to be_instance_of(ActiveRestClient::HTTPForbiddenClientException)
252
+ expect(e.status).to eq(403)
253
+ expect(e.result.first_name).to eq("John")
254
+ end
255
+
256
+ it "should raise a not found client exception for 404 errors" do
257
+ ActiveRestClient::Connection.
258
+ any_instance.
259
+ should_receive(:post).
260
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
261
+ and_return(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", headers:{}, status:404))
262
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
263
+ begin
264
+ object.create
265
+ rescue ActiveRestClient::HTTPNotFoundClientException => e
266
+ e
267
+ end
268
+ expect(e).to be_instance_of(ActiveRestClient::HTTPNotFoundClientException)
269
+ expect(e.status).to eq(404)
270
+ expect(e.result.first_name).to eq("John")
271
+ end
272
+
273
+ it "should raise a client exceptions for 4xx errors" do
274
+ ActiveRestClient::Connection.
275
+ any_instance.
276
+ should_receive(:post).
277
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
278
+ and_return(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", headers:{}, status:409))
279
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
280
+ begin
281
+ object.create
282
+ rescue ActiveRestClient::HTTPClientException => e
283
+ e
284
+ end
285
+ expect(e).to be_instance_of(ActiveRestClient::HTTPClientException)
286
+ expect(e.status).to eq(409)
287
+ expect(e.result.first_name).to eq("John")
288
+ end
289
+
290
+ it "should raise a server exception for 5xx errors" do
291
+ ActiveRestClient::Connection.
292
+ any_instance.
293
+ should_receive(:post).
294
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
295
+ and_return(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", headers:{}, status:500))
296
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
297
+ begin
298
+ object.create
299
+ rescue ActiveRestClient::HTTPServerException => e
300
+ e
301
+ end
302
+ expect(e).to be_instance_of(ActiveRestClient::HTTPServerException)
303
+ expect(e.status).to eq(500)
304
+ expect(e.result.first_name).to eq("John")
305
+ end
306
+
307
+ it "should raise a parse exception for invalid JSON returns" do
308
+ error_content = "<h1>500 Server Error</h1>"
309
+ ActiveRestClient::Connection.
310
+ any_instance.
311
+ should_receive(:post).
312
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
313
+ and_return(OpenStruct.new(body:error_content, headers:{}, status:500))
314
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
315
+ begin
316
+ object.create
317
+ rescue ActiveRestClient::ResponseParseException => e
318
+ e
319
+ end
320
+ expect(e).to be_instance_of(ActiveRestClient::ResponseParseException)
321
+ expect(e.status).to eq(500)
322
+ expect(e.body).to eq(error_content)
323
+ end
324
+
325
+ it "should raise an exception if you try to pass in an unsupport method" do
326
+ method = {:method => :wiggle, url:"/"}
327
+ class RequestFakeObject
328
+ def base_url
329
+ "http://www.example.com/"
330
+ end
331
+
332
+ def name ; end
333
+ def _filter_request(*args) ; end
334
+ def verbose ; false ; end
335
+ end
336
+ fake_object = RequestFakeObject.new
337
+ request = ActiveRestClient::Request.new(method, fake_object, {})
338
+ expect{request.call}.to raise_error(ActiveRestClient::InvalidRequestException)
339
+ end
340
+
341
+ it "should send all class mapped methods through _filter_request" do
342
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/", an_instance_of(Hash)).and_return(OpenStruct.new(body:"{\"first_name\":\"Johnny\", \"expenses\":[{\"amount\":1}, {\"amount\":2}]}", status:200, headers:{}))
343
+ ExampleClient.should_receive(:_filter_request).with(any_args)
344
+ ExampleClient.all
345
+ end
346
+
347
+ it "should send all instance mapped methods through _filter_request" do
348
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/", an_instance_of(Hash)).and_return(OpenStruct.new(body:"{\"first_name\":\"Johnny\", \"expenses\":[{\"amount\":1}, {\"amount\":2}]}", status:200, headers:{}))
349
+ ExampleClient.should_receive(:_filter_request).with(any_args)
350
+ e = ExampleClient.new
351
+ e.all
352
+ end
353
+
354
+ context "Direct URL requests" do
355
+ class SameServerExampleClient < ActiveRestClient::Base
356
+ URL = "http://www.example.com/some/url"
357
+ base_url "http://www.example.com/v1"
358
+ get :same_server, "/does-not-matter", url:URL
359
+ end
360
+
361
+ class OtherServerExampleClient < ActiveRestClient::Base
362
+ URL = "http://other.example.com/some/url"
363
+ base_url "http://www.example.com/v1"
364
+ get :other_server, "/does-not-matter", url:URL
365
+ end
366
+
367
+ it "should allow requests directly to URLs" do
368
+ ActiveRestClient::ConnectionManager.reset!
369
+ ActiveRestClient::Connection.
370
+ any_instance.
371
+ should_receive(:get).
372
+ with("/some/url", an_instance_of(Hash)).
373
+ and_return(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", headers:{}, status:200))
374
+ SameServerExampleClient.same_server
375
+ end
376
+
377
+ it "should allow requests directly to URLs even if to different URLs" do
378
+ ActiveRestClient::ConnectionManager.reset!
379
+ connection = double("Connection")
380
+ connection.
381
+ should_receive(:get).
382
+ with("/some/url", an_instance_of(Hash)).
383
+ and_return(OpenStruct.new(body:"", headers:{}, status:304))
384
+ connection.
385
+ stub(:base_url).
386
+ and_return("http://other.example.com")
387
+ ActiveRestClient::ConnectionManager.should_receive(:find_connection_for_url).with(OtherServerExampleClient::URL).and_return(connection)
388
+ OtherServerExampleClient.other_server
389
+ end
390
+
391
+ it "should allow requests to partial URLs using the current base_url" do
392
+ ActiveRestClient::ConnectionManager.reset!
393
+ connection = double("Connection").as_null_object
394
+ ActiveRestClient::ConnectionManager.should_receive(:get_connection).with("http://www.example.com").and_return(connection)
395
+ connection.
396
+ should_receive(:get).
397
+ with("/people", an_instance_of(Hash)).
398
+ and_return(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", headers:{}, status:200))
399
+ @obj = SameServerExampleClient._request('/people')
400
+ end
401
+ end
402
+
403
+ # HAL is Hypermedia Application Language
404
+ context "HAL" do
405
+ let(:hal) { ExampleClient.hal }
406
+
407
+ it "should request a HAL response or plain JSON" do
408
+ ActiveRestClient::Connection.any_instance.should_receive(:get).with("/headers", hash_including("Accept" => "application/hal+json, application/json;q=0.5")).and_return(OpenStruct.new(body:'{"result":true}', headers:{}))
409
+ ExampleClient.headers
410
+ end
411
+
412
+ it "should recognise a HAL response" do
413
+ method = {:method => :get, url:"/"}
414
+ class RequestFakeObject
415
+ def base_url
416
+ "http://www.example.com/"
417
+ end
418
+
419
+ def name ; end
420
+ def _filter_request(*args) ; end
421
+ end
422
+ fake_object = RequestFakeObject.new
423
+ request = ActiveRestClient::Request.new(method, fake_object, {})
424
+ request.instance_variable_set(:@response, OpenStruct.new(headers:{"Content-Type" => "application/hal+json"}))
425
+ expect(request.hal_response?).to be_true
426
+ request.instance_variable_set(:@response, OpenStruct.new(headers:{"Content-Type" => "application/json"}))
427
+ expect(request.hal_response?).to be_true
428
+ request.instance_variable_set(:@response, OpenStruct.new(headers:{"Content-Type" => "text/plain"}))
429
+ expect(request.hal_response?).to be_false
430
+ request.instance_variable_set(:@response, OpenStruct.new(headers:{"Content-Type" => ["text/plain", "application/hal+json"]}))
431
+ expect(request.hal_response?).to be_true
432
+ request.instance_variable_set(:@response, OpenStruct.new(headers:{"Content-Type" => ["text/plain", "application/json"]}))
433
+ expect(request.hal_response?).to be_true
434
+ request.instance_variable_set(:@response, OpenStruct.new(headers:{"Content-Type" => ["text/plain"]}))
435
+ expect(request.hal_response?).to be_false
436
+ end
437
+
438
+ it "should map _links in to the normal attributes" do
439
+ expect(hal.child).to be_an_instance_of(ExampleClient)
440
+ expect(hal.cars.size).to eq(3)
441
+ end
442
+
443
+ it "should be able to use other attributes of _links using _hal_attributes method with a key" do
444
+ expect(hal.child).to be_an_instance_of(ExampleClient)
445
+ expect(hal.cars[2]._hal_attributes("name")).to eq('car_not_embed')
446
+ end
447
+
448
+ it "should use _embedded responses instead of lazy loading if possible" do
449
+ expect(hal.child.name).to eq("Billy")
450
+ expect(hal.cars.first.make).to eq("Bugatti")
451
+ end
452
+
453
+ it "should instantiate other classes defined using has_many when using _embedded responses" do
454
+ expect(hal.other).to be_an(ExampleOtherClient)
455
+ end
456
+
457
+ it "should convert invalid _embedded responses in to lazy loading on error" do
458
+ expect(hal.invalid.first).to be_an_instance_of(ActiveRestClient::LazyAssociationLoader)
459
+ end
460
+
461
+ it "should lazy load _links attributes if not embedded" do
462
+ expect(hal.lazy).to be_an_instance_of(ActiveRestClient::LazyAssociationLoader)
463
+ expect(hal.lazy.instance_variable_get(:@url)).to eq("/lazy/load")
464
+ end
465
+ end
466
+
467
+ it "replaces the body completely in a filter" do
468
+ ActiveRestClient::Connection.any_instance.should_receive(:post).with("/save", "{\":id\":1234,\":name\":\"john\"}", an_instance_of(Hash)).and_return(OpenStruct.new(body:"{}", headers:{}))
469
+ FilteredBodyExampleClient.save id:1234, name:'john'
470
+ end
471
+ end