flexirest 1.9.11 → 1.9.16

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 294f2e24981dde7af931d0bedd578e4eb260538f947f716739a3d4037cd2a6b8
4
- data.tar.gz: 5ab2e8babd0e312eea734257e6fdeeb1ccd1701143ec4a953a0ea90b102ed3f4
3
+ metadata.gz: 70788d1c6cfd0e3490c50e6eec78136eb5f6e1031dd6135e943ab7a1a0367db0
4
+ data.tar.gz: ed5282f589dd2893fc16db20197d6109382887a6b3a5a936e2f04113c6ba70cf
5
5
  SHA512:
6
- metadata.gz: ce1125b17e4a41d11bc3cb3be10888d89604beaff46839fe3dbbce1fb663b4d69bbc58f3a8dab9463b2367daae9e182751393c8adc2ed5148e90138ce88ab167
7
- data.tar.gz: c4f992e9c9e70b2faabf3384512f6973b6e3f5fb8ebca7515faae48670ec61b630a2c951d908efbf95170506885f19a9e87454ce2bc0caac64a0a6f01a4f9770
6
+ metadata.gz: 21b69a5820a346cbdfded7e4f0c30e79f5ff10d4f2e272d59f0163070d1e4a729ebaf95d9fe7ccbb70fc15ba952bdee741bcbb994740b40e1882d7c6a0516ba3
7
+ data.tar.gz: 57fc2b560629d9cf2521d4f06b3f0dcdca059a686a88f3fe7cc8ca2b4ac912bd1896074243c8a589d5989b4019acc8d0eac709dd95d1f2cd7885ac0523eac702
@@ -1,5 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 2.7.0
3
4
  - 2.6.0
4
5
  - 2.5.0
5
- - 2.4.0
@@ -1,5 +1,36 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.16
4
+
5
+ Bugfix:
6
+
7
+ - Cached responses were always returning as `dirty?`/`changed?` (thanks to AKRathore for the bug report).
8
+
9
+ ## 1.9.15
10
+
11
+ Bugfix:
12
+
13
+ - Fix not marking unchanged attributes as dirty (thanks to Khairi Adnan for the bug report).
14
+
15
+ ## 1.9.14
16
+
17
+ Bugfix:
18
+
19
+ - Remove deprecation warning for `URI.escape` on Ruby 2.7.x.
20
+
21
+ ## 1.9.13
22
+
23
+ Change:
24
+
25
+ - Unified the response body to be `body` for all exceptions instead of sometimes being `body` and sometimes being `raw_response`, although both are available as aliases where they were defined before (thanks to Romain Gisiger for the PR).
26
+ - Adjust parsing of attributes to be done in a more unified way and lighten the CPU load (thanks to Romain Gisiger for the PR)
27
+
28
+ ## 1.9.12
29
+
30
+ Bugfix:
31
+
32
+ - Prevent crash on JSONAPI invalid error response (thanks to François Ferrandis for the PR).
33
+
3
34
  ## 1.9.11
4
35
 
5
36
  Bugfix:
@@ -1,16 +1,29 @@
1
1
  # *Flexirest:* HTTP/parse error handling
2
2
 
3
- Sometimes the backend server may respond with a non-200/304 header, in which case the code will raise an `Flexirest::HTTPClientException` for 4xx errors or an `Flexirest::HTTPServerException` for 5xx errors. These both have a `status` accessor and a `result` accessor (for getting access to the parsed body):
3
+ ## HTTP Errors
4
+
5
+ Sometimes the backend server may respond with a non-2xx/3xx header, in which case the code will raise an `Flexirest::HTTPClientException` for 4xx errors or an `Flexirest::HTTPServerException` for 5xx errors.
6
+
7
+ These both have a `status` accessor, a `result` accessor (for getting access to the parsed body) and a `body` accessor (for getting access to the original unparsed body). However, you can make use of the `message` method (or the `to_s` method) that will build a nice error message for you (containing the URL and the HTTP method used for the request as well as the HTTP status code and the original body of the response).
4
8
 
5
9
  ```ruby
6
10
  begin
7
11
  Person.all
8
12
  rescue Flexirest::HTTPClientException, Flexirest::HTTPServerException => e
9
- Rails.logger.error("API returned #{e.status} : #{e.result.message}")
13
+ # Display the HTTP status and parsed body
14
+ puts "Backend returned HTTP #{e.status} with following parsed body: #{e.result}"
15
+
16
+ # Display the unparsed body
17
+ puts "Original body is: #{e.body}"
18
+
19
+ # Using the message method, same than calling the to_s method
20
+ puts e.message
10
21
  end
11
22
  ```
12
23
 
