flexirest 1.2.0

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