flexirest 1.13.0 → 1.13.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4953df31ca4ca1d055e7a80ce2cd7a0df8e53cc4b67544b80d1a91ee2aa366a4
4
- data.tar.gz: 0f500061fd78240f0f2c2d8c19934d5c6c5f777f32de82c8bc2330415fd6c768
3
+ metadata.gz: d8b2caeda24cf6df9d105be2899483c18887dfa8903e54e0142d05f4be406ba4
4
+ data.tar.gz: 4072891a3796efbfa0323977dc922c57acbf77cce41f94ed50f95a63911dc70e
5
5
  SHA512:
6
- metadata.gz: 6c45c501ef89cc34a66a66ca585088e7af146348b2c42ffd0c36fe4559a957ae4bc5f579b207b238c5d0e5a0625bb1000f937a144a2107ce3eff79272d07286a
7
- data.tar.gz: 9ea32ee11733ee70ea539d37f094fb8e9f730c82c4a14f84e5322102ab380af0c5dc12a3ac258968e8ec9a6ebd6b84ae780e54ec9a518744888c6ee62be40e7f
6
+ metadata.gz: f07a2703870fbcd9c4d7b1862ccd95b8cf38a6e67c0caeca51706361b1bd871c4b49698fb69719747890e18adf22f1b809d8c3cf8e13d977758f88780d23db24
7
+ data.tar.gz: 3d4051535dda8ecac777686b506008cdb104aa832cd1fa4ac668ae01c1d129d53f92ac863ca69e3abebc3da55f6275a19ab259d3bdabf2bf477f9bf4059f73c4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.13.2
4
+
5
+ Bugfix:
6
+
7
+ - multi_json 1.21 renamed its module to `MultiJSON` and replaced `load`/`dump` with `parse`/`generate`, deprecating the old names with one-time warnings that appeared in host applications' test output. Flexirest now calls the current API (and `symbolize_names` rather than the deprecated `symbolize_keys`), so it requires multi_json >= 1.21
8
+
9
+ ## 1.13.1
10
+
11
+ Bugfix:
12
+
13
+ - Ruby 4.0 removed `ostruct` from the standard library, and Flexirest builds OpenStructs internally, so it is now required explicitly and declared as a runtime dependency rather than relying on the host application to provide it
14
+ - Proxy routes with parameters (e.g. `get "/things/:id"`) no longer mutate the string they are given, so declaring one with a frozen string literal works. Previously `ProxyBase.add_mapping` built the regex with `gsub!` on the caller's string, which warns under Ruby 3.4+ chilled string literals and raises `FrozenError` under `--enable=frozen-string-literal`
15
+ - A forced URL is now duplicated before `before_request` callbacks run, so the documented `request.url.gsub!` pattern works when the URL passed to `_request` is a frozen string literal
16
+
3
17
  ## 1.13.0
4
18
 
5
19
  Bugfix:
data/Rakefile CHANGED
@@ -1,3 +1,8 @@
1
+ # IMPORTANT: bundler/setup so a bare `rake spec` runs the same suite as CI's
2
+ # `bundle exec rake`. Several specs are defined conditionally on
3
+ # Gem.loaded_specs["api-auth"], which only lists *activated* gems - without Bundler
4
+ # those guards read false and the examples silently vanish rather than fail.
5
+ require "bundler/setup"
1
6
  require "bundler/gem_tasks"
2
7
  require 'rspec/core/rake_task'
3
8
  RSpec::Core::RakeTask.new('spec')
data/flexirest.gemspec CHANGED
@@ -38,8 +38,11 @@ Gem::Specification.new do |spec|
38
38
  spec.add_development_dependency 'rest-client'
39
39
  spec.add_development_dependency 'timecop'
40
40
 
41
+ # A default gem up to Ruby 3.4 and dropped from the stdlib in 4.0, so declare it
42
+ # rather than assume the host application provides it.
43
+ spec.add_runtime_dependency "ostruct"
41
44
  spec.add_runtime_dependency "mime-types"
42
- spec.add_runtime_dependency "multi_json"
45
+ spec.add_runtime_dependency "multi_json", ">= 1.21"
43
46
  spec.add_runtime_dependency "crack"
44
47
  spec.add_runtime_dependency "faraday", "~> 2.7"
45
48
 
@@ -87,7 +87,7 @@ module Flexirest
87
87
 
88
88
  def ensure_lazy_loaded
89
89
  if @object.nil?