13
- If the response is unparsable (e.g. not in the desired content type), then it will raise an `Flexirest::ResponseParseException` which has a `status` accessor for the HTTP status code and a `body` accessor for the unparsed response body.
24
+ ## Parse Error
25
+
26
+ If the original response is unparsable (e.g. not in the desired content type), then it will raise an `Flexirest::ResponseParseException` which has a `status` accessor for the HTTP status code and a `body` accessor for the unparsed response body.
14
27
 
15
28
  -----
16
29
 
@@ -36,7 +36,7 @@ Gem::Specification.new do |spec|
36
36
  spec.add_development_dependency "simplecov"
37
37
  spec.add_development_dependency "simplecov-rcov"
38
38
  spec.add_development_dependency 'coveralls'
39
- spec.add_development_dependency "api-auth", ">= 1.3.1"
39
+ spec.add_development_dependency "api-auth", ">= 1.3.1", "< 2.4"
40
40
  spec.add_development_dependency 'typhoeus'
41
41
  spec.add_development_dependency 'activemodel'
42
42
 
@@ -3,7 +3,8 @@ module Flexirest
3
3
  private
4
4
 
5
5
  def parse_attribute_value(v)
6
- return v if v.is_a?(Date) || v.is_a?(DateTime)
6
+ return v if v.is_a?(Date) || v.is_a?(DateTime) ||
7
+ v.kind_of?(NilClass) || v.kind_of?(TrueClass) || v.kind_of?(FalseClass) || v.kind_of?(Numeric)
7
8
 
8
9
  if v.to_s[(/\A(((19|20)\d\d[- \/.](0[1-9]|1[012]|[1-9])[- \/.](0[1-9]|[12][0-9]|3[01]|[1-9]))|((0[1-9]|1[012]|[1-9])[- \/.](0[1-9]|[12][0-9]|3[01]|[1-9])[- \/.](19|20)\d\d))\Z/)]
9
10
  Date.parse(v) rescue v
@@ -41,6 +41,7 @@ module Flexirest
41
41
  def _copy_from(result)
42
42
  @attributes = result._attributes
43
43
  @_status = result._status
44
+ _clean!
44
45
  end
45
46
 
46
47
  def dirty?
@@ -189,7 +190,7 @@ module Flexirest
189
190
  old_value = @dirty_attributes[key.to_sym]
190
191
  old_value = @attributes[key.to_sym] unless old_value
191
192
  old_value = old_value[0] if old_value and old_value.is_a? Array
192
- @dirty_attributes[key.to_sym] = [old_value, value]
193
+ @dirty_attributes[key.to_sym] = [old_value, value] if old_value != value
193
194
  @attributes[key.to_sym] = value
194
195
  end
195
196
 
@@ -110,9 +110,12 @@ module Flexirest
110
110
  if @result.is_a?(Array)
111
111
  ri = ResultIterator.new(self)
112
112
  ri.items = @result.map{|i| @class_name.constantize.new(i)}
113
+ ri._clean!
113
114
  ri
114
115
  else
115
- @class_name.constantize.new(@result)
116
+ obj = @class_name.constantize.new(@result)
117
+ obj._clean!
118
+ obj
116
119
  end
117
120
  end
118
121
  end
@@ -196,8 +196,10 @@ module Flexirest
196
196
  # Save resource class for building lazy association loaders
197
197
  save_resource_class(object)
198
198
 
199
- # try to return errors if their is no data
200
- return body.fetch("errors", {}) if body['data'].nil?
199
+ # According to the spec:
200
+ # "The members data and errors MUST NOT coexist in the same document."
201
+ # Thus, if the "errors" key is present, we can return it and ignore the "data" key.
202
+ return body['errors'] if body.include?('errors')
201
203
 
202
204
  # return early if data is an empty array
203
205
  return [] if body['data'] == []
@@ -380,7 +380,7 @@ module Flexirest
380
380
  if target.to_s.blank?
381
381
  missing << token
382
382
  end
383
- @url.gsub!(":#{token}", URI.escape(target.to_s).gsub("/", "%2F").gsub("+", "%2B"))
383
+ @url.gsub!(":#{token}", URI.encode_www_form_component(target.to_s))
384
384
  end
385
385
  end
386
386
 
@@ -656,40 +656,52 @@ module Flexirest
656
656
  k = k.to_sym
657
657
  end
658
658
  overridden_name = select_name(k, overridden_name)
