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,217 @@
1
+ require 'spec_helper'
2
+
3
+ describe Flexirest::Caching do
4
+ before :each do
5
+ Flexirest::Base._reset_caching!
6
+ end
7
+
8
+ context "Configuration of caching" do
9
+ it "should not have caching enabled by default" do
10
+ class CachingExample1
11
+ include Flexirest::Caching
12
+ end
13
+ expect(CachingExample1.perform_caching).to be_falsey
14
+ end
15
+
16
+ it "should be able to have caching enabled without affecting Flexirest::Base" do
17
+ class CachingExample2
18
+ include Flexirest::Caching
19
+ end
20
+ CachingExample2.perform_caching true
21
+ expect(CachingExample2.perform_caching).to be_truthy
22
+ expect(Flexirest::Base.perform_caching).to be_falsey
23
+ end
24
+
25
+ it "should be possible to enable caching for all objects" do
26
+ class CachingExample3 < Flexirest::Base ; end
27
+ Flexirest::Base._reset_caching!
28
+
29
+ expect(Flexirest::Base.perform_caching).to be_falsey
30
+
31
+ Flexirest::Base.perform_caching = true
32
+ expect(Flexirest::Base.perform_caching).to be_truthy
33
+ expect(CachingExample3.perform_caching).to be_truthy
34
+
35
+ Flexirest::Base._reset_caching!
36
+ end
37
+
38
+ it "should use Rails.cache if available" do
39
+ begin
40
+ class Rails
41
+ def self.cache
42
+ true
43
+ end
44
+ end
45
+ expect(Flexirest::Base.cache_store).to eq(true)
46
+ ensure
47
+ Object.send(:remove_const, :Rails) if defined?(Rails)
48
+ end
49
+
50
+ end
51
+
52
+ it "should use a custom cache store if a valid one is manually set" do
53
+ class CachingExampleCacheStore1
54
+ def read(key) ; end
55
+ def write(key, value, options={}) ; end
56
+ def fetch(key, &block) ; end
57
+ end
58
+ cache_store = CachingExampleCacheStore1.new
59
+ Flexirest::Base.cache_store = cache_store
60
+ expect(Flexirest::Base.cache_store).to eq(cache_store)
61
+ end
62
+
63
+ it "should error if you try to use a custom cache store that doesn't match the required interface" do
64
+ class CachingExampleCacheStore2
65
+ def write(key, value, options={}) ; end
66
+ def fetch(key, &block) ; end
67
+ end
68
+ class CachingExampleCacheStore3
69
+ def read(key) ; end
70
+ def fetch(key, &block) ; end
71
+ end
72
+ class CachingExampleCacheStore4
73
+ def read(key) ; end
74
+ def write(key, value, options={}) ; end
75
+ end
76
+
77
+ expect{ Flexirest::Base.cache_store = CachingExampleCacheStore2.new }.to raise_error
78
+ expect{ Flexirest::Base.cache_store = CachingExampleCacheStore3.new }.to raise_error
79
+ expect{ Flexirest::Base.cache_store = CachingExampleCacheStore4.new }.to raise_error
80
+ end
81
+
82
+ it "should allow you to remove the custom cache store" do
83
+ expect{ Flexirest::Base.cache_store = nil }.to_not raise_error
84
+ end
85
+ end
86
+
87
+ context "Reading/writing to the cache" do
88
+ before :each do
89
+ Object.send(:remove_const, :CachingExampleCacheStore5) if defined?(CachingExampleCacheStore5)
90
+ class CachingExampleCacheStore5
91
+ def read(key) ; end
92
+ def write(key, value, options={}) ; end
93
+ def fetch(key, &block) ; end
94
+ end
95
+
96
+ class Person < Flexirest::Base
97
+ perform_caching true
98
+ base_url "http://www.example.com"
99
+ get :all, "/"
100
+ end
101
+
102
+ Person.cache_store = CachingExampleCacheStore5.new
103
+
104
+ @etag = "6527914a91e0c5769f6de281f25bd891"
105
+ @cached_object = Person.new(first_name:"Johnny")
106
+ end
107
+
108
+ it "should read from the cache store, to check for an etag" do
109
+ cached_response = Flexirest::CachedResponse.new(
110
+ status:200,
111
+ result:@cached_object,
112
+ etag:@etag)
113
+ expect_any_instance_of(CachingExampleCacheStore5).to receive(:read).once.with("Person:/").and_return(Marshal.dump(cached_response))
114
+ expect_any_instance_of(Flexirest::Connection).to receive(:get){ |connection, path, options|
115
+ expect(path).to eq('/')
116
+ expect(options[:headers]).to include("If-None-Match" => @etag)
117
+ }.and_return(::FaradayResponseMock.new(OpenStruct.new(status:304, response_headers:{})))
118
+ ret = Person.all
119
+ expect(ret.first_name).to eq("Johnny")
120
+ end
121
+
122
+ it 'queries the server when the cache has expired' do
123
+ cached_response = Flexirest::CachedResponse.new(
124
+ status: 200,
125
+ result: @cached_object,
126
+ etag: @etag
127
+ )
128
+ allow_any_instance_of(CachingExampleCacheStore5).to receive(:read).and_return(Marshal.dump(cached_response))
129
+ new_name = 'Pete'
130
+ response_body = Person.new(first_name: new_name).to_json
131
+ response = ::FaradayResponseMock.new(double(status: 200, response_headers: {}, body: response_body))
132
+ allow_any_instance_of(Flexirest::Connection).to(
133
+ receive(:get){ |connection, path, options|
134
+ expect(path).to eq('/')
135
+ expect(options[:headers]).to include('If-None-Match' => @etag)
136
+ }.and_return(response))
137
+
138
+ result = Person.all
139
+
140
+ expect(result.first_name).to eq new_name
141
+ end
142
+
143
+ it "should read from the cache store, and not call the server if there's a hard expiry" do
144
+ cached_response = Flexirest::CachedResponse.new(
145
+ status:200,
146
+ result:@cached_object,
147
+ expires:Time.now + 30)
148
+ expect_any_instance_of(CachingExampleCacheStore5).to receive(:read).once.with("Person:/").and_return(Marshal.dump(cached_response))
149
+ expect_any_instance_of(Flexirest::Connection).not_to receive(:get)
150
+ ret = Person.all
151
+ expect(ret.first_name).to eq("Johnny")
152
+ end
153
+
154
+ it "should read from the cache store and restore to the same object" do
155
+ cached_response = Flexirest::CachedResponse.new(
156
+ status:200,
157
+ result:@cached_object,
158
+ expires:Time.now + 30)
159
+ expect_any_instance_of(CachingExampleCacheStore5).to receive(:read).once.with("Person:/").and_return(Marshal.dump(cached_response))
160
+ expect_any_instance_of(Flexirest::Connection).not_to receive(:get)
161
+ p = Person.new(first_name:"Billy")
162
+ ret = p.all({})
163
+ expect(ret.first_name).to eq("Johnny")
164
+ end
165
+
166
+ it "should restore a result iterator from the cache store, if there's a hard expiry" do
167
+ class CachingExample3 < Flexirest::Base ; end
168
+ object = Flexirest::ResultIterator.new(double(status: 200))
169
+ object << CachingExample3.new(first_name:"Johnny")
170
+ object << CachingExample3.new(first_name:"Billy")
171
+ etag = "6527914a91e0c5769f6de281f25bd891"
172
+ cached_response = Flexirest::CachedResponse.new(
173
+ status:200,
174
+ result:object,
175
+ etag:etag,
176
+ expires:Time.now + 30)
177
+ expect_any_instance_of(CachingExampleCacheStore5).to receive(:read).once.with("Person:/").and_return(Marshal.dump(cached_response))
178
+ expect_any_instance_of(Flexirest::Connection).not_to receive(:get)
179
+ ret = Person.all
180
+ expect(ret.first.first_name).to eq("Johnny")
181
+ expect(ret._status).to eq(200)
182
+ end
183
+
184
+ it "should not write the response to the cache unless if has caching headers" do
185
+ expect_any_instance_of(CachingExampleCacheStore5).to receive(:read).once.with("Person:/").and_return(nil)
186
+ expect_any_instance_of(CachingExampleCacheStore5).not_to receive(:write)
187
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/", an_instance_of(Hash)).and_return(OpenStruct.new(status:200, body:"{\"result\":true}", headers:{}))
188
+ Person.all
189
+ end
190
+
191
+ it "should write the response to the cache if there's an etag" do
192
+ expect_any_instance_of(CachingExampleCacheStore5).to receive(:read).once.with("Person:/").and_return(nil)
193
+ expect_any_instance_of(CachingExampleCacheStore5).to receive(:write).once.with("Person:/", an_instance_of(String), {})
194
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, body:"{\"result\":true}", response_headers:{etag:"1234567890"})))
195
+ Person.perform_caching true
196
+ Person.all
197
+ end
198
+
199
+ it "should write the response to the cache if there's a hard expiry" do
200
+ expect_any_instance_of(CachingExampleCacheStore5).to receive(:read).once.with("Person:/").and_return(nil)
201
+ expect_any_instance_of(CachingExampleCacheStore5).to receive(:write).once.with("Person:/", an_instance_of(String), an_instance_of(Hash))
202
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, body:"{\"result\":true}", response_headers:{expires:(Time.now + 30).rfc822})))
203
+ Person.perform_caching = true
204
+ Person.all
205
+ end
206
+
207
+ it "should not write the response to the cache if there's an invalid expiry" do
208
+ expect_any_instance_of(CachingExampleCacheStore5).to receive(:read).once.with("Person:/").and_return(nil)
209
+ expect_any_instance_of(CachingExampleCacheStore5).to_not receive(:write).once.with("Person:/", an_instance_of(String), an_instance_of(Hash))
210
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/", an_instance_of(Hash)).and_return(OpenStruct.new(status:200, body:"{\"result\":true}", headers:{expires:"0"}))
211
+ Person.perform_caching = true
212
+ Person.all
213
+ end
214
+
215
+ end
216
+
217
+ end
@@ -0,0 +1,234 @@
1
+ require 'spec_helper'
2
+
3
+ describe Flexirest::Configuration do
4
+ before :each do
5
+ Object.send(:remove_const, :ConfigurationExample) if defined?(ConfigurationExample)
6
+ Flexirest::Base._reset_configuration!
7
+
8
+ class ConfigurationExample
9
+ include Flexirest::Configuration
10
+ base_url "http://www.example.com"
11
+ username "john"
12
+ password "smith"
13
+ request_body_type :json
14
+ end
15
+
16
+ class ConfigurationExampleBare
17
+ include Flexirest::Configuration
18
+ end
19
+ end
20
+
21
+ it "should default to non-whiny missing methods" do
22
+ class UnusuedConfigurationExample1
23
+ include Flexirest::Configuration
24
+ end
25
+ expect(UnusuedConfigurationExample1.whiny_missing).to be_falsey
26
+ end
27
+
28
+ it "should allow whiny missing methods to be enabled" do
29
+ ConfigurationExample.whiny_missing true
30
+ expect(ConfigurationExample.whiny_missing).to be_truthy
31
+ end
32
+
33
+ it "should remember the set base_url" do
34
+ expect(ConfigurationExample.base_url).to eq("http://www.example.com")
35
+ end
36
+
37
+ it "should remember the set base_url on a class, overriding a general one" do
38
+ Flexirest::Base.base_url = "http://general.example.com"
39
+ expect(ConfigurationExample.base_url).to eq("http://www.example.com")
40
+ end
41
+
42
+ it "should remove a trailing slash from a globally configured base_url" do
43
+ Flexirest::Base.base_url = "http://general.example.com/"
44
+ expect(ConfigurationExample.base_url).to eq("http://www.example.com")
45
+ Flexirest::Base.base_url = ""
46
+ end
47
+
48
+ it "should remember the set base_url on the base class if a more specific one hasn't been set" do
49
+ Flexirest::Base.base_url = "http://general.example.com"
50
+ expect(ConfigurationExampleBare.base_url).to eq("http://general.example.com")
51
+ Flexirest::Base.base_url = ""
52
+ end
53
+
54
+ it "should remove a trailing slash from a specific class configured base_url" do
55
+ class ConfigurationExample2
56
+ include Flexirest::Configuration
57
+ base_url "http://specific.example.com/"
58
+ end
59
+ expect(ConfigurationExample2.base_url).to eq("http://specific.example.com")
60
+ end
61
+
62
+ it "should remember the set username" do
63
+ expect(ConfigurationExample.username).to eq("john")
64
+ end
65
+
66
+ it "should remember the set username on a class, overriding a general one" do
67
+ Flexirest::Base.username = "bill"
68
+ expect(ConfigurationExample.username).to eq("john")
69
+ Flexirest::Base.username = nil
70
+ end
71
+
72
+ it "should escape the username" do
73
+ Flexirest::Base.username = "bill@example.com"
74
+ expect(Flexirest::Base.username).to eq("bill%40example.com")
75
+ Flexirest::Base.username = nil
76
+ end
77
+
78
+ it "should not doubly escape the username" do
79
+ Flexirest::Base.username = "bill%40example.com"
80
+ expect(Flexirest::Base.username).to_not eq("bill%2540example.com")
81
+ Flexirest::Base.username = nil
82
+ end
83
+
84
+ it "should remember the set password" do
85
+ expect(ConfigurationExample.password).to eq("smith")
86
+ end
87
+
88
+ it "should remember the set password on a class, overriding a general one" do
89
+ Flexirest::Base.password = "bloggs"
90
+ expect(ConfigurationExample.password).to eq("smith")
91
+ Flexirest::Base.password = nil
92
+ end
93
+
94
+ it "should escape the password" do
95
+ Flexirest::Base.password = "something@else"
96
+ expect(Flexirest::Base.password).to eq("something%40else")
97
+ Flexirest::Base.password = nil
98
+ end
99
+
100
+ it "should not doubly escape the password" do
101
+ Flexirest::Base.password = "something%40else"
102
+ expect(Flexirest::Base.password).to_not eq("something%2540else")
103
+ Flexirest::Base.password = nil
104
+ end
105
+
106
+ it "should default to a form_encoded request_body_type" do
107
+ expect(Flexirest::Base.request_body_type).to eq(:form_encoded)
108
+ end
109
+
110
+ it "should remember the request_body_type" do
111
+ expect(ConfigurationExample.request_body_type).to eq(:json)
112
+ end
113
+
114
+ it "should remember the set base_url on a class, overriding a general one" do
115
+ Flexirest::Base.request_body_type = :unknown
116
+ expect(Flexirest::Base.request_body_type).to eq(:unknown)
117
+ expect(ConfigurationExample.request_body_type).to eq(:json)
118
+ end
119
+
120
+ it "should default to non-lazy loading" do
121
+ class LazyLoadingConfigurationExample1
122
+ include Flexirest::Configuration
123
+ end
124
+ expect(LazyLoadingConfigurationExample1.lazy_load?).to be_falsey
125
+ end
126
+
127
+ it "should be able to switch on lazy loading" do
128
+ class LazyLoadingConfigurationExample2
129
+ include Flexirest::Configuration
130
+ lazy_load!
131
+ end
132
+ expect(LazyLoadingConfigurationExample2.lazy_load?).to be_truthy
133
+ end
134
+
135
+ describe 'api auth' do
136
+ context 'default' do
137
+ it "should be false using_api_auth?" do
138
+ expect(Flexirest::Base.using_api_auth?).to be_falsey
139
+ end
140
+ end
141
+
142
+ context 'setting api auth credentials' do
143
+ before(:each) do
144
+ ConfigurationExample.api_auth_credentials('id123', 'secret123')
145
+ end
146
+
147
+ it "should remember setting using_api_auth?" do
148
+ expect(ConfigurationExample.using_api_auth?).to be_truthy
149
+ end
150
+
151
+ it "should remember setting api_auth_access_id" do
152
+ expect(ConfigurationExample.api_auth_access_id).to eq('id123')
153
+ end
154
+
155
+ it "should remember setting api_auth_secret_key" do
156
+ expect(ConfigurationExample.api_auth_secret_key).to eq('secret123')
157
+ end
158
+
159
+ it "should inherit api_auth_credentials when not set" do
160
+ class ConfigurationExtension < ConfigurationExample
161
+ end
162
+ expect(ConfigurationExtension.api_auth_access_id).to eq('id123')
163
+ expect(ConfigurationExtension.api_auth_secret_key).to eq('secret123')
164
+ end
165
+
166
+ it "should override inherited api_auth_credentials when set" do
167
+ class ConfigurationExtension2 < ConfigurationExample
168
+ end
169
+ ConfigurationExtension2.api_auth_credentials('id456', 'secret456')
170
+ expect(ConfigurationExtension2.api_auth_access_id).to eq('id456')
171
+ expect(ConfigurationExtension2.api_auth_secret_key).to eq('secret456')
172
+ end
173
+ end
174
+ end
175
+
176
+ it "should default to non-verbose loggingg" do
177
+ class VerboseConfigurationExample1
178
+ include Flexirest::Configuration
179
+ end
180
+ expect(VerboseConfigurationExample1.verbose).to be_falsey
181
+ end
182
+
183
+ it "should be able to switch on verbose logging" do
184
+ class VerboseConfigurationExample2
185
+ include Flexirest::Configuration
186
+ verbose!
187
+ end
188
+ class VerboseConfigurationExample3
189
+ include Flexirest::Configuration
190
+ verbose true
191
+ end
192
+ expect(VerboseConfigurationExample2.verbose).to be_truthy
193
+ expect(VerboseConfigurationExample3.verbose).to be_truthy
194
+ end
195
+
196
+ it "should store a translator given" do
197
+ expect{ ConfigurationExample.send(:translator) }.to_not raise_error
198
+ ConfigurationExample.send(:translator, String.new)
199
+ expect(ConfigurationExample.translator).to respond_to(:length)
200
+ end
201
+
202
+ it "should store a proxy given" do
203
+ expect{ ConfigurationExample.send(:proxy) }.to_not raise_error
204
+ ConfigurationExample.send(:proxy, String.new)
205
+ expect(ConfigurationExample.proxy).to respond_to(:length)
206
+ end
207
+
208
+ describe "faraday_config" do
209
+ let(:faraday_double){double(:faraday).as_null_object}
210
+
211
+ it "should use default adapter if no other block set" do
212
+ expect(faraday_double).to receive(:adapter).with(Faraday.default_adapter)
213
+ ConfigurationExample.faraday_config.call(faraday_double)
214
+ end
215
+
216
+ it "should us set adapter if no other block set" do
217
+ ConfigurationExample.adapter = :net_http
218
+
219
+ expect(faraday_double).to receive(:adapter).with(:net_http)
220
+
221
+ ConfigurationExample.faraday_config.call(faraday_double)
222
+ end
223
+
224
+ it "should use the adapter of the passed in faraday_config block" do
225
+ ConfigurationExample.faraday_config {|faraday| faraday.adapter(:rack)}
226
+
227
+ expect(faraday_double).to receive(:adapter).with(:rack)
228
+
229
+ ConfigurationExample.faraday_config.call(faraday_double)
230
+ end
231
+
232
+ end
233
+
234
+ end