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