659
- if @method[:options][:lazy].include?(k)
660
- object._attributes[k] = Flexirest::LazyAssociationLoader.new(overridden_name, v, self, overridden_name:(overridden_name))
661
- elsif v.is_a? Hash
662
- object._attributes[k] = new_object(v, overridden_name )
663
- elsif v.is_a? Array
664
- if @method[:options][:array].include?(k)
665
- object._attributes[k] = Array.new
666
- else
667
- object._attributes[k] = Flexirest::ResultIterator.new
668
- end
669
- v.each do |item|
670
- if item.is_a? Hash
671
- object._attributes[k] << new_object(item, overridden_name)
672
- else
673
- object._attributes[k] << item
674
- end
675
- end
659
+ set_corresponding_value(v, k, object, overridden_name)
660
+ end
661
+ object.clean! unless object_is_class?
662
+
663
+ object
664
+ end
665
+
666
+ def set_corresponding_value(value, key = nil, object = nil, overridden_name = nil)
667
+ optional_args = [key, object, overridden_name]
668
+ value_from_object = optional_args.all? # trying to parse a JSON Hash value
669
+ value_from_other_type = optional_args.none? # trying to parse anything else
670
+ raise Flexirest::InvalidArgumentsException.new("Optional args need all to be filled or none") unless value_from_object || value_from_other_type
671
+ k = key || :key
672
+ v = value
673
+ assignable_hash = value_from_object ? object._attributes : {}
674
+ if value_from_object && @method[:options][:lazy].include?(k)
675
+ assignable_hash[k] = Flexirest::LazyAssociationLoader.new(overridden_name, v, self, overridden_name:(overridden_name))
676
+ elsif v.is_a? Hash
677
+ assignable_hash[k] = new_object(v, overridden_name )
678
+ elsif v.is_a? Array
679
+ if @method[:options][:array].include?(k)
680
+ assignable_hash[k] = Array.new
676
681
  else
677
- parse_fields = [ @method[:options][:parse_fields], @object._date_fields ].compact.reduce([], :|)
678
- parse_fields = nil if parse_fields.empty?
679
- if (parse_fields && parse_fields.include?(k))
680
- object._attributes[k] = parse_attribute_value(v)
681
- elsif parse_fields
682
- object._attributes[k] = v
683
- elsif Flexirest::Base.disable_automatic_date_parsing
684
- object._attributes[k] = v
682
+ assignable_hash[k] = Flexirest::ResultIterator.new
683
+ end
684
+ v.each do |item|
685
+ if item.is_a? Hash
686
+ assignable_hash[k] << new_object(item, overridden_name)
685
687
  else
686
- object._attributes[k] = parse_attribute_value(v)
688
+ assignable_hash[k] << set_corresponding_value(item)
687
689
  end
688
690
  end
691
+ else
692
+ parse_fields = [ @method[:options][:parse_fields], @object._date_fields ].compact.reduce([], :|)
693
+ parse_fields = nil if parse_fields.empty?
694
+ if (parse_fields && parse_fields.include?(k))
695
+ assignable_hash[k] = parse_attribute_value(v)
696
+ elsif parse_fields
697
+ assignable_hash[k] = v
698
+ elsif Flexirest::Base.disable_automatic_date_parsing
699
+ assignable_hash[k] = v
700
+ else
701
+ assignable_hash[k] = parse_attribute_value(v)
702
+ end
689
703
  end
690
- object.clean! unless object_is_class?
691
-
692
- object
704
+ value_from_object ? object : assignable_hash[k]
693
705
  end
694
706
 
695
707
  def hal_response?
@@ -822,12 +834,10 @@ module Flexirest
822
834
 
823
835
  def add_nested_body_to_iterator(result, items)
824
836
  items.each do |json_object|
825
- if json_object.is_a?(Array)
826
- iterator = ResultIterator.new
827
- add_nested_body_to_iterator(iterator, json_object)
828
- result << iterator
829
- else
837
+ if json_object.is_a? Hash
830
838
  result << new_object(json_object, @overridden_name)
839
+ else
840
+ result << set_corresponding_value(json_object)
831
841
  end
832
842
  end
833
843
  end
@@ -838,6 +848,7 @@ module Flexirest
838
848
  end
839
849
 
840
850
  class RequestException < StandardError ; end
851
+ class InvalidArgumentsException < StandardError ; end
841
852
 
842
853
  class InvalidRequestException < RequestException ; end
843
854
  class MissingParametersException < RequestException ; end
@@ -851,18 +862,19 @@ module Flexirest
851
862
  end
852
863
 
853
864
  class HTTPException < RequestException
854
- attr_accessor :status, :result, :request_url
865
+ attr_accessor :status, :result, :request_url, :body, :raw_response
855
866
  def initialize(options)
856
867
  @status = options[:status]
857
868
  @result = options[:result]
858
869
  @request_url = options[:url]
859
- @raw_response = options[:raw_response]
870
+ @body = options[:raw_response]
860
871
  @method = options[:method]
861
872
  end
873
+ alias_method :body, :raw_response
862
874
 
863
875
  def message
864
876
  method = @method.try(:upcase)
865
- "The #{method} to '#{@request_url}' returned a #{@status} status, which raised a #{self.class.to_s} with a body of: #{@raw_response}"
877
+ "The #{method} to '#{@request_url}' returned a #{@status} status, which raised a #{self.class.to_s} with a body of: #{@body}"
866
878
  end
867
879
 
868
880
  def to_s
