sift 4.5.1 → 4.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0be867318fbb8cb5cb85b614e48de3748def9c8d9a6122c066c785d65c6b3b48
4
- data.tar.gz: abf0fe3da0bbf939f29cdb1071e9acb7db62473bc9fdc37dcde52631db930abc
3
+ metadata.gz: f52238626726ed4acc2b903b57bc393b46b2e8df162a021abd0b94d084631acc
4
+ data.tar.gz: 4576e4a7bb68d34b9687a8cea83d7e767cb9d10aacbf354d6dcf6167cd0de89b
5
5
  SHA512:
6
- metadata.gz: cc2d9d80295f6f4605413d001bb29b4745e28ecc58e9595a526bd8a7d8529d2b63dc14fce063e1ee347bde3b9d9370ad160917a795e59377a54d5eeb50f876b7
7
- data.tar.gz: f5f75423865db471ed649a4146a8e3e388203977ec6607c20966c9622f9b074f8e149574f9bbe28db22f44b21203e9ae082b52386bea6f8c2096afd869955a65
6
+ metadata.gz: 3b7c07547207731c7592c0a08f229adde8400511855ad54ed7ae86de0b891acbb7b4fa91b7666df2e4927a7f9483898554daa8658f28ed9d5e0a49875139a011
7
+ data.tar.gz: 3d3fd96f0ed512504cdd4f52f353ef9264ee74395b1c1af58c6d583c17114e3cdd971ec577397d28339ceea68e0d69c495e817bdb42dc16aac61a7b6e348f7a5
@@ -20,7 +20,7 @@ jobs:
20
20
  runs-on: ubuntu-latest
21
21
  strategy:
22
22
  matrix:
23
- ruby-version: ['2.4.2']
23
+ ruby-version: ['2.7', '3.1', '3.2', '3.3']
24
24
  steps:
25
25
  - uses: actions/checkout@v4
26
26
  - name: Set up Ruby
@@ -36,7 +36,7 @@ jobs:
36
36
  if: ${{ github.ref == 'refs/heads/master' }}
37
37
  strategy:
38
38
  matrix:
39
- ruby-version: [ '2.4.2' ]
39
+ ruby-version: ['2.7', '3.1', '3.2', '3.3']
40
40
  steps:
41
41
  - uses: actions/checkout@v4
42
42
  - name: Set up Ruby
data/HISTORY CHANGED
@@ -1,3 +1,9 @@
1
+ === 4.6.0 2026-02-10
2
+ - Bump the minimum version of httparty to 0.23.3 to ensure protection against CVE-2025-68696
3
+ - Refactor Client to use dedicated internal HTTP clients for different API endpoints
4
+ - Fix Verification API methods to use correct version parameter
5
+ - Increase minimum required version for Ruby to 2.7.0
6
+
1
7
  === 4.5.1 2025-04-07
2
8
  - Fix Verification URLs
3
9
 
data/README.md CHANGED
@@ -4,7 +4,7 @@ The official Ruby bindings for the latest version (v205) of the [Sift API](https
4
4
 
5
5
  ## Requirements
6
6
 
7
- * Ruby 2.0.0 or above.
7
+ * Ruby 2.7.0 or above.
8
8
 
9
9
 
10
10
  ## Installation
@@ -21,7 +21,7 @@ module Sift
21
21
  time
22
22
  }
23
23
 
24
- attr_reader :decision_id, :configs, :getter, :api_key
24
+ attr_reader :decision_id, :configs, :getter, :api_key, :client_class
25
25
 
26
26
  PROPERTIES.each do |attribute|
27
27
  class_eval %{
@@ -31,11 +31,12 @@ module Sift
31
31
  }
32
32
  end
33
33
 
34
- def initialize(api_key, decision_id, configs)
34
+ def initialize(api_key, decision_id, configs, client_class = Sift::Client)
35
35
  @api_key = api_key
36
36
  @decision_id = decision_id
37
37
  @configs = configs
38
38
  @getter = Utils::HashGetter.new(configs)
39
+ @client_class = client_class
39
40
  end
40
41
 
41
42
  def run
@@ -58,7 +59,8 @@ module Sift
58
59
  def send_request
59
60
  Router.post(path, {
60
61
  body: request_body,
61
- headers: headers
62
+ headers: headers,
63
+ client_class: client_class
62
64
  })