90
- method = MultiJson.load(MultiJson.dump(@request.method),:symbolize_keys => true)
90
+ method = MultiJSON.parse(MultiJSON.generate(@request.method), symbolize_names: true)
91
91
  method[:method] = :get
92
92
  method[:options][:url] = @url
93
93
  method[:options][:overridden_name] = @options[:overridden_name]
@@ -39,11 +39,14 @@ module Flexirest
39
39
  @mappings ||= []
40
40
 
41
41
  if match.is_a?(String) && (param_keys = match.scan(/:\w+/)) && param_keys.any?
42
- param_keys.each do |key|
43
- match.gsub!(key, "([^/]+)")
44
- end
42
+ # Build the pattern from a copy rather than mutating the caller's string. A
43
+ # route is almost always declared with a string literal, and under Ruby 3.4+
44
+ # those are chilled (mutating one warns); with frozen string literals enabled
45
+ # - Ruby 4.0's --enable=frozen-string-literal, or a magic comment - the same
46
+ # mutation raises FrozenError and the proxy class fails to load.
47
+ pattern = param_keys.reduce(match.dup) { |acc, key| acc.gsub(key, "([^/]+)") }
45
48
  param_keys = param_keys.map {|k| k.gsub(":", "").to_sym}
46
- match = Regexp.new(match)
49
+ match = Regexp.new(pattern)
47
50
  end
48
51
 
49
52
  @mappings << OpenStruct.new(http_method:method_type, match:match, block:block, param_keys:param_keys)
@@ -96,7 +99,7 @@ module Flexirest
96
99
  if incoming_content_type && incoming_content_type["xml"]
97
100
  result.body = yield Crack::XML.parse(result.body)
98
101
  else
99
- result.body = yield MultiJson.load(result.body)
102
+ result.body = yield MultiJSON.parse(result.body)
100
103
  end
101
104
  end
102
105
  result
@@ -426,7 +426,10 @@ module Flexirest
426
426
  def prepare_url
427
427
  missing = []
428
428
  if @forced_url && @forced_url.present?
429
- @url = @forced_url
429
+ # dup for the same reason as the branch below: before_request callbacks are
430
+ # documented to rewrite the URL in place with gsub!, and a forced URL is
431
+ # usually a string literal from the caller.
432
+ @url = @forced_url.dup
430
433
  else
431
434
  @url = @method[:url].dup
432
435
  matches = @url.scan(/(:[a-z_-]+)/)
@@ -869,9 +872,9 @@ module Flexirest
869
872
  body = @response.body
870
873
  elsif is_json_response?
871
874
  begin
872
- body = @response.body.blank? ? {} : MultiJson.load(@response.body)
875
+ body = @response.body.blank? ? {} : MultiJSON.parse(@response.body)
873
876
  body = {} if body.nil?
874
- rescue MultiJson::ParseError
877
+ rescue MultiJSON::ParseError
875
878
  raise ResponseParseException.new(status:@response.status, body:@response.body, headers:@response.headers)
876
879
  end
877
880
 
@@ -1,3 +1,3 @@
1
1
  module Flexirest
2
- VERSION = "1.13.0"
2
+ VERSION = "1.13.2"
3
3
  end
data/lib/flexirest.rb CHANGED
@@ -1,3 +1,7 @@
1
+ # ostruct stopped being a default gem in Ruby 4.0, and Flexirest builds OpenStructs in
2
+ # Request and ProxyBase, so require it explicitly rather than relying on the host
3
+ # application having pulled it in.
4
+ require "ostruct"
1
5
  require 'active_support/all'
2
6
  require "flexirest/version"
3
7
  require "flexirest/attribute_parsing"
@@ -520,11 +520,11 @@ describe Flexirest::BaseWithoutValidation do
520
520
  let(:location) { EmptyExample.new(place:"Room 1408") }
521
521
  let(:lazy) { Laz }
522
522
  let(:object) { EmptyExample.new(name:"Programming 101", location:location, students:[student1, student2]) }
523
- let(:json_parsed_object) { MultiJson.load(object.to_json) }
523
+ let(:json_parsed_object) { MultiJSON.parse(object.to_json) }
524
524
 
525
525
  it "should be able to export to valid json" do
526
526
  expect(object.to_json).to_not be_blank
527
- expect{MultiJson.load(object.to_json)}.to_not raise_error
527
+ expect{MultiJSON.parse(object.to_json)}.to_not raise_error
528
528
  end
529
529
 
530
530
  it "should not be using Object's #to_json method" do