@@ -15,6 +15,10 @@ module Flexirest
15
15
  @items << item
16
16
  end
17
17
 
18
+ def _clean!
19
+ @items.map(&:_clean!)
20
+ end
21
+
18
22
  def size
19
23
  @items.size
20
24
  end
@@ -1,3 +1,3 @@
1
1
  module Flexirest
2
- VERSION = "1.9.11"
2
+ VERSION = "1.9.16"
3
3
  end
@@ -126,6 +126,13 @@ describe Flexirest::BaseWithoutValidation do
126
126
  expect(client).to be_dirty
127
127
  end
128
128
 
129
+ it "should not mark attributes that don't change as dirty" do
130
+ client = EmptyExample.new(test: "Foo")
131
+ client._clean!
132
+ client.test = client.test
133
+ expect(client).to_not be_dirty
134
+ end
135
+
129
136
  it "should store attribute set using []= array notation and mark them as dirty" do
130
137
  client = EmptyExample.new()
131
138
  client["test"] = "Something"
@@ -171,6 +178,13 @@ describe Flexirest::BaseWithoutValidation do
171
178
  expect(client).to be_dirty
172
179
  end
173
180
 
181
+ it "should not mark a freshly retrieved object as changed" do
182
+ 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\"}")))
183
+ client = AlteringClientExample.find(id: 1)
184
+ expect(client).to_not be_changed
185
+ expect(client.changes).to eq({})
186
+ end
187
+
174
188
  it 'should respond_to? attributes defined in the response' do
175
189
  client = EmptyExample.new(hello: "World")
176
190
  expect(client.respond_to?(:hello)).to be_truthy
@@ -184,6 +184,18 @@ describe Flexirest::Caching do
184
184
  expect(ret.first_name).to eq("Johnny")
185
185
  end
186
186
 
187
+ it "cache read objects shouldn't be marked as changed" do
188
+ cached_response = Flexirest::CachedResponse.new(
189
+ status:200,
190
+ result:@cached_object,
191
+ expires:Time.now + 30)
192
+ expect_any_instance_of(CachingExampleCacheStore5).to receive(:read).once.with("Person:/").and_return(Marshal.dump(cached_response))
193
+ expect_any_instance_of(Flexirest::Connection).not_to receive(:get)
194
+ ret = Person.all
195
+ expect(ret).to_not be_changed
196
+ expect(ret.changes).to eq({})
197
+ end
198
+
187
199
  it "should read from the cache store and restore to the same object" do
