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,44 @@
1
+ module Flexirest
2
+ class RequestDelegator < Delegator
3
+ def initialize(obj)
4
+ super
5
+ @delegate_obj = obj
6
+ end
7
+
8
+ def __getobj__
9
+ @delegate_obj
10
+ end
11
+
12
+ def __setobj__(obj)
13
+ @delegate_obj = obj
14
+ end
15
+
16
+ def class
17
+ @delegate_obj.class
18
+ end
19
+
20
+ def method_missing(name, *args, &block)
21
+ # Handles issue with private method 'test' on base Ruby Object
22
+ return @delegate_obj.test if name.to_sym == :test
23
+
24
+ # Forward request to delegate
25
+ @delegate_obj.send(name, *args, &block)
26
+ end
27
+
28
+ def kind_of?(obj)
29
+ @delegate_obj.kind_of?(obj)
30
+ end
31
+
32
+ def is_a?(obj)
33
+ @delegate_obj.is_a?(obj)
34
+ end
35
+
36
+ def instance_of?(obj)
37
+ @delegate_obj.instance_of?(obj)
38
+ end
39
+
40
+ def _delegate?
41
+ return true
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,62 @@
1
+ module Flexirest
2
+ module RequestFiltering
3
+ module ClassMethods
4
+ def before_request(method_name = nil, &block)
5
+ @before_filters ||= []
6
+ if block
7
+ @before_filters << block
8
+ elsif method_name
9
+ @before_filters << method_name
10
+ end
11
+ end
12
+
13
+ def after_request(method_name = nil, &block)
14
+ @after_filters ||= []
15
+ if block
16
+ @after_filters << block
17
+ elsif method_name
18
+ @after_filters << method_name
19
+ end
20
+ end
21
+
22
+ def _filter_request(type, name, param)
23
+ _handle_super_class_filters(type, name, param)
24
+ filters = (type == :before ? @before_filters : @after_filters)
25
+ filters ||= []
26
+ filters.each do |filter|
27
+ if filter.is_a? Symbol
28
+ if self.respond_to?(filter)
29
+ self.send(filter, name, param)
30
+ else
31
+ instance = self.new
32
+ instance.send(filter, name, param)
33
+ end
34
+ else
35
+ filter.call(name, param)
36
+ end
37
+ end
38
+ end
39
+
40
+ def _handle_super_class_filters(type, name, request)
41
+ @parents ||= []
42
+ @parents.each do |parent|
43
+ parent._filter_request(type, name, request)
44
+ end
45
+ end
46
+
47
+ def _parents
48
+ @parents ||= []
49
+ end
50
+
51
+ def inherited(subclass)
52
+ subclass._parents << self
53
+ super
54
+ end
55
+ end
56
+
57
+ def self.included(base)
58
+ base.extend(ClassMethods)
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,85 @@
1
+ module Flexirest
2
+ class ResultIterator
3
+ include Enumerable
4
+
5
+ attr_accessor :_status
6
+ attr_reader :items, :_headers
7
+
8
+ def initialize(response = nil)
9
+ @_status = response.try :status
10
+ @_headers = response.try :response_headers
11
+ @items = []
12
+ end
13
+
14
+ def <<(item)
15
+ @items << item
16
+ end
17
+
18
+ def size
19
+ @items.size
20
+ end
21
+
22
+ def index(value)
23
+ @items.index(value)
24
+ end
25
+
26
+ def empty?
27
+ size == 0
28
+ end
29
+
30
+ def reverse
31
+ @reversed_items ||= @items.reverse
32
+ end
33
+
34
+ def each
35
+ @items.each do |el|
36
+ yield el
37
+ end
38
+ end
39
+
40
+ def last
41
+ @items.last
42
+ end
43
+
44
+ def [](key)
45
+ @items[key]
46
+ end
47
+
48
+ def shuffle
49
+ @items = @items.shuffle
50
+ self
51
+ end
52
+
53
+ def parallelise(method=nil)
54
+ collected_responses = []
55
+ threads = []
56
+ @items.each do |item|
57
+ threads << Thread.new do
58
+ ret = item.send(method) if method
59
+ ret = yield(item) if block_given?
60
+ Thread.current[:response] = ret
61
+ end
62
+ end
63
+ threads.each do |t|
64
+ t.join
65
+ collected_responses << t[:response]
66
+ end
67
+ collected_responses
68
+ end
69
+
70
+ def paginate(options = {})
71
+ raise WillPaginateNotAvailableException.new unless Object.constants.include?(:WillPaginate)
72
+
73
+ page = options[:page] || 1
74
+ per_page = options[:per_page] || WillPaginate.per_page
75
+ total = options[:total_entries] || @items.length
76
+
77
+ WillPaginate::Collection.create(page, per_page, total) do |pager|
78
+ pager.replace @items[pager.offset, pager.per_page].to_a
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+ class WillPaginateNotAvailableException < StandardError ; end
85
+ end
@@ -0,0 +1,60 @@
1
+ module Flexirest
2
+ module Validation
3
+ module ClassMethods
4
+ def validates(field_name, options={}, &block)
5
+ @_validations ||= []
6
+ @_validations << {field_name:field_name, options:options, block:block}
7
+ end
8
+
9
+ def _validations
10
+ @_validations ||= []
11
+ @_validations
12
+ end
13
+ end
14
+
15
+ def self.included(base)
16
+ base.extend(ClassMethods)
17
+ end
18
+
19
+ def valid?
20
+ @errors = Hash.new {|h,k| h[k] = []}
21
+ self.class._validations.each do |validation|
22
+ value = self.send(validation[:field_name])
23
+ validation[:options].each do |type, options|
24
+ if type == :presence
25
+ if value.nil?
26
+ @errors[validation[:field_name]] << "must be present"
27
+ end
28
+ elsif type == :length
29
+ if options[:within]
30
+ @errors[validation[:field_name]] << "must be within range #{options[:within]}" unless options[:within].include?(value.to_s.length )
31
+ end
32
+ if options[:minimum]
33
+ @errors[validation[:field_name]] << "must be at least #{options[:minimum]} characters long" unless value.to_s.length >= options[:minimum]
34
+ end
35
+ if options[:maximum]
36
+ @errors[validation[:field_name]] << "must be no more than #{options[:minimum]} characters long" unless value.to_s.length <= options[:maximum]
37
+ end
38
+ elsif type == :numericality
39
+ numeric = (true if Float(value) rescue false)
40
+ @errors[validation[:field_name]] << "must be numeric" unless numeric
41
+ elsif type == :minimum && !value.nil?
42
+ @errors[validation[:field_name]] << "must be at least #{options}" unless value.to_f >= options.to_f
43
+ elsif type == :maximum && !value.nil?
44
+ @errors[validation[:field_name]] << "must be no more than #{options}" unless value.to_f <= options.to_f
45
+ end
46
+ end
47
+ if validation[:block]
48
+ validation[:block].call(self, validation[:field_name], value)
49
+ end
50
+ end
51
+ @errors.empty?
52
+ end
53
+
54
+ def _errors
55
+ @errors ||= Hash.new {|h,k| h[k] = []}
56
+ @errors
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,3 @@
1
+ module Flexirest
2
+ VERSION = "1.2.0"
3
+ end
@@ -0,0 +1,389 @@
1
+ require 'spec_helper'
2
+
3
+ class EmptyExample < Flexirest::Base
4
+ whiny_missing true
5
+ end
6
+
7
+ class TranslatorExample
8
+ def self.all(object)
9
+ ret = {}
10
+ ret["first_name"] = object["name"]
11
+ ret
12
+ end
13
+ end
14
+
15
+ class AlteringClientExample < Flexirest::Base
16
+ translator TranslatorExample
17
+ base_url "http://www.example.com"
18
+
19
+ get :all, "/all", fake:"{\"name\":\"Billy\"}"
20
+ get :list, "/list", fake:"{\"name\":\"Billy\", \"country\":\"United Kingdom\"}"
21
+ get :iterate, "/iterate", fake:"{\"name\":\"Billy\", \"country\":\"United Kingdom\"}"
22
+ get :find, "/find/:id"
23
+ end
24
+
25
+ class RecordResponseExample < Flexirest::Base
26
+ base_url "http://www.example.com"
27
+
28
+ record_response do |url, response|
29
+ raise Exception.new("#{url}|#{response.body}")
30
+ end
31
+
32
+ get :all, "/all"
33
+ end
34
+
35
+ class NonHostnameBaseUrlExample < Flexirest::Base
36
+ base_url "http://www.example.com/v1/"
37
+ get :all, "/all"
38
+ end
39
+
40
+ describe Flexirest::Base do
41
+ it 'should instantiate a new descendant' do
42
+ expect{EmptyExample.new}.to_not raise_error
43
+ end
44
+
45
+ it "should not instantiate a new base class" do
46
+ expect{Flexirest::Base.new}.to raise_error(Exception)
47
+ end
48
+
49
+ it "should save attributes passed in constructor" do
50
+ client = EmptyExample.new(:test => "Something")
51
+ expect(client._attributes[:test]).to be_a(String)
52
+ end
53
+
54
+ it "should allow attribute reading using missing method names" do
55
+ client = EmptyExample.new(:test => "Something")
56
+ expect(client.test).to eq("Something")
57
+ end
58
+
59
+ it "should allow attribute reading using [] array notation" do
60
+ client = EmptyExample.new(:test => "Something")
61
+ expect(client["test"]).to eq("Something")
62
+ end
63
+
64
+ it "allows iteration over attributes using each" do
65
+ client = AlteringClientExample.iterate
66
+ expect(client).to be_respond_to(:each)
67
+ keys = []
68
+ values = []
69
+ client.each do |key, value|
70
+ keys << key ; values << value
71
+ end
72
+ expect(keys).to eq(%w{name country}.map(&:to_sym))
73
+ expect(values).to eq(["Billy", "United Kingdom"])
74
+ end
75
+
76
+ it "should automatically parse ISO 8601 format date and time" do
77
+ t = Time.now
78
+ client = EmptyExample.new(:test => t.iso8601)
79
+ expect(client["test"]).to be_an_instance_of(DateTime)
80
+ expect(client["test"].to_s).to eq(t.to_datetime.to_s)
81
+ end
82
+
83
+ it "should automatically parse ISO 8601 format date and time with milliseconds" do
84
+ t = Time.now
85
+ client = EmptyExample.new(:test => t.iso8601(3))
86
+ expect(client["test"]).to be_an_instance_of(DateTime)
87
+ expect(client["test"].to_s).to eq(t.to_datetime.to_s)
88
+ end
89
+
90
+ it "should automatically parse ISO 8601 format dates" do
91
+ d = Date.today
92
+ client = EmptyExample.new(:test => d.iso8601)
93
+ expect(client["test"]).to be_an_instance_of(Date)
94
+ expect(client["test"]).to eq(d)
95
+ end
96
+
97
+ it "should store attributes set using missing method names and mark them as dirty" do
98
+ client = EmptyExample.new()
99
+ client.test = "Something"
100
+ expect(client.test.to_s).to eq("Something")
101
+ expect(client).to be_dirty
102
+ end
103
+
104
+ it "should store attribute set using []= array notation and mark them as dirty" do
105
+ client = EmptyExample.new()
106
+ client["test"] = "Something"
107
+ expect(client["test"].to_s).to eq("Something")
108
+ expect(client).to be_dirty
109
+ end
110
+
111
+ it "should overwrite attributes already set and mark them as dirty" do
112
+ client = EmptyExample.new(:hello => "World")
113
+ client._clean!
114
+ expect(client).to_not be_dirty
115
+
116
+ client.hello = "Everybody"
117
+ expect(client).to be_dirty
118
+ end
119
+
120
+ it 'should respond_to? attributes defined in the response' do
121
+ client = EmptyExample.new(:hello => "World")
122
+ expect(client.respond_to?(:hello)).to be_truthy
123
+ expect(client.respond_to?(:world)).to be_falsey
124
+ end
125
+
126
+ it "should save the base URL for the API server" do
127
+ class BaseExample < Flexirest::Base
128
+ base_url "https://www.example.com/api/v1"
129
+ end
130
+ expect(BaseExample.base_url).to eq("https://www.example.com/api/v1")
131
+ end
132
+
133
+ it "should allow changing the base_url while running" do
134
+ class OutsideBaseExample < Flexirest::Base ; end
135
+
136
+ Flexirest::Base.base_url = "https://www.example.com/api/v1"
137
+ expect(OutsideBaseExample.base_url).to eq("https://www.example.com/api/v1")
138
+
139
+ Flexirest::Base.base_url = "https://www.example.com/api/v2"
140
+ expect(OutsideBaseExample.base_url).to eq("https://www.example.com/api/v2")
141
+ end
142
+
143
+ it "should include the Mapping module" do
144
+ expect(EmptyExample).to respond_to(:_calls)
145
+ expect(EmptyExample).to_not respond_to(:_non_existant)
146
+ end
147
+
148
+ it "should be able to easily clean all attributes" do
149
+ client = EmptyExample.new(hello:"World", goodbye:"Everyone")
150
+ expect(client).to be_dirty
151
+ client._clean!
152
+ expect(client).to_not be_dirty
153
+ end
154
+
155
+ it "should not overly pollute the instance method namespace to reduce chances of clashing (<10 instance methods)" do
156
+ instance_methods = EmptyExample.instance_methods - Object.methods
157
+ instance_methods = instance_methods - instance_methods.grep(/^_/)
158
+ expect(instance_methods.size).to be < 10
159
+ end
160
+
161
+ it "should raise an exception for missing attributes if whiny_missing is enabled" do
162
+ expect{EmptyExample.new.first_name}.to raise_error(Flexirest::NoAttributeException)
163
+ end
164
+
165
+ it "should be able to lazy instantiate an object from a prefixed lazy_ method call" do
166
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with('/find/1', anything).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
167
+ example = AlteringClientExample.lazy_find(1)
168
+ expect(example).to be_an_instance_of(Flexirest::LazyLoader)
169
+ expect(example.first_name).to eq("Billy")
170
+ end
171
+
172
+ it "should be able to lazy instantiate an object from a prefixed lazy_ method call from an instance" do
173
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with('/find/1', anything).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
174
+ example = AlteringClientExample.new.lazy_find(1)
175
+ expect(example).to be_an_instance_of(Flexirest::LazyLoader)
176
+ expect(example.first_name).to eq("Billy")
177
+ end
178
+
179
+ context "#inspect output" do
180
+ it "displays a nice version" do
181
+ object = EmptyExample.new(id: 1, name: "John Smith")
182
+ expect(object.inspect).to match(/#<EmptyExample id: 1, name: "John Smith"/)
183
+ end
184
+
185
+ it "shows dirty attributes as a list of names at the end" do
186
+ object = EmptyExample.new(id: 1, name: "John Smith")
187
+ expect(object.inspect).to match(/#<EmptyExample id: 1, name: "John Smith" \(unsaved: id, name\)/)
188
+ end
189
+
190
+ it "doesn't show an empty list of dirty attributes" do
191
+ object = EmptyExample.new(id: 1, name: "John Smith")
192
+ object.instance_variable_set(:@dirty_attributes, Set.new)
193
+ expect(object.inspect).to_not match(/\(unsaved: id, name\)/)
194
+ end
195
+
196
+ it "shows dates in a nice format" do
197
+ object = EmptyExample.new(dob: Time.new(2015, 01, 02, 03, 04, 05))
198
+ expect(object.inspect).to match(/#<EmptyExample dob: "2015\-01\-02 03:04:05"/)
199
+ end
200
+
201
+ it "shows the etag if one is set" do
202
+ object = EmptyExample.new(id: 1)
203
+ object.instance_variable_set(:@_etag, "sample_etag")
204
+ expect(object.inspect).to match(/#<EmptyExample id: 1, ETag: sample_etag/)
205
+ end
206
+
207
+ it "shows the HTTP status code if one is set" do
208
+ object = EmptyExample.new(id: 1)
209
+ object.instance_variable_set(:@_status, 200)
210
+ expect(object.inspect).to match(/#<EmptyExample id: 1, Status: 200/)
211
+ end
212
+
213
+ it "shows [uninitialized] for new objects" do
214
+ object = EmptyExample.new
215
+ expect(object.inspect).to match(/#<EmptyExample \[uninitialized\]/)
216
+ end
217
+
218
+ end
219
+
220
+ context "accepts a Translator to reformat JSON" do
221
+ it "should log a deprecation warning when using a translator" do
222
+ expect(Flexirest::Logger).to receive(:warn) do |message|
223
+ expect(message).to start_with("DEPRECATION")
224
+ end
225
+ Proc.new do
226
+ class DummyExample < Flexirest::Base
227
+ translator TranslatorExample
228
+ end
229
+ end.call
230
+ end
231
+
232
+ it "should call Translator#method when calling the mapped method if it responds to it" do
233
+ expect(TranslatorExample).to receive(:all).with(an_instance_of(Hash)).and_return({})
234
+ AlteringClientExample.all
235
+ end
236
+
237
+ it "should not raise errors when calling Translator#method if it does not respond to it" do
238
+ expect {AlteringClientExample.list}.to_not raise_error
239
+ end
240
+
241
+ it "should translate JSON returned through the Translator" do
242
+ ret = AlteringClientExample.all
243
+ expect(ret.first_name).to eq("Billy")
244
+ expect(ret.name).to be_nil
245
+ end
246
+
247
+ it "should return original JSON for items that aren't handled by the Translator" do
248
+ ret = AlteringClientExample.list
249
+ expect(ret.name).to eq("Billy")
250
+ expect(ret.first_name).to be_nil
251
+ end
252
+ end
253
+
254
+ context "directly call a URL, rather than via a mapped method" do
255
+ it "should be able to directly call a URL" do
256
+ expect_any_instance_of(Flexirest::Request).to receive(:do_request).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
257
+ EmptyExample._request("http://api.example.com/")
258
+ end
259
+
260
+ it "runs filters as usual" do
261
+ expect_any_instance_of(Flexirest::Request).to receive(:do_request).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
262
+ expect(EmptyExample).to receive(:_filter_request).with(any_args).exactly(2).times
263
+ EmptyExample._request("http://api.example.com/")
264
+ end
265
+
266
+ it "should make an HTTP request" do
267
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
268
+ EmptyExample._request("http://api.example.com/")
269
+ end
270
+
271
+ it "should make an HTTP request including the path in the base_url" do
272
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with('/v1/all', anything).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
273
+ NonHostnameBaseUrlExample.all
274
+ end
275
+
276
+ it "should map the response from the directly called URL in the normal way" do
277
+ expect_any_instance_of(Flexirest::Request).to receive(:do_request).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
278
+ example = EmptyExample._request("http://api.example.com/")
279
+ expect(example.first_name).to eq("Billy")
280
+ end
281
+
282
+ it "should be able to pass the plain response from the directly called URL bypassing JSON loading" do
283
+ response_body = "This is another non-JSON string"
284
+ expect_any_instance_of(Flexirest::Connection).to receive(:post).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:response_body)))
285
+ expect(EmptyExample._plain_request("http://api.example.com/", :post, {id:1234})).to eq(response_body)
286
+ end
287
+
288
+ context "Simulating Faraday connection in_parallel" do
289
+ it "should be able to pass the plain response from the directly called URL bypassing JSON loading" do
290
+ response_body = "This is another non-JSON string"
291
+ response = ::FaradayResponseMock.new(
292
+ OpenStruct.new(status:200, response_headers:{}, body:response_body),
293
+ false)
294
+ expect_any_instance_of(Flexirest::Connection).to receive(:post).with(any_args).and_return(response)
295
+ result = EmptyExample._plain_request("http://api.example.com/", :post, {id:1234})
296
+
297
+ expect(result).to eq(nil)
298
+
299
+ response.finish
300
+ expect(result).to eq(response_body)
301
+ end
302
+ end
303
+
304
+ it "should cache plain requests separately" do
305
+ perform_caching = EmptyExample.perform_caching
306
+ cache_store = EmptyExample.cache_store
307
+ begin
308
+ response = "This is a non-JSON string"
309
+ other_response = "This is another non-JSON string"
310
+ allow_any_instance_of(Flexirest::Connection).to receive(:get) do |instance, url, others|
311
+ if url == "/?test=1"
312
+ ::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:response))
313
+ else
314
+ ::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:other_response))
315
+ end
316
+ end
317
+ EmptyExample.perform_caching = true
318
+ EmptyExample.cache_store = TestCacheStore.new
319
+ expect(EmptyExample._plain_request("http://api.example.com/?test=1")).to eq(response)
320
+ expect(EmptyExample._plain_request("http://api.example.com/?test=2")).to eq(other_response)
321
+ ensure
322
+ EmptyExample.perform_caching = perform_caching
323
+ EmptyExample.cache_store = cache_store
324
+ end
325
+ end
326
+
327
+ it "should be able to lazy load a direct URL request" do
328
+ expect_any_instance_of(Flexirest::Request).to receive(:do_request).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
329
+ example = EmptyExample._lazy_request("http://api.example.com/")
330
+ expect(example).to be_an_instance_of(Flexirest::LazyLoader)
331
+ expect(example.first_name).to eq("Billy")
332
+ end
333
+
334
+ it "should be able to specify a method and parameters for the call" do
335
+ expect_any_instance_of(Flexirest::Connection).to receive(:post).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
336
+ EmptyExample._request("http://api.example.com/", :post, {id:1234})
337
+ end
338
+
339
+ it "should be able to use mapped methods to create a request to pass in to _lazy_request" do
340
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with('/find/1', anything).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
341
+ request = AlteringClientExample._request_for(:find, :id => 1)
342
+ example = AlteringClientExample._lazy_request(request)
343
+ expect(example.first_name).to eq("Billy")
344
+ end
345
+ end
346
+
347
+ context "Recording a response" do
348
+ it "calls back to the record_response callback with the url and response body" do
349
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"Hello world")))
350
+ expect{RecordResponseExample.all}.to raise_error(Exception, "/all|Hello world")
351
+ end
352
+ end
353
+
354
+ context "JSON output" do
355
+ let(:student1) { EmptyExample.new(name:"John Smith", age:31) }
356
+ let(:student2) { EmptyExample.new(name:"Bob Brown", age:29) }
357
+ let(:location) { EmptyExample.new(place:"Room 1408") }
358
+ let(:lazy) { Laz }
359
+ let(:object) { EmptyExample.new(name:"Programming 101", location:location, students:[student1, student2]) }
360
+ let(:json_parsed_object) { MultiJson.load(object.to_json) }
361
+
362
+ it "should be able to export to valid json" do
363
+ expect(object.to_json).to_not be_blank
364
+ expect{MultiJson.load(object.to_json)}.to_not raise_error
365
+ end
366
+
367
+ it "should not be using Object's #to_json method" do
368
+ expect(json_parsed_object["dirty_attributes"]).to be_nil
369
+ end
370
+
371
+ it "should recursively convert nested objects" do
372
+ expect(json_parsed_object["location"]["place"]).to eq(location.place)
373
+ end
374
+
375
+ it "should include arrayed objects" do
376
+ expect(json_parsed_object["students"]).to be_an_instance_of(Array)
377
+ expect(json_parsed_object["students"].size).to eq(2)
378
+ expect(json_parsed_object["students"].first["name"]).to eq(student1.name)
379
+ expect(json_parsed_object["students"].second["name"]).to eq(student2.name)
380
+ end
381
+
382
+ it "should set integers as a native JSON type" do
383
+ expect(json_parsed_object["students"].first["age"]).to eq(student1.age)
384
+ expect(json_parsed_object["students"].second["age"]).to eq(student2.age)
385
+ end
386
+
387
+ end
388
+
389
+ end