63
65
  end
64
66
 
@@ -79,6 +81,8 @@ module Sift
79
81
  validator.valid_order?
80
82
  elsif applying_to_session?
81
83
  validator.valid_session?
84
+ elsif applying_to_content?
85
+ validator.valid_content?
82
86
  else
83
87
  validator.valid_user?
84
88
  end
@@ -10,11 +10,12 @@ module Sift
10
10
  class Decision
11
11
  FILTER_PARAMS = %w{ limit entity_type abuse_types from }
12
12
 
13
- attr_reader :account_id, :api_key
13
+ attr_reader :account_id, :api_key, :client_class
14
14
 
15
- def initialize(api_key, account_id)
15
+ def initialize(api_key, account_id, client_class = Sift::Client)
16
16
  @account_id = account_id
17
17
  @api_key = api_key
18
+ @client_class = client_class
18
19
  end
19
20
 
20
21
  def list(options = {})
@@ -25,7 +26,8 @@ module Sift
25
26
  else
26
27
  Router.get(index_path, {
27
28
  query: build_query(getter),
28
- headers: auth_header
29
+ headers: auth_header,
30
+ client_class: client_class
29
31
  })
30
32
  end
31
33
  end
@@ -44,7 +46,7 @@ module Sift
44
46
  getter = Utils::HashGetter.new(configs)
45
47
  configs[:account_id] = account_id
46
48
 
47
- ApplyTo.new(api_key, getter.get(:decision_id), configs).run
49
+ ApplyTo.new(api_key, getter.get(:decision_id), configs, client_class).run
48
50
  end
49
51
 
50
52
  def index_path
@@ -54,7 +56,7 @@ module Sift
54
56
  private
55
57
 
56
58
  def request_next_page(path)
57
- Router.get(path, headers: auth_header)
59
+ Router.get(path, headers: auth_header, client_class: client_class)
58
60
  end
59
61
 
60
62
  def auth_header
@@ -63,4 +65,3 @@ module Sift
63
65
  end
64
66
  end
65
67
  end
66
-
data/lib/sift/client.rb CHANGED
@@ -2,9 +2,6 @@ require "httparty"
2
2
  require "multi_json"
3
3
  require "base64"
4
4
 
5
- require_relative "./client/decision"
6
- require_relative "./error"
7
-
8
5
  module Sift
9
6
 
10
7
  # Represents the payload returned from a call through the track API
@@ -94,17 +91,36 @@ module Sift
94
91
  API_ENDPOINT = ENV["SIFT_RUBY_API_URL"] || 'https://api.siftscience.com'
95
92
  API3_ENDPOINT = ENV["SIFT_RUBY_API3_URL"] || 'https://api3.siftscience.com'
96
93
 
94
+ # Maintain backward compatibility for users who may rely on HTTParty methods
97
95
  include HTTParty
98
96
  base_uri API_ENDPOINT
99
97
 
100
98
  attr_reader :api_key, :account_id
101
99
 
102
- def self.build_auth_header(api_key)
103
- { "Authorization" => "Basic #{Base64.encode64(api_key)}" }
104
- end
100
+ class << self
101
+ def build_auth_header(api_key)
102
+ { "Authorization" => "Basic #{Base64.strict_encode64(api_key + ":")}" }
103
+ end
104
+
105
+ def user_agent
106
+ "sift-ruby/#{VERSION}"
107
+ end
108
+
109
+ # Factory methods for internal API executors that inherit from the current class context.
110
+ # This ensures that subclasses of Client propagate their HTTParty configuration
111
+ # to these internal clients.
105
112
 
106
- def self.user_agent
107
- "sift-ruby/#{VERSION}"
113
+ def api_client
114
+ @api_client ||= Class.new(self) do
115
+ base_uri API_ENDPOINT
116
+ end
117
+ end
118
+
119
+ def api3_client
120
+ @api3_client ||= Class.new(self) do
121
+ base_uri API3_ENDPOINT
122
+ end
123
+ end
108
124
  end
109
125
 
110
126
  # Constructor
@@ -256,7 +272,7 @@ module Sift
256
272
  }
257
273
  options.merge!(:timeout => timeout) unless timeout.nil?
258
274
 