188
200
  cached_response = Flexirest::CachedResponse.new(
189
201
  status:200,
@@ -312,28 +312,46 @@ describe 'JSON API' do
312
312
  expect(subject.find_all).to be_an_instance_of(Flexirest::ResultIterator)
313
313
  end
314
314
 
315
- context 'when response has "errors" key and no "data" key' do
316
- let(:headers) { { "Content-Type" => "application/vnd.api+json" } }
317
- let(:response_body) do
318
- {
319
- errors: [
320
- {
321
- title: "Record not found",
322
- detail: "The record identified by 123456 could not be found",
323
- code: "not_found",
324
- status: "404",
325
- }
326
- ]
327
- }
328
- end
315
+ describe 'error responses' do
316
+ subject(:make_request) { JsonAPIExample::Article.real_find(123) }
329
317
 
330
- it 'should raise a Flexirest error' do
318
+ before do
319
+ headers = { "Content-Type" => "application/vnd.api+json" }
331
320
  expect_any_instance_of(Flexirest::Connection).
332
321
  to receive(:get).with("/articles/123", an_instance_of(Hash)).
333
322
  and_return(::FaradayResponseMock.new(OpenStruct.new(body: response_body.to_json, response_headers: headers, status: 404)))
323
+ end
324
+
325
+ context 'when no "data" key is present alongside the "errors" key' do
326
+ let(:response_body) do
327
+ {
328
+ errors: [
329
+ { detail: "The record identified by 123456 could not be found", }
330
+ ]
331
+ }
332
+ end
333
+
334
+ it 'should raise the relevant Flexirest error' do
335
+ expect(-> { make_request }).to raise_error(Flexirest::HTTPNotFoundClientException) do |exception|
336
+ expect(exception.result.first.detail).to eq("The record identified by 123456 could not be found")
337
+ end
338
+ end
339
+ end
340
+
341
+ context 'when a "data" key is present alongside the "errors" key (although this is forbidden by the spec)' do
342
+ let(:response_body) do
343
+ {
344
+ errors: [
345
+ { detail: "The record identified by 123456 could not be found", }
346
+ ],
347
+ data: {}
348
+ }
349
+ end
334
350
 
335
- expect(-> { JsonAPIExample::Article.real_find(123) }).to raise_error(Flexirest::HTTPNotFoundClientException) do |exception|
336
- expect(exception.result.first.title).to eq("Record not found")
351
+ it 'should ignore the "data" key and raise the relevant Flexirest error' do
352
+ expect(-> { make_request }).to raise_error(Flexirest::HTTPNotFoundClientException) do |exception|
353
+ expect(exception.result.first.detail).to eq("The record identified by 123456 could not be found")
354
+ end
337
355
  end
338
356
  end
339
357
  end
@@ -31,6 +31,8 @@ describe Flexirest::Request do
31
31
  get :flat, "/", params_encoder: :flat
32
32
  get :array, "/johnny", array: [:likes, :dislikes]
33
33
  get :babies, "/babies", has_many: {children: ExampleOtherClient}
34
+ get :basket, "/basket", array: [:options]
35
+ get :dates, "/dates", array: [:dates]
34
36
  get :single_association, "/single", has_one: {single: ExampleSingleClient}, has_many: {children: ExampleOtherClient}
35
37
  get :headers, "/headers"
36
38
  get :cancel_callback, "/cancel-callback"
@@ -38,6 +40,7 @@ describe Flexirest::Request do
38
40
  put :headers_json, "/headers_json", request_body_type: :json
39
41
  get :find, "/:id", required: [:id]
40
42
  get :find_cat, "/:id/cat"
43
+ get :fruits, "/fruits"
41
44
  get :change, "/change"
42
45
  get :plain, "/plain/:id", plain: true
43
46
  post :create, "/create", rubify_names: true
@@ -46,11 +49,14 @@ describe Flexirest::Request do
46
49
  put :update, "/put/:id"
47
50
  put :wrapped, "/put/:id", wrap_root: "example"
48
51
  put :conversion, "/put/:id", parse_fields: [:converted]
52
+ put :conversion_child, "/put/:id", parse_fields: [:converted_child]
49
53
  delete :remove, "/remove/:id"
50
54
  delete :remove_body, "/remove/:id", send_delete_body: true
51
55
  get :hal, "/hal", fake:"{\"_links\":{\"child\": {\"href\": \"/child/1\"}, \"other\": {\"href\": \"/other/1\"}, \"cars\":[{\"href\": \"/car/1\", \"name\":\"car1\"}, {\"href\": \"/car/2\", \"name\":\"car2\"}, {\"href\": \"/car/not-embed\", \"name\":\"car_not_embed\"} ], \"lazy\": {\"href\": \"/lazy/load\"}, \"invalid\": [{\"href\": \"/invalid/1\"}]}, \"_embedded\":{\"other\":{\"name\":\"Jane\"},\"child\":{\"name\":\"Billy\"}, \"cars\":[{\"_links\": {\"self\": {\"href\": \"/car/1\"} }, \"make\": \"Bugatti\", \"model\": \"Veyron\"}, {\"_links\": {\"self\": {\"href\": \"/car/2\"} }, \"make\": \"Ferrari\", \"model\": \"F458 Italia\"} ], \"invalid\": [{\"present\":true, \"_links\": {} } ] } }", has_many:{other:ExampleOtherClient}
52
- get :fake, "/fake", fake:"{\"result\":true, \"list\":[1,2,3,{\"test\":true}], \"child\":{\"grandchild\":{\"test\":true}}}"
53
- get :fake_proc, "/fake", fake:->(request) { "{\"result\":#{request.get_params[:id]}}" }
56
+ get :fake_object, "/fake", fake:"{\"result\":true, \"list\":[1,2,3,{\"test\":true}], \"child\":{\"grandchild\":{\"test\":true}}}"
57
+ get :fake_proc_object, "/fake", fake:->(request) { "{\"result\":#{request.get_params[:id]}}" }
58
+ get :fake_array, "/fake", fake:"[1,2,3,{\"test\":true},null]"
59
+ get :fake_proc_array, "/fake", fake:->(request) { "[{\"result\":#{request.get_params[:id]}},null]" }
54
60
  get :defaults, "/defaults", defaults:{overwrite:"no", persist:"yes"}
55
61
  get :requires, "/requires", requires:[:name, :age]
56
62
  patch :only_changed_1, "/changed1", only_changed: true
@@ -143,7 +149,8 @@ describe Flexirest::Request do
143
149
  class LazyLoadedExampleClient < ExampleClient
144
150
  base_url "http://www.example.com"
145
151
  lazy_load!
146
- get :fake, "/fake", fake:"{\"result\":true, \"list\":[1,2,3,{\"test\":true}], \"child\":{\"grandchild\":{\"test\":true}}}"
152
+ get :fake_object, "/fake", fake:"{\"result\":true, \"list\":[1,2,3,{\"test\":true}], \"child\":{\"grandchild\":{\"test\":true}}}"
153
+ get :fake_array, "/fake", fake:"[1,2,3,{\"test\":true},{\"child\":{\"grandchild\":{\"test\":true}}}]"
147
154
  get :lazy_test, "/does-not-matter", fake:"{\"people\":[\"http://www.example.com/some/url\"]}", lazy: [:people]
148
155
  end
149
156
 
@@ -190,7 +197,8 @@ describe Flexirest::Request do
190
197
  class WhitelistedDateClient < Flexirest::Base
191
198
  base_url "http://www.example.com"
192
199
  put :conversion, "/put/:id"
193
- parse_date :converted
200
+ put :conversion_child, "/put/:id"
201
+ parse_date :converted, :converted_child
194
202
  end
195
203
 
196
204
  allow_any_instance_of(Flexirest::Request).to receive(:read_cached_response)
@@ -336,7 +344,7 @@ describe Flexirest::Request do
336
344
  end
337
345
 
338
346
  it "should pass URL-encode URL parameters" do
339
- expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/foo%20bar", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"result":true}', response_headers:{})))
347
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/foo+bar", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"result":true}', response_headers:{})))
340
348
  ExampleClient.find id:"foo bar"
341
349
  end
342
350
 
@@ -511,33 +519,84 @@ describe Flexirest::Request do
511
519
  end
512
520
  end
513
521
 
514
- it "should only convert date times in JSON if specified" do
522
+ it "should only convert date times in JSON if specified when converted is root property" do
515
523
  expect_any_instance_of(Flexirest::Connection).to receive(:put).with("/put/1234", "debug=true", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"converted\":\"2012-03-04T01:02:03Z\", \"not_converted\":\"2012-03-04T01:02:03Z\"}", response_headers:{})))
