active_rest_client 0.9.58

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.
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