259
- response = self.class.post(path, options)
275
+ response = self.class.api_client.post(path, options)
260
276
  Response.new(response.body, response.code, response.response)
261
277
  end
262
278
 
@@ -319,7 +335,7 @@ module Sift
319
335
  }
320
336
  options.merge!(:timeout => timeout) unless timeout.nil?
321
337
 
322
- response = self.class.get(Sift.score_api_path(user_id, version), options)
338
+ response = self.class.api_client.get(Sift.score_api_path(user_id, version), options)
323
339
  Response.new(response.body, response.code, response.response)
324
340
  end
325
341
 
@@ -364,6 +380,7 @@ module Sift
364
380
  abuse_types = opts[:abuse_types]
365
381
  api_key = opts[:api_key] || @api_key
366
382
  timeout = opts[:timeout] || @timeout
383
+ version = opts[:version] || @version
367
384
  include_score_percentiles = opts[:include_score_percentiles]
368
385
 
369
386
  raise("user_id must be a non-empty string") if (!user_id.is_a? String) || user_id.to_s.empty?
@@ -382,7 +399,7 @@ module Sift
382
399
  }
383
400
  options.merge!(:timeout => timeout) unless timeout.nil?
384
401
 
385
- response = self.class.get(Sift.user_score_api_path(user_id, @version), options)
402
+ response = self.class.api_client.get(Sift.user_score_api_path(user_id, version), options)
386
403
  Response.new(response.body, response.code, response.response)
387
404
  end
388
405
 
@@ -420,6 +437,7 @@ module Sift
420
437
  abuse_types = opts[:abuse_types]
421
438
  api_key = opts[:api_key] || @api_key
422
439
  timeout = opts[:timeout] || @timeout
440
+ version = opts[:version] || @version
423
441
 
424
442
  raise("user_id must be a non-empty string") if (!user_id.is_a? String) || user_id.to_s.empty?
425
443
  raise("Bad api_key parameter") if api_key.empty?
@@ -434,7 +452,7 @@ module Sift
434
452
  }
435
453
  options.merge!(:timeout => timeout) unless timeout.nil?
436
454
 
437
- response = self.class.post(Sift.user_score_api_path(user_id, @version), options)
455
+ response = self.class.api_client.post(Sift.user_score_api_path(user_id, version), options)
438
456
  Response.new(response.body, response.code, response.response)
439
457
  end
440
458
 
@@ -532,7 +550,7 @@ module Sift
532
550
  }
533
551
  options.merge!(:timeout => timeout) unless timeout.nil?
534
552
 
535
- response = self.class.delete(Sift.users_label_api_path(user_id, version), options)
553
+ response = self.class.api_client.delete(Sift.users_label_api_path(user_id, version), options)
536
554
  Response.new(response.body, response.code, response.response)
537
555
  end
538
556
 
@@ -569,8 +587,7 @@ module Sift
569
587
  }
570
588
  options.merge!(:timeout => timeout) unless timeout.nil?
571
589
 
572
- uri = API3_ENDPOINT + Sift.workflow_status_path(account_id, run_id)
573
- response = self.class.get(uri, options)
590
+ response = self.class.api3_client.get(Sift.workflow_status_path(account_id, run_id), options)
574
591
  Response.new(response.body, response.code, response.response)
575
592
  end
576
593
 
@@ -607,8 +624,7 @@ module Sift
607
624
  }
608
625
  options.merge!(:timeout => timeout) unless timeout.nil?
609
626
 
610
- uri = API3_ENDPOINT + Sift.user_decisions_api_path(account_id, user_id)
611
- response = self.class.get(uri, options)
627
+ response = self.class.api3_client.get(Sift.user_decisions_api_path(account_id, user_id), options)
612
628
  Response.new(response.body, response.code, response.response)
613
629
  end
614
630
 
@@ -645,8 +661,7 @@ module Sift
645
661
  }
646
662
  options.merge!(:timeout => timeout) unless timeout.nil?
647
663
 
648
- uri = API3_ENDPOINT + Sift.order_decisions_api_path(account_id, order_id)
649
- response = self.class.get(uri, options)
664
+ response = self.class.api3_client.get(Sift.order_decisions_api_path(account_id, order_id), options)
650
665
  Response.new(response.body, response.code, response.response)
651
666
  end
652
667
 