@@ -42,7 +42,11 @@ class SubClassedCallbacksExample < CallbacksExample
42
42
  end
43
43
 
44
44
  describe Flexirest::Callbacks do
45
- let(:request) { OpenStruct.new(get_params:{}, post_params:{}, url:"http://www.example.com", headers:Flexirest::HeadersList.new) }
45
+ # The unary + matters: these examples exercise callbacks rewriting the URL in place
46
+ # with gsub!, which is the documented API (docs/using-callbacks.md). A real request
47
+ # hands callbacks a mutable String, so the double has to as well - a frozen literal
48
+ # here would fail under frozen string literals for a reason the library doesn't have.
49
+ let(:request) { OpenStruct.new(get_params:{}, post_params:{}, url:+"http://www.example.com", headers:Flexirest::HeadersList.new) }
46
50
  let(:response) { OpenStruct.new(body:"") }
47
51
 
48
52
  it "should call through to adjust the parameters" do
@@ -5,6 +5,27 @@ describe Flexirest::ConnectionManager do
5
5
  Flexirest::ConnectionManager.reset!
6
6
  end
7
7
 
8
+ # IMPORTANT: The two typhoeus examples below set a *global* adapter. Reset it in an
9
+ # after hook rather than on the last line of each example: if the example raises
10
+ # first that line never runs, every later spec in the suite inherits
11
+ # adapter = :typhoeus, and they all fail with ":typhoeus is not registered on
12
+ # Faraday::Adapter". Specs run in random order, so that turns one local failure into
13
+ # a suite-wide cascade whose size depends on the seed.
14
+ after(:each) do
15
+ Flexirest::Base._reset_configuration!
16
+ end
17
+
18
+ # Gem.loaded_specs only lists gems that have been *activated*, not every gem in the
19
+ # bundle, so it is not a reliable way to ask whether faraday-typhoeus is installed -
20
+ # when it reads as absent the require is skipped and :typhoeus is never registered.
21
+ # Just attempt the require and let the older typhoeus-provided adapter be the
22
+ # fallback.
23
+ def require_typhoeus_adapter
24
+ require 'faraday/typhoeus'
25
+ rescue LoadError
26
+ require 'typhoeus/adapters/faraday'
27
+ end
28
+
8
29
  it "should have a get_connection method" do
9
30
  expect(Flexirest::ConnectionManager).to respond_to("get_connection")
10
31
  end
@@ -35,19 +56,17 @@ describe Flexirest::ConnectionManager do
35
56
  end
36
57
 
37
58
  it "should call 'in_parallel' for a session and yield procedure inside that block" do
38
- require 'faraday/typhoeus' if Gem.loaded_specs["faraday-typhoeus"].present?
59
+ require_typhoeus_adapter
39
60
  Flexirest::Base.adapter = :typhoeus
40
61
  Flexirest::ConnectionManager.get_connection("http://www.example.com").session
41
62
  expect { |b| Flexirest::ConnectionManager.in_parallel("http://www.example.com", &b)}.to yield_control
42
- Flexirest::Base._reset_configuration!
43
63
  end
44
64
 
45
65
  it "should raise Flexirest::MissingOptionalLibraryError if Typhoeus isn't available" do
46
- require 'faraday/typhoeus' if Gem.loaded_specs["faraday-typhoeus"].present?
66
+ require_typhoeus_adapter
47
67
  Flexirest::Base.adapter = :typhoeus
48
68
  Flexirest::ConnectionManager.get_connection("http://www.example.com").session
49
69
  expect(Flexirest::ConnectionManager).to receive(:require).and_raise(LoadError)
50
70
  expect { Flexirest::ConnectionManager.in_parallel("http://www.example.com")}.to raise_error(Flexirest::MissingOptionalLibraryError)
51
- Flexirest::Base._reset_configuration!
52
71
  end
53
72
  end
@@ -479,7 +479,7 @@ describe 'JSON API' do
479
479
 
480
480
  it 'should be able to call #.create on class' do