516
524
  object = ExampleClient.conversion id:1234, debug:true
517
525
  expect(object.converted).to be_an_instance_of(DateTime)
518
526
  expect(object.not_converted).to be_an_instance_of(String)
519
527
  end
520
528
 
521
- it "should convert date times in JSON if whitelisted" do
529
+ it "should only convert date times in JSON if specified when converted is child property" do
530
+ expect_any_instance_of(Flexirest::Connection).to receive(:put).with("/put/1234", "debug=true", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"not_converted\":\"2012-03-04T01:02:03Z\", \"child\":{\"converted_child\":\"2012-03-04T01:02:03Z\"}}", response_headers:{})))
531
+ object = ExampleClient.conversion_child id:1234, debug:true
532
+ expect(object.child.converted_child).to be_an_instance_of(DateTime)
533
+ expect(object.not_converted).to be_an_instance_of(String)
534
+ end
535
+
536
+ it "should convert date times in JSON if root property is whitelisted" do
522
537
  expect_any_instance_of(Flexirest::Connection).to receive(:put).with("/put/1234", "debug=true", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"converted\":\"2012-03-04T01:02:03Z\", \"not_converted\":\"2012-03-04T01:02:03Z\"}", response_headers:{})))
523
538
  object = WhitelistedDateClient.conversion id:1234, debug:true
524
539
  expect(object.converted).to be_an_instance_of(DateTime)
525
540
  expect(object.not_converted).to be_an_instance_of(String)
526
541
  end
527
542
 
528
- it "should parse JSON and return a nice object for faked responses" do
529
- object = ExampleClient.fake id:1234, debug:true
543
+ it "should convert date times in JSON if child property is whitelisted" do
544
+ expect_any_instance_of(Flexirest::Connection).to receive(:put).with("/put/1234", "debug=true", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"not_converted\":\"2012-03-04T01:02:03Z\", \"child\":{\"converted_child\":\"2012-03-04T01:02:03Z\"}}", response_headers:{})))
545
+ object = WhitelistedDateClient.conversion_child id:1234, debug:true
546
+ expect(object.child.converted_child).to be_an_instance_of(DateTime)
547
+ expect(object.not_converted).to be_an_instance_of(String)
548
+ end
549
+
550
+ it "should convert date times in JSON from a result iterator response" do
551
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/dates", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"[\"2012-03-04T01:02:03Z\", \"2012-03-04T01:02:03Z\"]", response_headers:{})))
552
+ object = ExampleClient.dates
553
+ expect(object).to be_a(Flexirest::ResultIterator)
554
+ expect(object.items).to be_a(Array)
555
+ expect(object.first).to be_an_instance_of(DateTime)
556
+ end
557
+
558
+ it "should convert date times in JSON even in a pure array" do
559
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/dates", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"dates\":[\"2012-03-04T01:02:03Z\", \"2012-03-04T01:02:03Z\"]}", response_headers:{})))
560
+ object = ExampleClient.dates
561
+ expect(object).to be_a(ExampleClient)
562
+ expect(object.dates).to be_a(Array)
563
+ expect(object.dates.first).to be_an_instance_of(DateTime)
564
+ end
565
+
566
+ it "should parse JSON object and return a nice object for faked responses" do
567
+ object = ExampleClient.fake_object id:1234, debug:true
530
568
  expect(object.result).to eq(true)