@@ -685,8 +700,7 @@ module Sift
685
700
  }
686
701
  options.merge!(:timeout => timeout) unless timeout.nil?
687
702
 
688
- uri = API3_ENDPOINT + Sift.session_decisions_api_path(account_id, user_id, session_id)
689
- response = self.class.get(uri, options)
703
+ response = self.class.api3_client.get(Sift.session_decisions_api_path(account_id, user_id, session_id), options)
690
704
  Response.new(response.body, response.code, response.response)
691
705
  end
692
706
 
@@ -725,8 +739,7 @@ module Sift
725
739
  }
726
740
  options.merge!(:timeout => timeout) unless timeout.nil?
727
741
 
728
- uri = API3_ENDPOINT + Sift.content_decisions_api_path(account_id, user_id, content_id)
729
- response = self.class.get(uri, options)
742
+ response = self.class.api3_client.get(Sift.content_decisions_api_path(account_id, user_id, content_id), options)
730
743
  Response.new(response.body, response.code, response.response)
731
744
  end
732
745
 
@@ -748,7 +761,7 @@ module Sift
748
761
 
749
762
  def build_default_headers_post(api_key)
750
763
  {
751
- "Authorization" => "Basic #{Base64.encode64(api_key+":")}",
764
+ "Authorization" => "Basic #{Base64.strict_encode64(api_key+":")}",
752
765
  "User-Agent" => "SiftScience/v#{@version} sift-ruby/#{VERSION}",
753
766
  "Content-Type" => "application/json"
754
767
  }
@@ -768,7 +781,7 @@ module Sift
768
781
  :headers => build_default_headers_post(api_key)
769
782
  }
770
783
  options.merge!(:timeout => timeout) unless timeout.nil?
771
- response = self.class.post(Sift.verification_api_send_path(@version), options)
784
+ response = self.class.api_client.post(Sift.verification_api_send_path(version), options)
772
785
  Response.new(response.body, response.code, response.response)
773
786
  end
774
787
 
@@ -787,7 +800,7 @@ module Sift
787
800
  }
788
801
  options.merge!(:timeout => timeout) unless timeout.nil?
789
802
 
790
- response = self.class.post(Sift.verification_api_resend_path(@version), options)
803
+ response = self.class.api_client.post(Sift.verification_api_resend_path(version), options)
791
804
  Response.new(response.body, response.code, response.response)
792
805
  end
793
806
 
@@ -806,7 +819,7 @@ module Sift
806
819
  }
807
820
  options.merge!(:timeout => timeout) unless timeout.nil?
808
821
 
809
- response = self.class.post(Sift.verification_api_check_path(@version), options)
822
+ response = self.class.api_client.post(Sift.verification_api_check_path(version), options)
810
823
  Response.new(response.body, response.code, response.response)
811
824
  end
812
825
 
@@ -831,7 +844,7 @@ module Sift
831
844
  :basic_auth => { :username => api_key, :password => "" }
832
845
  }
833
846
  options.merge!(:timeout => timeout) unless timeout.nil?
834
- response = self.class.post(API_ENDPOINT + Sift.psp_merchant_api_path(account_id), options)
847
+ response = self.class.api_client.post(Sift.psp_merchant_api_path(account_id), options)
835
848
  Response.new(response.body, response.code, response.response)
836
849
  end
837
850
 
@@ -858,7 +871,7 @@ module Sift
858
871
  :basic_auth => { :username => api_key, :password => "" }
859
872
  }
860
873
  options.merge!(:timeout => timeout) unless timeout.nil?
861
- response = self.class.put(API_ENDPOINT + Sift.psp_merchant_id_api_path(account_id, merchant_id), options)
874
+ response = self.class.api_client.put(Sift.psp_merchant_id_api_path(account_id, merchant_id), options)
862
875
  Response.new(response.body, response.code, response.response)
863
876
  end
864
877
 
@@ -882,7 +895,7 @@ module Sift
882
895
  :basic_auth => { :username => api_key, :password => "" }
883
896
  }
884
897
  options.merge!(:timeout => timeout) unless timeout.nil?
885
- response = self.class.get(API_ENDPOINT + Sift.psp_merchant_id_api_path(account_id, merchant_id), options)
898
+ response = self.class.api_client.get(Sift.psp_merchant_id_api_path(account_id, merchant_id), options)
886
899
  Response.new(response.body, response.code, response.response)