481
481
  expect_any_instance_of(Flexirest::Connection).to receive(:post) { |_, path, data|
482
- hash = MultiJson.load(data)
482
+ hash = MultiJSON.parse(data)
483
483
  expect(path).to eq('/articles')
484
484
  expect(hash['data']).to_not be_nil
485
485
  expect(hash['data']['id']).to be_nil
@@ -490,7 +490,7 @@ describe 'JSON API' do
490
490
 
491
491
  it 'should be able to call #.create with params on class' do
492
492
  expect_any_instance_of(Flexirest::Connection).to receive(:post) { |_, path, data|
493
- hash = MultiJson.load(data)
493
+ hash = MultiJSON.parse(data)
494
494
  expect(path).to eq('/articles')
495
495
  expect(hash['data']).to_not be_nil
496
496
  expect(hash['data']['id']).to be_nil
@@ -505,7 +505,7 @@ describe 'JSON API' do
505
505
 
506
506
  it 'should perform a post request in proper json api format' do
507
507
  expect_any_instance_of(Flexirest::Connection).to receive(:post) { |_, path, data|
508
- hash = MultiJson.load(data)
508
+ hash = MultiJSON.parse(data)
509
509
  expect(path).to eq('/articles')
510
510
  expect(hash['data']).to_not be_nil
511
511
  expect(hash['data']['id']).to be_nil
@@ -533,7 +533,7 @@ describe 'JSON API' do
533
533
 
534
534
  it 'should perform a patch request in proper json api format' do
535
535
  expect_any_instance_of(Flexirest::Connection).to receive(:patch) { |_, path, data|
536
- hash = MultiJson.load(data)
536
+ hash = MultiJSON.parse(data)
537
537
  expect(path).to eq('/articles/1')
538
538
  expect(hash['data']).to_not be_nil
539
539
  expect(hash['data']['id']).to_not be_nil
@@ -554,7 +554,7 @@ describe 'JSON API' do
554
554
 
555
555
  it 'should have placed the right type value in the request' do
556
556
  expect_any_instance_of(Flexirest::Connection).to receive(:patch) { |_, _, data|
557
- hash = MultiJson.load(data)
557
+ hash = MultiJSON.parse(data)
558
558
  expect(hash['data']['type']).to eq(JsonAPIExample::ArticleAlias.alias_type.to_s)
559
559
  expect(hash['data']['relationships']['author']['data']['type']).to eq(JsonAPIExample::AuthorAlias.alias_type.to_s)
560
560
  }.and_return(::FaradayResponseMock.new(OpenStruct.new(body: '{}', response_headers: {})))
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe "multi_json API" do
4
+ before :each do
5
+ class MultiJsonChildExample < Flexirest::Base
6
+ base_url "http://www.example.com"
7
+
8
+ get :find, "/child/:id"
9
+ end
10
+
11
+ class MultiJsonExampleClient < Flexirest::Base
12
+ base_url "http://www.example.com"
13
+
14
+ get :find, "/find/:id"
15
+ get :parent, "/parent", lazy: { children: MultiJsonChildExample }, fake: "{\"children\": [\"http://www.example.com/child/1\"]}"
16
+ end
17
+ end
18
+
19
+ # multi_json warns once per process for each deprecated entry point, so a
20
+ # warning emitted by an earlier spec would make a later one look clean.
21
+ # Message expectations on the deprecated surface - the legacy MultiJson module
22
+ # (whose const_missing serves MultiJson::ParseError) and the load/dump aliases
23
+ # on MultiJSON itself - hold regardless of spec order.
24
+ def expect_no_deprecated_json_api
25
+ [MultiJson, MultiJSON].each do |target|
26
+ %i[load dump decode encode const_missing].each do |name|
27
+ expect(target).to_not receive(name) if target.respond_to?(name)
28
+ end
29
+ end
30
+ end
31
+
32
+ def response_for(body)
33
+ ::FaradayResponseMock.new(OpenStruct.new(status: 200, response_headers: { "Content-Type" => "application/json" }, body: body))
34
+ end
35
+
36
+ it "parses a response body" do
37
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with(any_args).and_return(response_for("{\"name\":\"Billy\"}"))
38
+ expect_no_deprecated_json_api
39
+
40
+ expect(MultiJsonExampleClient.find(id: 1).name).to eq("Billy")
41
+ end
42
+
43
+ it "reports a body it cannot parse" do
44
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with(any_args).and_return(response_for("{\"name\": Billy"))
45
+ expect_no_deprecated_json_api
46
+
47
+ expect { MultiJsonExampleClient.find(id: 1) }.to raise_error(Flexirest::ResponseParseException)
48
+ end
49
+
50
+ it "loads a lazy association" do
51
+ loader = MultiJsonExampleClient.parent.children.first
52
+ expect(loader).to be_an_instance_of(Flexirest::LazyAssociationLoader)
53
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with(any_args).and_return(response_for("{\"name\":\"Jane\"}"))
54
+ expect_no_deprecated_json_api
55
+
56
+ expect(loader.name).to eq("Jane")
57
+ end
58
+ end
@@ -265,6 +265,18 @@ describe Flexirest::Base do
265
265
  expect(paths.sort).to eq(expected.sort)
266
266
  end
267
267
 
268
+ it "builds a parameterised route without mutating the string it was given" do
269
+ route = "/frozen/:id/:name".freeze
270
+ proxy = Class.new(Flexirest::ProxyBase)
271
+
272
+ expect { proxy.get(route) { passthrough } }.to_not raise_error
273
+ expect(route).to eq("/frozen/:id/:name")
274
+
275
+ mapping = proxy.instance_variable_get(:@mappings).last
276
+ expect(mapping.param_keys).to eq(%i[id name])
277
+ expect("/frozen/12/john").to match(mapping.match)
278
+ end
279
+
268
280
  it "properly passes basic HTTP auth credentials" do
269
281
  host, credentials, url_path = 'www.example.com', 'user:pass', '/getAll?id=1'
270
282
  ProxyClientExample.base_url "http://#{credentials}@#{host}"
@@ -206,7 +206,7 @@ describe Flexirest::Request do
206
206
  class CallbackBodyExampleClient < ExampleClient
207
207
  base_url "http://www.example.com"
208
208
  before_request do |name, request|
209
- request.body = MultiJson.dump(request.post_params)
209
+ request.body = MultiJSON.generate(request.post_params)
210
210
  end
211
211
 
212
212
  post :save, "/save"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flexirest
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.13.0
4
+ version: 1.13.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Jeffries
@@ -192,7 +192,7 @@ dependencies:
192
192
  - !ruby/object:Gem::Version
193
193
  version: '0'
194
194
  - !ruby/object:Gem::Dependency
195
- name: mime-types
195
+ name: ostruct
196
196
  requirement: !ruby/object:Gem::Requirement
197
197
  requirements:
198
198
  - - ">="
@@ -206,7 +206,7 @@ dependencies:
206
206
  - !ruby/object:Gem::Version
207
207
  version: '0'
208
208
  - !ruby/object:Gem::Dependency
209
- name: multi_json
209
+ name: mime-types
210
210
  requirement: !ruby/object:Gem::Requirement
211
211
  requirements:
212
212
  - - ">="
@@ -219,6 +219,20 @@ dependencies:
219
219
  - - ">="
220
220
  - !ruby/object:Gem::Version
221
221
  version: '0'
222
+ - !ruby/object:Gem::Dependency
223
+ name: multi_json
224
+ requirement: !ruby/object:Gem::Requirement
225
+ requirements:
226
+ - - ">="
227
+ - !ruby/object:Gem::Version
228
+ version: '1.21'
229
+ type: :runtime
230
+ prerelease: false
231
+ version_requirements: !ruby/object:Gem::Requirement
232
+ requirements:
233
+ - - ">="
234
+ - !ruby/object:Gem::Version
235
+ version: '1.21'
222
236
  - !ruby/object:Gem::Dependency
223
237
  name: crack
224
238
  requirement: !ruby/object:Gem::Requirement
@@ -376,6 +390,7 @@ files:
376
390
  - spec/lib/lazy_loader_spec.rb
377
391
  - spec/lib/logger_spec.rb
378
392
  - spec/lib/mapping_spec.rb
393
+ - spec/lib/multi_json_api_spec.rb
379
394
  - spec/lib/plain_response_spec.rb
380
395
  - spec/lib/proxy_spec.rb
381
396
  - spec/lib/recording_spec.rb
@@ -405,7 +420,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
405
420
  - !ruby/object:Gem::Version
406
421
  version: '0'
407
422
  requirements: []
408
- rubygems_version: 3.6.9
423
+ rubygems_version: 4.0.10
409
424
  specification_version: 4
410
425
  summary: This gem is for accessing REST services in a flexible way. ActiveResource
411
426
  already exists for this, but it doesn't work where the resource naming doesn't follow
@@ -430,6 +445,7 @@ test_files:
430
445
  - spec/lib/lazy_loader_spec.rb
431
446
  - spec/lib/logger_spec.rb
432
447
  - spec/lib/mapping_spec.rb
448
+ - spec/lib/multi_json_api_spec.rb
433
449
  - spec/lib/plain_response_spec.rb
434
450
  - spec/lib/proxy_spec.rb
435
451
  - spec/lib/recording_spec.rb