531
569
  expect(object.list.first).to eq(1)
532
570
  expect(object.list.last.test).to eq(true)
533
571
  expect(object.child.grandchild.test).to eq(true)
534
572
  end
535
573
 
536
- it "should parse JSON from a fake response generated by a proc" do
537
- object = ExampleClient.fake_proc id:1234
574
+ it "should parse JSON object from a fake response generated by a proc" do
575
+ object = ExampleClient.fake_proc_object id:1234
538
576
  expect(object.result).to eq(1234)
539
577
  end
540
578
 
579
+ it "should parse JSON array and return a nice result iterator for faked responses" do
580
+ object = ExampleClient.fake_array debug:true
581
+ expect(object).to be_instance_of(Flexirest::ResultIterator)
582
+ expect(object.size).to eq(5)
583
+ expect(object.first).to eq(1)
584
+ expect(object.last).to eq(nil)
585
+ expect(object[3]).to be_instance_of(ExampleClient)
586
+ expect(object[3].test).to eq(true)
587
+ expect(object._status).to eq(200)
588
+ end
589
+
590
+ it "should parse JSON array from a fake response generated by a proc" do
591
+ object = ExampleClient.fake_proc_array id:1234
592
+ expect(object).to be_instance_of(Flexirest::ResultIterator)
593
+ expect(object.size).to eq(2)
594
+ expect(object.first).to be_instance_of(ExampleClient)
595
+ expect(object.first.result).to eq(1234)
596
+ expect(object.last).to eq(nil)
597
+ expect(object._status).to eq(200)
598
+ end
599
+
541
600
  it "should not parse JSON from a plain request" do
542
601
  response_body = "This is another non-JSON string"
543
602
  expect_any_instance_of(Flexirest::Connection).to receive(:get).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:response_body)))
@@ -570,19 +629,36 @@ describe Flexirest::Request do
570
629
  expect(ExampleClient.all).to be_truthy
571
630
  end
572
631
 
573
- it "should return a lazy loader object if lazy loading is enabled" do
574
- object = LazyLoadedExampleClient.fake id:1234, debug:true
632
+ it "should return a lazy loader object if lazy loading is enabled for JSON object" do
633
+ object = LazyLoadedExampleClient.fake_object id:1234, debug:true
575
634
  expect(object).to be_an_instance_of(Flexirest::LazyLoader)
576
635
  end
577
636
 
578
- it "should proxy through nice object for lazy loaded responses" do
579
- object = LazyLoadedExampleClient.fake id:1234, debug:true
580
- expect(object.result).to eq(true)
637
+ it "should proxy through nice object for lazy loaded responses from JSON object" do
638
+ object = LazyLoadedExampleClient.fake_object id:1234, debug:true
639
+ expect(object.instance_variable_get(:@result)).to be(nil)
640
+ expect(object.result).to eq(true) # method call the attribute received in response and never the instance attribute of the LazyLoader class
641
+ expect(object.instance_variable_get(:@result)).to be_a(LazyLoadedExampleClient)
581
642
  expect(object.list.first).to eq(1)
582
643
  expect(object.list.last.test).to eq(true)
583
644
  expect(object.child.grandchild.test).to eq(true)
584
645
  end
585
646
 
647
+ it "should return a lazy loader object if lazy loading is enabled for JSON array" do
648
+ object = LazyLoadedExampleClient.fake_array debug:true
649
+ expect(object).to be_an_instance_of(Flexirest::LazyLoader)
650
+ end
651
+
652
+ it "should proxy through nice result iterator for lazy loaded responses from JSON array" do
653
+ object = LazyLoadedExampleClient.fake_array debug:true
654
+ expect(object.instance_variable_get(:@result)).to be(nil)
655
+ expect(object.items).to be_a(Array)
656
+ expect(object.instance_variable_get(:@result)).to be_a(Flexirest::ResultIterator)
657
+ expect(object.first).to eq(1)
658
+ expect(object[3].test).to eq(true)
659
+ expect(object.last.child.grandchild.test).to eq(true)
660
+ end
661
+
586
662
  it "should return a LazyAssociationLoader for lazy loaded properties" do
587
663
  object = LazyLoadedExampleClient.lazy_test
588
664
  expect(object.people.size).to eq(1)
@@ -592,7 +668,7 @@ describe Flexirest::Request do
592
668
  it "should log faked responses" do
593
669
  allow(Flexirest::Logger).to receive(:debug)
594
670
  expect(Flexirest::Logger).to receive(:debug).with(/Faked response found/)
595
- ExampleClient.fake id:1234, debug:true
671
+ ExampleClient.fake_object id:1234, debug:true
596
672
  end
597
673
 
598
674
  it "should parse an array within JSON to be a result iterator" do
@@ -628,6 +704,51 @@ describe Flexirest::Request do
628
704
  #TODO
629
705
  end