887
900
  end
888
901
 
@@ -911,7 +924,7 @@ module Sift
911
924
  :query => query
912
925
  }
913
926
  options.merge!(:timeout => timeout) unless timeout.nil?
914
- response = self.class.get(API_ENDPOINT + Sift.psp_merchant_api_path(account_id), options)
927
+ response = self.class.api_client.get(Sift.psp_merchant_api_path(account_id), options)
915
928
  Response.new(response.body, response.code, response.response)
916
929
  end
917
930
 
@@ -926,7 +939,7 @@ module Sift
926
939
  end
927
940
 
928
941
  def decision_instance
929
- @decision_instance ||= Decision.new(api_key, account_id)
942
+ @decision_instance ||= Decision.new(api_key, account_id, self.class)
930
943
  end
931
944
 
932
945
  def delete_nils(properties)
@@ -943,4 +956,8 @@ module Sift
943
956
  end
944
957
  end
945
958
  end
959
+
960
+ require_relative "./client/decision"
961
+ require_relative "./error"
962
+
946
963
  end
data/lib/sift/router.rb CHANGED
@@ -2,20 +2,23 @@ require_relative "./version"
2
2
  require_relative "./client"
3
3
 
4
4
  module Sift
5
- class Router
6
- include HTTParty
5
+ class Router < Client
7
6
 
8
7
  class << self
9
8
  def get(path, options = {})
9
+ client_class = options.delete(:client_class) || Sift::Client
10
+ options[:base_uri] = nil
10
11
  serialize_body(options)
11
12
  add_default_headers(options)
12
- wrap_response(super(path, options))
13
+ wrap_response(client_class.api3_client.get(path, options))
13
14
  end
14
15
 
15
16
  def post(path, options = {})
17
+ client_class = options.delete(:client_class) || Sift::Client
18
+ options[:base_uri] = nil
16
19
  serialize_body(options)
17
20
  add_default_headers(options)
18
- wrap_response(super(path, options))
21
+ wrap_response(client_class.api3_client.post(path, options))
19
22
  end
20
23
 
21
24
  def serialize_body(options)
data/lib/sift/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Sift
2
- VERSION = "4.5.1"
2
+ VERSION = "4.6.0"
3
3
  API_VERSION = "205"
4
4
  VERIFICATION_API_VERSION = "1.1"
5
5
  end
data/sift.gemspec CHANGED
@@ -7,10 +7,10 @@ Gem::Specification.new do |s|
7
7
  s.version = Sift::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
9
  s.authors = ["Fred Sadaghiani", "Yoav Schatzberg", "Jacob Burnim"]
