flexirest 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +2 -0
  4. data/.simplecov +4 -0
  5. data/.travis.yml +6 -0
  6. data/CHANGELOG.md +37 -0
  7. data/CONTRIBUTING.md +62 -0
  8. data/Gemfile +4 -0
  9. data/Guardfile +9 -0
  10. data/LICENSE.txt +22 -0
  11. data/README.md +846 -0
  12. data/Rakefile +13 -0
  13. data/doc/ActiveRestClient Internals.graffle +1236 -0
  14. data/doc/ActiveRestClient Internals.png +0 -0
  15. data/flexirest.gemspec +39 -0
  16. data/lib/flexirest.rb +25 -0
  17. data/lib/flexirest/base.rb +189 -0
  18. data/lib/flexirest/caching.rb +92 -0
  19. data/lib/flexirest/configuration.rb +209 -0
  20. data/lib/flexirest/connection.rb +103 -0
  21. data/lib/flexirest/connection_manager.rb +36 -0
  22. data/lib/flexirest/headers_list.rb +47 -0
  23. data/lib/flexirest/instrumentation.rb +62 -0
  24. data/lib/flexirest/lazy_association_loader.rb +97 -0
  25. data/lib/flexirest/lazy_loader.rb +23 -0
  26. data/lib/flexirest/logger.rb +67 -0
  27. data/lib/flexirest/mapping.rb +69 -0
  28. data/lib/flexirest/monkey_patching.rb +7 -0
  29. data/lib/flexirest/proxy_base.rb +193 -0
  30. data/lib/flexirest/recording.rb +24 -0
  31. data/lib/flexirest/request.rb +573 -0
  32. data/lib/flexirest/request_delegator.rb +44 -0
  33. data/lib/flexirest/request_filtering.rb +62 -0
  34. data/lib/flexirest/result_iterator.rb +85 -0
  35. data/lib/flexirest/validation.rb +60 -0
  36. data/lib/flexirest/version.rb +3 -0
  37. data/spec/lib/base_spec.rb +389 -0
  38. data/spec/lib/caching_spec.rb +217 -0
  39. data/spec/lib/configuration_spec.rb +234 -0
  40. data/spec/lib/connection_manager_spec.rb +43 -0
  41. data/spec/lib/connection_spec.rb +159 -0
  42. data/spec/lib/headers_list_spec.rb +61 -0
  43. data/spec/lib/instrumentation_spec.rb +58 -0
  44. data/spec/lib/lazy_association_loader_spec.rb +135 -0
  45. data/spec/lib/lazy_loader_spec.rb +25 -0
  46. data/spec/lib/logger_spec.rb +63 -0
  47. data/spec/lib/mapping_spec.rb +52 -0
  48. data/spec/lib/proxy_spec.rb +189 -0
  49. data/spec/lib/recording_spec.rb +34 -0
  50. data/spec/lib/request_filtering_spec.rb +84 -0
  51. data/spec/lib/request_spec.rb +711 -0
  52. data/spec/lib/result_iterator_spec.rb +140 -0
  53. data/spec/lib/validation_spec.rb +113 -0
  54. data/spec/lib/xml_spec.rb +74 -0
  55. data/spec/spec_helper.rb +88 -0
  56. metadata +347 -0
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Flexirest::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
+ expect(request).not_to receive(:call)
9
+ Flexirest::LazyLoader.new(request)
10
+ end
11
+
12
+ it "should #call the passed in request if you check for response to a message" do
13
+ expect(request).to receive(:call)
14
+ loader = Flexirest::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
+ expect(request).to receive(:call).and_return(response)
20
+ expect(response).to receive(:valid).and_return(true)
21
+ loader = Flexirest::LazyLoader.new(request)
22
+ expect(loader.valid).to be_truthy
23
+ end
24
+
25
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ describe Flexirest::Instrumentation do
4
+ before :each do
5
+ Flexirest::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
+ expect(Rails.logger).to receive(:debug)
17
+ expect(Rails.logger).to receive(:info)
18
+ expect(Rails.logger).to receive(:warn)
19
+ expect(Rails.logger).to receive(:error)
20
+ Flexirest::Logger.debug("Hello world")
21
+ Flexirest::Logger.info("Hello world")
22
+ Flexirest::Logger.warn("Hello world")
23
+ Flexirest::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
+ Flexirest::Logger.logfile = "/dev/null"
29
+ file = double('file')
30
+ expect(File).to receive(:open).with("/dev/null", "a").and_yield(file)
31
+ expect(file).to receive(:<<).with("Hello world\n")
32
+ Flexirest::Logger.debug("Hello world")
33
+
34
+ file = double('file')
35
+ expect(File).to receive(:open).with("/dev/null", "a").and_yield(file)
36
+ expect(file).to receive(:<<).with("Hello info\n")
37
+ Flexirest::Logger.info("Hello info")
38
+
39
+ file = double('file')
40
+ expect(File).to receive(:open).with("/dev/null", "a").and_yield(file)
41
+ expect(file).to receive(:<<).with("Hello error\n")
42
+ Flexirest::Logger.error("Hello error")
43
+
44
+ file = double('file')
45
+ expect(File).to receive(:open).with("/dev/null", "a").and_yield(file)
46
+ expect(file).to receive(:<<).with("Hello warn\n")
47
+ Flexirest::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
+ expect(File).not_to receive(:open)
52
+ Flexirest::Logger.debug("Hello world")
53
+ Flexirest::Logger.info("Hello info")
54
+ Flexirest::Logger.warn("Hello warn")
55
+ Flexirest::Logger.error("Hello error")
56
+ expect(Flexirest::Logger.messages.size).to eq(4)
57
+ expect(Flexirest::Logger.messages[0]).to eq("Hello world")
58
+ expect(Flexirest::Logger.messages[1]).to eq("Hello info")
59
+ expect(Flexirest::Logger.messages[2]).to eq("Hello warn")
60
+ expect(Flexirest::Logger.messages[3]).to eq("Hello error")
61
+ end
62
+
63
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ class MappingExampleBase
4
+ include Flexirest::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
+ patch :test_patch, "/patch", tag:4
12
+ delete :test_delete, "/delete", tag:5
13
+ end
14
+
15
+ describe Flexirest::Mapping do
16
+ it "should support methods for get/put/post/delete for mapping" do
17
+ expect(EmptyExample).to respond_to(:get)
18
+ expect(EmptyExample).to respond_to(:put)
19
+ expect(EmptyExample).to respond_to(:post)
20
+ expect(EmptyExample).to respond_to(:delete)
21
+ end
22
+
23
+ it "should save URL for each mapped call" do
24
+ expect(MappingExample._calls[:test_get][:url]).to eq("/get")
25
+ expect(MappingExample._calls[:test_put][:url]).to eq("/put")
26
+ expect(MappingExample._calls[:test_post][:url]).to eq("/post")
27
+ expect(MappingExample._calls[:test_patch][:url]).to eq("/patch")
28
+ expect(MappingExample._calls[:test_delete][:url]).to eq("/delete")
29
+ end
30
+
31
+ it "should save the correct method type for each mapped call" do
32
+ expect(MappingExample._calls[:test_get][:method]).to eq(:get)
33
+ expect(MappingExample._calls[:test_put][:method]).to eq(:put)
34
+ expect(MappingExample._calls[:test_post][:method]).to eq(:post)
35
+ expect(MappingExample._calls[:test_patch][:method]).to eq(:patch)
36
+ expect(MappingExample._calls[:test_delete][:method]).to eq(:delete)
37
+ end
38
+
39
+ it "should remember options set for each mapped call" do
40
+ expect(MappingExample._calls[:test_get][:options][:fake]).to eq("{result:true}")
41
+ expect(MappingExample._calls[:test_get][:options][:lazy]).to eq([:something])
42
+ expect(MappingExample._calls[:test_get][:options][:tag]).to eq(1)
43
+ expect(MappingExample._calls[:test_put][:options][:tag]).to eq(2)
44
+ expect(MappingExample._calls[:test_post][:options][:tag]).to eq(3)
45
+ expect(MappingExample._calls[:test_patch][:options][:tag]).to eq(4)
46
+ expect(MappingExample._calls[:test_delete][:options][:tag]).to eq(5)
47
+ end
48
+
49
+ it "should allow for mapped calls on the class" do
50
+ expect(MappingExample).to respond_to(:test_get)
51
+ end
52
+ end
@@ -0,0 +1,189 @@
1
+ require 'spec_helper'
2
+ require 'active_support/core_ext/hash'
3
+
4
+ class ProxyExample < Flexirest::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 "/change-xml-format" do
47
+ response = passthrough
48
+ translate(response) do |body|
49
+ body["first_name"] = body["object"].delete("fname")
50
+ body
51
+ end
52
+ end
53
+
54
+ get "/hal_test/:id" do
55
+ response = passthrough
56
+ translate(response) do |body|
57
+ body["_links"] = {"test" => {href:"/this/is/a/test"}}
58
+ body
59
+ end
60
+ end
61
+
62
+ get "/param/:id/:name" do
63
+ render "{\"id\":\"#{params[:id]}\", \"name\":\"#{params[:name]}\"}"
64
+ end
65
+
66
+ get "/fake" do
67
+ render "{\"id\":1234}"
68
+ end
69
+ end
70
+
71
+ class ProxyClientExample < Flexirest::Base
72
+ proxy ProxyExample
73
+ base_url "http://www.example.com"
74
+
75
+ get :all, "/all"
76
+ get :old, "/old"
77
+ get :list, "/list"
78
+ get :fake, "/fake"
79
+ get :param, "/param/:id/:name"
80
+ get :change_format, "/change-format"
81
+ get :change_xml_format, "/change-xml-format"
82
+ post :create, "/create"
83
+ put :update, "/update"
84
+ get :not_proxied, "/not_proxied"
85
+ delete :remove, "/remove"
86
+ get :hal_test, "/hal_test/:id"
87
+ end
88
+
89
+ describe Flexirest::Base do
90
+ it "allows the URL to be changed" do
91
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/getAll?id=1", instance_of(Hash)).and_return(OpenStruct.new(body:"{\"result\":true}", status:200, headers:{}))
92
+ ProxyClientExample.all(id:1)
93
+ end
94
+
95
+ it "allows the URL to be replaced" do
96
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/new", instance_of(Hash)).and_return(OpenStruct.new(body:"{\"result\":true}", status:200, headers:{}))
97
+ ProxyClientExample.old
98
+ end
99
+
100
+ it "has access to the GET params and allow them to be changed/removed/added" do
101
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/list?age=12&first_name=John", instance_of(Hash)).and_return(OpenStruct.new(body:"{\"result\":true}", status:200, headers:{}))
102
+ ProxyClientExample.list(fname:"John", lname:"Smith")
103
+ end
104
+
105
+ it "has access to the POST params and allow them to be changed/removed/added" do
106
+ expect_any_instance_of(Flexirest::Connection).to receive(:post).with("/create", {age:12, first_name:"John"}.to_query, instance_of(Hash)).and_return(OpenStruct.new(body:"{\"result\":true}", status:200, headers:{}))
107
+ ProxyClientExample.create(fname:"John", lname:"Smith")
108
+ end
109
+
110
+ it "has access to raw body content for requests" do
111
+ expect_any_instance_of(Flexirest::Connection).to receive(:put).with("/update", "MY-BODY-CONTENT", instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"result\":true}", status:200, response_headers:{})))
112
+ ProxyClientExample.update(fname:"John", lname:"Smith")
113
+ end
114
+
115
+ it "handles DELETE requests" do
116
+ expect_any_instance_of(Flexirest::Connection).to receive(:delete).with("/remove", instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"result\":true}", status:200, response_headers:{})))
117
+ ProxyClientExample.remove
118
+ end
119
+
120
+ it "can return fake JSON data and have this parsed in the normal way" do
121
+ expect_any_instance_of(Flexirest::Connection).not_to receive(:get).with("/fake", instance_of(Hash))
122
+ ret = ProxyClientExample.fake
123
+ expect(ret.id).to eq(1234)
124
+ end
125
+
126
+ it "can intercept the response and parse the response, alter it and pass it on during the request" do
127
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/change-format", instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"fname\":\"Billy\"}", status:200, response_headers:{})))
128
+ ret = ProxyClientExample.change_format
129
+ expect(ret.first_name).to eq("Billy")
130
+ end
131
+
132
+ it "can intercept XML responses, parse the response, alter it and pass it on during the request" do
133
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/change-xml-format",
134
+ instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(
135
+ body:"<?xml version=\"1.0\" encoding=\"UTF-8\"?><object><fname>Billy</fname></object>",
136
+ status:200,
137
+ response_headers:{"Content-Type" => "application/xml"})))
138
+ ret = ProxyClientExample.change_xml_format
139
+ expect(ret.first_name).to eq("Billy")
140
+ end
141
+
142
+ it 'skips the altering of the response body when there is none' do
143
+ allow_any_instance_of(Flexirest::Connection).to receive(:get).with('/change-format', instance_of(Hash))
144
+ .and_return(double(body: '', status: 200, headers: {}))
145
+ result = ProxyClientExample.change_format
146
+ expect(result._attributes).to be_empty
147
+ end
148
+
149
+ it "can continue with the request in the normal way, passing it on to the server" do
150
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/not_proxied?id=1", instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"result\":true}", status:200, response_headers:{})))
151
+ ProxyClientExample.not_proxied(id:1)
152
+ end
153
+
154
+ it "caches responses in the standard way" do
155
+
156
+ cached_response = Flexirest::CachedResponse.new(
157
+ status:200,
158
+ result:@cached_object,
159
+ etag:@etag)
160
+
161
+ cache_store = double("CacheStore")
162
+ allow(cache_store).to receive(:read).with(any_args).and_return(nil)
163
+ ProxyClientExample.perform_caching true
164
+ allow(ProxyClientExample).to receive(:cache_store).and_return(cache_store)
165
+ expiry = 10.minutes.from_now.rfc2822
166
+ expect_any_instance_of(Flexirest::Connection).to receive(:put).with("/update", "MY-BODY-CONTENT", instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"result\":true}", status:200, response_headers:{"Expires" => expiry, "ETag" => "123456"})))
167
+ expect(ProxyClientExample.cache_store).to receive(:write) do |key, object, options|
168
+ expect(key).to eq("ProxyClientExample:/update")
169
+ expect(object).to be_an_instance_of(String)
170
+ unmarshalled = Marshal.load(object)
171
+ expect(unmarshalled.etag).to eq("123456")
172
+ expect(unmarshalled.expires).to eq(expiry)
173
+ end
174
+ ProxyClientExample.update(id:1)
175
+ end
176
+
177
+ it "can have parameters in the URL" do
178
+ ret = ProxyClientExample.param(id:1234, name:"Johnny")
179
+ expect(ret.id).to eq("1234")
180
+ expect(ret.name).to eq("Johnny")
181
+ end
182
+
183
+ it "can force the URL from a filter without it being passed through URL replacement" do
184
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/hal_test/1", instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"result\":true}", status:200, response_headers:{})))
185
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/this/is/a/test", instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"result\":true}", status:200, response_headers:{})))
186
+ expect(ProxyClientExample.hal_test(id:1).test.result).to eq(true)
187
+ end
188
+
189
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Flexirest::Recording do
4
+ it "can confirm if a recording callback is set" do
5
+ class MyObject1
6
+ include Flexirest::Recording
7
+ end
8
+ expect(MyObject1.record_response?).to be_falsey
9
+ MyObject1.record_response do
10
+ puts "Hello world"
11
+ end
12
+ expect(MyObject1.record_response?).to be_truthy
13
+ end
14
+
15
+ it "remembers a block given to it to later be called back" do
16
+ class MyObject2
17
+ include Flexirest::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 Flexirest::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,84 @@
1
+ require 'spec_helper'
2
+
3
+ class RequestFilteringExample
4
+ include Flexirest::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
+ after_request :change_body
22
+
23
+ private
24
+
25
+ def self.set_to_ssl(name, request)
26
+ request.url.gsub!("http://", "https://")
27
+ end
28
+
29
+ def set_via_instance(name, request)
30
+ request.url.gsub!("//www", "//new")
31
+ end
32
+
33
+ def change_body(name, response)
34
+ response.body = "{test: 1}"
35
+ end
36
+ end
37
+
38
+ class SubClassedRequestFilteringExample < RequestFilteringExample
39
+ before_request do |name, request|
40
+ request.get_params[:api_key] = 1234
41
+ end
42
+ end
43
+
44
+ describe Flexirest::RequestFiltering do
45
+ let(:request) { OpenStruct.new(get_params:{}, post_params:{}, url:"http://www.example.com", headers:Flexirest::HeadersList.new) }
46
+ let(:response) { OpenStruct.new(body:"") }
47
+
48
+ it "should call through to adjust the parameters" do
49
+ RequestFilteringExample._filter_request(:before, :test, request)
50
+ expect(request.get_params).to have_key(:filter1)
51
+ end
52
+
53
+ it "should call through for more than one filter" do
54
+ RequestFilteringExample._filter_request(:before, :test, request)
55
+ expect(request.get_params).to have_key(:filter1)
56
+ expect(request.post_params).to have_key(:post_filter1)
57
+ end
58
+
59
+ it "should allow adjusting the URL via a named filter" do
60
+ RequestFilteringExample._filter_request(:before, :test, request)
61
+ expect(request.url).to match(/https:\/\//)
62
+ end
63
+
64
+ it "should allow adjusting the URL via a named filter as an instance method" do
65
+ RequestFilteringExample._filter_request(:before, :test, request)
66
+ expect(request.url).to match(/\/\/new\./)
67
+ end
68
+
69
+ it "should allow filters to be set on the parent or on the child" do
70
+ SubClassedRequestFilteringExample._filter_request(:before, :test, request)
71
+ expect(request.url).to match(/\/\/new\./)
72
+ expect(request.get_params[:api_key]).to eq(1234)
73
+ end
74
+
75
+ it "should allow filters to add custom headers" do
76
+ RequestFilteringExample._filter_request(:before, :test, request)
77
+ expect(request.headers["X-My-Header"]).to eq("myvalue")
78
+ end
79
+
80
+ it "should be able to alter the response body" do
81
+ RequestFilteringExample._filter_request(:after, :test, response)
82
+ expect(response.body).to eq("{test: 1}")
83
+ end
84
+ end