630
706
 
707
+ it "should parse an attribute to be an array if attribute included in a nested array" do
708
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/johnny", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"Johnny\", \"nested_array\":[[{\"likes\":[\"donuts\", \"bacon\"], \"dislikes\":[\"politicians\", \"lawyers\", \"taxes\"]}]]}", status:200, response_headers:{})))
709
+ object = ExampleClient.array
710
+ nested_array = object.nested_array[0][0]
711
+ expect(nested_array.likes).to be_instance_of(Array)
712
+ expect(nested_array.likes.size).to eq(2)
713
+ expect(nested_array.likes[0]).to eq("donuts")
714
+ expect(nested_array.likes[1]).to eq("bacon")
715
+ expect(nested_array.dislikes).to be_instance_of(Array)
716
+ expect(nested_array.dislikes.size).to eq(3)
717
+ expect(nested_array.dislikes[0]).to eq("politicians")
718
+ expect(nested_array.dislikes[1]).to eq("lawyers")
719
+ expect(nested_array.dislikes[2]).to eq("taxes")
720
+ #TODO - Pasted from the Test above
721
+ end
722
+
723
+ it "should parse an attribute to be either a result iterator or an array and containing simple values like integers" do
724
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/basket", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"products\":[101, 55, 37], \"options\":[854, 225, 772]}", status:200, response_headers:{})))
725
+ object = ExampleClient.basket
726
+ expect(object).to be_instance_of(ExampleClient)
727
+ expect(object.products).to be_a(Flexirest::ResultIterator)
728
+ expect(object.products.size).to eq(3)
729
+ expect(object.products.first).to eq(101)
730
+ expect(object.products[1]).to eq(55)
731
+ expect(object.products.last).to eq(37)
732
+ expect(object.options).to be_a(Array)
733
+ expect(object.options.size).to eq(3)
734
+ expect(object.options.first).to eq(854)
735
+ expect(object.options[1]).to eq(225)
736
+ expect(object.options.last).to eq(772)
737
+ expect(object._status).to eq(200)
738
+ #TODO - Pasted from the Test above
739
+ end
740
+
741
+ it "should parse the response to be a result iterator and containing simple values like strings" do
742
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/fruits", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"[\"apple\", \"banana\", \"pear\", \"watermelon\"]", status:200, response_headers:{})))
743
+ object = ExampleClient.fruits
744
+ expect(object).to be_instance_of(Flexirest::ResultIterator)
745
+ expect(object.first).to eq('apple')
746
+ expect(object[1]).to eq('banana')
747
+ expect(object.last).to eq('watermelon')
748
+ expect(object._status).to eq(200)
749
+ #TODO - Pasted from the Test above
750
+ end
751
+
631
752
  it "should only send changed attributes if only_changed:true" do
632
753
  expect_any_instance_of(Flexirest::Connection).to receive(:patch).with("/changed1", "debug=true", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"[{\"first_name\":\"Johnny\"}, {\"first_name\":\"Billy\"}, {\"debug\":\"true\"}]", status:200, response_headers:{})))
633
754
  object = ExampleClient.new
@@ -66,12 +66,12 @@ class FaradayResponseMock < ::Flexirest::FaradayResponseProxy
66
66
  @finished = false
67
67
  end
68
68
 
69
- def on_complete
69
+ def on_complete(&block)
70
70
  if @auto_resolve
71
71
  @finished = true
72
72
  yield(@response)
73
73
  else
74
- @callback = Proc.new
74
+ @callback = block
75
75
  end
76
76
  end
77
77
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flexirest
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.11
4
+ version: 1.9.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Jeffries
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-13 00:00:00.000000000 Z
11
+ date: 2020-07-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -129,6 +129,9 @@ dependencies:
129
129
  - - ">="
130
130
  - !ruby/object:Gem::Version
131
131
  version: 1.3.1
132
+ - - "<"
133
+ - !ruby/object:Gem::Version
134
+ version: '2.4'
132
135
  type: :development
133
136
  prerelease: false
134
137
  version_requirements: !ruby/object:Gem::Requirement
@@ -136,6 +139,9 @@ dependencies:
136
139
  - - ">="
137
140
  - !ruby/object:Gem::Version
138
141
  version: 1.3.1
142
+ - - "<"
143
+ - !ruby/object:Gem::Version
144
+ version: '2.4'
139
145
  - !ruby/object:Gem::Dependency
140
146
  name: typhoeus
141
147
  requirement: !ruby/object:Gem::Requirement
@@ -364,7 +370,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
364
370
  - !ruby/object:Gem::Version
365
371
  version: '0'
366
372
  requirements: []
367
- rubygems_version: 3.0.6
373
+ rubygems_version: 3.1.2
368
374
  signing_key:
369
375
  specification_version: 4
370
376
  summary: This gem is for accessing REST services in a flexible way. ActiveResource