10
- s.email = ["support@siftscience.com"]
11
- s.homepage = "http://siftscience.com"
12
- s.summary = %q{Sift Science Ruby API Gem}
13
- s.description = %q{Sift Science Ruby API. Please see http://siftscience.com for more details.}
10
+ s.email = ["support@sift.com"]
11
+ s.homepage = "http://sift.com"
12
+ s.summary = %q{Sift Ruby API Gem}
13
+ s.description = %q{Sift Ruby API. Please see http://sift.com for more details.}
14
14
 
15
15
  s.rubyforge_project = "sift"
16
16
 
@@ -19,6 +19,8 @@ Gem::Specification.new do |s|
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
21
 
22
+ s.required_ruby_version = '>= 2.7.0'
23
+
22
24
  # Gems that must be intalled for sift to compile and build
23
25
  s.add_development_dependency "rspec", "~> 3.5"
24
26
  s.add_development_dependency "rspec_junit_formatter"
@@ -26,7 +28,7 @@ Gem::Specification.new do |s|
26
28
  s.add_development_dependency "webmock", ">= 1.16.0", "< 2"
27
29
 
28
30
  # Gems that must be intalled for sift to work
29
- s.add_dependency "httparty", ">= 0.11.0"
31
+ s.add_dependency "httparty", ">= 0.23.3"
30
32
  s.add_dependency "multi_json", ">= 1.0"
31
33
 
32
34
  s.add_development_dependency("rake")
@@ -254,7 +254,7 @@ module Sift
254
254
  def put_auth_in_url(api_key, url)
255
255
  protocal, uri = url.split(/(?<=https\:\/\/)/)
256
256
 
257
- protocal + api_key + "@" + uri
257
+ protocal + api_key + ":@" + uri
258
258
  end
259
259
  end
260
260
  end
@@ -20,7 +20,7 @@ module Sift
20
20
  #
21
21
  protocal, uri = decision.index_path.split(/(?<=https\:\/\/)/)
22
22
 
23
- protocal + api_key + "@" + uri
23
+ protocal + api_key + ":@" + uri
24
24
  }
25
25
 
26
26
  describe "#list" do
@@ -88,4 +88,50 @@ describe Sift::Client do
88
88
 
89
89
  end
90
90
 
91
+ # Regression tests for verification API version bug fix
92
+ # These tests ensure verification methods use verification_version (1.1)
93
+ # instead of the events API version (205)
94
+
95
+ it "Uses verification API version (1.1) not events API version (205) for verification_send" do
96
+ api_key = "test_key"
97
+ response_json = { :status => 0, :error_message => "OK"}
98
+
99
+ # Should call v1.1, NOT v205
100
+ stub_request(:post, "https://test_key:@api.siftscience.com/v1.1/verification/send")
101
+ .to_return(:status => 200, :body => MultiJson.dump(response_json))
102
+
103
+ # Client defaults: version=205 (events), verification_version=1.1
104
+ response = Sift::Client.new(:api_key => api_key).verification_send(valid_send_properties)
105
+
106
+ expect(response.ok?).to eq(true)
107
+ end
108
+
109
+ it "Uses verification API version (1.1) not events API version (205) for verification_resend" do
110
+ api_key = "test_key"
111
+ response_json = { :status => 0, :error_message => "OK"}
112
+
113
+ # Should call v1.1, NOT v205
114
+ stub_request(:post, "https://test_key:@api.siftscience.com/v1.1/verification/resend")
115
+ .to_return(:status => 200, :body => MultiJson.dump(response_json))
116
+
117
+ # Client defaults: version=205 (events), verification_version=1.1
118
+ response = Sift::Client.new(:api_key => api_key).verification_resend(valid_resend_properties)
119
+
120
+ expect(response.ok?).to eq(true)
121
+ end
122
+
123
+ it "Uses verification API version (1.1) not events API version (205) for verification_check" do
124
+ api_key = "test_key"
125
+ response_json = { :status => 0, :error_message => "OK"}
126
+
127
+ # Should call v1.1, NOT v205
128
+ stub_request(:post, "https://test_key:@api.siftscience.com/v1.1/verification/check")
129
+ .to_return(:status => 200, :body => MultiJson.dump(response_json))
130
+
131
+ # Client defaults: version=205 (events), verification_version=1.1
132
+ response = Sift::Client.new(:api_key => api_key).verification_check(valid_check_properties)
133
+
134
+ expect(response.ok?).to eq(true)
135
+ end
136
+
91
137
  end
@@ -0,0 +1,67 @@
1
+ require_relative "../spec_helper"
2
+ require "sift"
3
+ require "logger"
4
+
5
+ describe "Sift::Client Configuration Patterns" do
6
+ let(:api_key) { "test_api_key" }
7
+
8
+ it "propagates global Sift::Client configuration to internal clients" do
9
+ Sift::Client.default_timeout 5
10
+
11
+ # Internal executors should inherit this
12
+ expect(Sift::Client.api_client.default_options[:timeout]).to eq(5)
13
+ expect(Sift::Client.api3_client.default_options[:timeout]).to eq(5)
14
+
15
+ # Reset
16
+ Sift::Client.default_timeout 2
17
+ end
18
+
19
+ it "allows independent subclass configurations" do
20
+ class SubclassA < Sift::Client; end
21
+ class SubclassB < Sift::Client; end
22
+
23
+ SubclassA.default_timeout 10
24
+ SubclassB.default_timeout 20
25
+
26
+ expect(SubclassA.api_client.default_options[:timeout]).to eq(10)
27
+ expect(SubclassB.api_client.default_options[:timeout]).to eq(20)
28
+
29
+ # Ensure they didn't leak to parent
30
+ expect(Sift::Client.api_client.default_options[:timeout]).to be <= 5
31
+ end
32
+
33
+ it "propagates complex settings like loggers to subclasses" do
34
+ class LoggingClient < Sift::Client; end
35
+
36
+ logger = Logger.new(nil)
37
+ LoggingClient.logger logger, :debug, :curl
38
+
39
+ expect(LoggingClient.api_client.default_options[:logger]).to eq(logger)
40
+ expect(LoggingClient.api_client.default_options[:log_level]).to eq(:debug)
41
+ expect(LoggingClient.api_client.default_options[:log_format]).to eq(:curl)
42
+
43
+ # Ensure Sift::Client remains untouched
44
+ expect(Sift::Client.api_client.default_options[:logger]).to be_nil
45
+ end
46
+
47
+ it "respects inheritance chain for Decisions and Router" do
48
+ class DecisionClient < Sift::Client; end
49
+ DecisionClient.default_timeout 15
50
+
51
+ client = DecisionClient.new(api_key: api_key, account_id: "acc")
52
+
53
+ # Verify the executor used by the Router is the one from DecisionClient
54
+ expect(DecisionClient.api3_client.default_options[:timeout]).to eq(15)
55
+
56
+ # We want to ensure that when Router.get is called, it uses DecisionClient.api3_client
57
+ # In lib/sift/router.rb, we have:
58
+ # wrap_response(client_class.api3_client.get(path, options))
59
+
60
+ expect(DecisionClient.api3_client).to receive(:get).and_call_original
61
+
62
+ # Mock the request to avoid network calls
63
+ stub_request(:get, /api3.siftscience.com/)
64
+
65
+ client.get_user_decisions("user_1")
66
+ end
67
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sift
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.5.1
4
+ version: 4.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Sadaghiani
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2025-04-07 00:00:00.000000000 Z
13
+ date: 2026-03-02 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec
@@ -80,14 +80,14 @@ dependencies:
80
80
  requirements:
81
81
  - - ">="
82
82
  - !ruby/object:Gem::Version
83
- version: 0.11.0
83
+ version: 0.23.3
84
84
  type: :runtime
85
85
  prerelease: false
86
86
  version_requirements: !ruby/object:Gem::Requirement
87
87
  requirements:
88
88
  - - ">="
89
89
  - !ruby/object:Gem::Version
90
- version: 0.11.0
90
+ version: 0.23.3
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: multi_json
93
93
  requirement: !ruby/object:Gem::Requirement
@@ -116,9 +116,9 @@ dependencies:
116
116
  - - ">="
117
117
  - !ruby/object:Gem::Version
118
118
  version: '0'
119
- description: Sift Science Ruby API. Please see http://siftscience.com for more details.
119
+ description: Sift Ruby API. Please see http://sift.com for more details.
120
120
  email:
121
- - support@siftscience.com
121
+ - support@sift.com
122
122
  executables: []
123
123
  extensions: []
124
124
  extra_rdoc_files: []
@@ -157,6 +157,7 @@ files:
157
157
  - spec/unit/client_psp_merchant_spec.rb
158
158
  - spec/unit/client_spec.rb
159
159
  - spec/unit/client_validationapi_spec.rb
160
+ - spec/unit/configuration_spec.rb
160
161
  - spec/unit/router_spec.rb
161
162
  - spec/unit/validate/decision_spec.rb
162
163
  - spec/unit/validate/primitive_spec.rb
@@ -167,7 +168,7 @@ files:
167
168
  - test_integration_app/psp_merchants_api/test_psp_merchant_api.rb
168
169
  - test_integration_app/score_api/test_score_api.rb
169
170
  - test_integration_app/verification_api/test_verification_api.rb
170
- homepage: http://siftscience.com
171
+ homepage: http://sift.com
171
172
  licenses: []
172
173
  metadata: {}
173
174
  post_install_message:
@@ -178,7 +179,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
178
179
  requirements:
179
180
  - - ">="
180
181
  - !ruby/object:Gem::Version
181
- version: '0'
182
+ version: 2.7.0
182
183
  required_rubygems_version: !ruby/object:Gem::Requirement
183
184
  requirements:
184
185
  - - ">="
@@ -188,5 +189,5 @@ requirements: []
188
189
  rubygems_version: 3.1.6
189
190
  signing_key:
190
191
  specification_version: 4
191
- summary: Sift Science Ruby API Gem
192
+ summary: Sift Ruby API Gem
192
193
  test_files: []