x 0.8.0 → 0.9.0

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: 0b97ab44334b647867e9fdd400531d6e925a10533fb51be399c669c28d441c67
4
- data.tar.gz: 03e6b5b78f62cdef778dea21128e8ea1b56955a1077c5ea040b6640694e7577a
3
+ metadata.gz: 7ff134774b6d6b28f3f72c740c74b1f44b11ce94cff63fbfe4eabc0414f600c3
4
+ data.tar.gz: 9f3b9abd3ca2628979676b5f634781b960adf6afe8a77559cf6f3a96ee738098
5
5
  SHA512:
6
- metadata.gz: 47b47a9e6f98e6a61933d7f8f53d2477b2e921206894b2907171f04c2d94da05ebdbb25b501824240997d07c935cc0d2ea1269c6082e7488800c6c7d141f9c0e
7
- data.tar.gz: a642eb6fa5e9b61b3cbcf7fff91506e7f44c6b5b5e7a7f16c4cc5ce7ed57d8d19191583fad4bd2bc174c8e9f010546c34d8c83a1b46444de3685805a4b6471dc
6
+ metadata.gz: cd471d0b216161f5e68832e83b5024a8a89cdda1c815f49543f8361b225776ad46f84690eddf9a9b884cf0ab540924ecae94ebd145f26ac07e44ffdf7c4ff1f2
7
+ data.tar.gz: 3670666ca95e154a253be800af600cdcaf44195b9dcd3d001e1404d85f27dd5939ce09bd25588cfbe81cefef92ce22bfcd26e105dcb70d813bc4efa221a4360d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.9.0] - 2023-09-26
4
+
5
+ - Add support for HTTP proxies (3740f4f)
6
+
7
+ ## [0.8.1] - 2023-09-20
8
+
9
+ - Fix bug where setting Connection#base_uri= doesn't update the HTTP client (d5a89db)
10
+
3
11
  ## [0.8.0] - 2023-09-14
4
12
 
5
13
  - Add (back) bearer token authentication (62e141d)
data/README.md CHANGED
@@ -56,6 +56,8 @@ ads_client = X::Client.new(base_url: "https://ads-api.twitter.com/12/", **x_cred
56
56
  ads_client.get("accounts")
57
57
  ```
58
58
 
59
+ See other common usage [examples](https://github.com/sferik/x-ruby/tree/main/examples).
60
+
59
61
  ## History and Philosophy
60
62
 
61
63
  This library is a rewrite of the [Twitter Ruby library](https://github.com/sferik/twitter). Over 16 years of development, that library ballooned to over 3,000 lines of code (plus 7,500 lines of tests). At the time of writing, this library is about 300 lines of code (plus 200 test lines) and I’d like to keep it that way. That doesn’t mean new features won’t be added over time, but the benefits of more code must be weighted against the benefits of less:
@@ -74,6 +76,20 @@ The tests for the previous version of this library executed in about 2 seconds.
74
76
 
75
77
  This code is not littered with comments that are intended to generate documentation. Rather, this code is intended to be simple enough to serve as its own documentation. If you want to understand how something works, don’t read the documentation—it might be wrong—read the code. The code is always right.
76
78
 
79
+ ## Sponsorship
80
+
81
+ The X gem is free to use, but with X API pricing tiers, it actually costs money to develop and maintain. By contributing to the project, you help us:
82
+
83
+ 1. Maintain the library: Keeping it up-to-date and secure.
84
+ 2. Add new features: Enhancements that make your life easier.
85
+ 3. Provide support: Faster responses to issues and feature requests.
86
+
87
+ ⭐️ Bonus: Sponsors will get priority support and influence over the project roadmap. We will also list your name or your company's logo on our GitHub page.
88
+
89
+ Building and maintaining an open-source project like this takes a considerable amount of time and effort. Your sponsorship can help sustain this project. Even a small monthly donation makes a huge difference!
90
+
91
+ [Click here to sponsor this project.](https://github.com/sponsors/sferik)
92
+
77
93
  ## Development
78
94
 
79
95
  1. Checkout and repo:
data/bin/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "x"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ require "irb"
10
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
data/lib/x/client.rb CHANGED
@@ -26,6 +26,7 @@ module X
26
26
  open_timeout: Connection::DEFAULT_OPEN_TIMEOUT,
27
27
  read_timeout: Connection::DEFAULT_READ_TIMEOUT,
28
28
  write_timeout: Connection::DEFAULT_WRITE_TIMEOUT,
29
+ proxy_url: nil,
29
30
  content_type: RequestBuilder::DEFAULT_CONTENT_TYPE,
30
31
  user_agent: RequestBuilder::DEFAULT_USER_AGENT,
31
32
  debug_output: nil,
@@ -35,7 +36,7 @@ module X
35
36
 
36
37
  initialize_authenticator(bearer_token, api_key, api_key_secret, access_token, access_token_secret)
37
38
  @connection = Connection.new(base_url: base_url, open_timeout: open_timeout, read_timeout: read_timeout,
38
- write_timeout: write_timeout, debug_output: debug_output)
39
+ write_timeout: write_timeout, debug_output: debug_output, proxy_url: proxy_url)
39
40
  @request_builder = RequestBuilder.new(content_type: content_type, user_agent: user_agent)
40
41
  @redirect_handler = RedirectHandler.new(@authenticator, @connection, @request_builder,
41
42
  max_redirects: max_redirects)
data/lib/x/connection.rb CHANGED
@@ -9,6 +9,8 @@ module X
9
9
  extend Forwardable
10
10
 
11
11
  DEFAULT_BASE_URL = "https://api.twitter.com/2/".freeze
12
+ DEFAULT_HOST = "https://api.twitter.com".freeze
13
+ DEFAULT_PORT = 443
12
14
  DEFAULT_OPEN_TIMEOUT = 60 # seconds
13
15
  DEFAULT_READ_TIMEOUT = 60 # seconds
14
16
  DEFAULT_WRITE_TIMEOUT = 60 # seconds
@@ -18,21 +20,22 @@ module X
18
20
  Net::ReadTimeout
19
21
  ].freeze
20
22
 
21
- attr_reader :base_uri
23
+ attr_reader :base_uri, :proxy_uri, :http_client
22
24
 
23
25
  def_delegators :@http_client, :open_timeout, :read_timeout, :write_timeout
24
26
  def_delegators :@http_client, :open_timeout=, :read_timeout=, :write_timeout=
25
27
  def_delegator :@http_client, :set_debug_output, :debug_output=
26
28
 
27
29
  def initialize(base_url: DEFAULT_BASE_URL, open_timeout: DEFAULT_OPEN_TIMEOUT,
28
- read_timeout: DEFAULT_READ_TIMEOUT, write_timeout: DEFAULT_WRITE_TIMEOUT, debug_output: nil)
30
+ read_timeout: DEFAULT_READ_TIMEOUT, write_timeout: DEFAULT_WRITE_TIMEOUT, proxy_url: nil, debug_output: nil)
31
+ @proxy_uri = URI(proxy_url) unless proxy_url.nil?
29
32
  self.base_uri = base_url
30
- @http_client = Net::HTTP.new(base_uri.host, base_uri.port) if base_uri.host
31
- @http_client.use_ssl = base_uri.scheme == "https"
32
- @http_client.open_timeout = open_timeout
33
- @http_client.read_timeout = read_timeout
34
- @http_client.write_timeout = write_timeout
35
- @http_client.set_debug_output(debug_output) if debug_output
33
+ apply_http_client_settings(
34
+ open_timeout: open_timeout,
35
+ read_timeout: read_timeout,
36
+ write_timeout: write_timeout,
37
+ debug_output: debug_output
38
+ )
36
39
  end
37
40
 
38
41
  def send_request(request)
@@ -46,10 +49,56 @@ module X
46
49
  raise ArgumentError, "Invalid base URL" unless base_uri.is_a?(URI::HTTPS) || base_uri.is_a?(URI::HTTP)
47
50
 
48
51
  @base_uri = base_uri
52
+ update_http_client_settings
49
53
  end
50
54
 
51
55
  def debug_output
52
56
  @http_client.instance_variable_get(:@debug_output)
53
57
  end
58
+
59
+ private
60
+
61
+ def apply_http_client_settings(open_timeout:, read_timeout:, write_timeout:, debug_output:)
62
+ @http_client.open_timeout = open_timeout
63
+ @http_client.read_timeout = read_timeout
64
+ @http_client.write_timeout = write_timeout
65
+ @http_client.set_debug_output(debug_output) if debug_output
66
+ end
67
+
68
+ def current_http_client_settings
69
+ {
70
+ open_timeout: @http_client.open_timeout,
71
+ read_timeout: @http_client.read_timeout,
72
+ write_timeout: @http_client.write_timeout,
73
+ debug_output: debug_output
74
+ }
75
+ end
76
+
77
+ def update_http_client_settings
78
+ conditionally_apply_http_client_settings do
79
+ host = @base_uri.host || DEFAULT_HOST
80
+ port = @base_uri.port || DEFAULT_PORT
81
+ @http_client = build_http_client(host: host, port: port)
82
+ @http_client.use_ssl = @base_uri.scheme == "https"
83
+ end
84
+ end
85
+
86
+ def build_http_client(host:, port:)
87
+ if @proxy_uri.nil?
88
+ Net::HTTP.new(host, port)
89
+ else
90
+ Net::HTTP.new(host, port, @proxy_uri&.host, @proxy_uri&.port, @proxy_uri&.user, @proxy_uri&.password)
91
+ end
92
+ end
93
+
94
+ def conditionally_apply_http_client_settings
95
+ if @http_client
96
+ settings = current_http_client_settings
97
+ yield
98
+ apply_http_client_settings(**settings)
99
+ else
100
+ yield
101
+ end
102
+ end
54
103
  end
55
104
  end
@@ -49,7 +49,7 @@ module X
49
49
  def send_new_request(new_uri, new_request)
50
50
  @connection = Connection.new(base_url: new_uri, open_timeout: connection.open_timeout,
51
51
  read_timeout: connection.read_timeout, write_timeout: connection.write_timeout,
52
- debug_output: connection.debug_output)
52
+ debug_output: connection.debug_output, proxy_url: connection.proxy_uri)
53
53
  connection.send_request(new_request)
54
54
  end
55
55
  end
data/lib/x/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require "rubygems/version"
2
2
 
3
3
  module X
4
- VERSION = Gem::Version.create("0.8.0")
4
+ VERSION = Gem::Version.create("0.9.0")
5
5
  end
data/sig/x.rbs CHANGED
@@ -82,23 +82,32 @@ module X
82
82
  end
83
83
 
84
84
  class Connection
85
- extend Forwardable
86
-
87
85
  DEFAULT_BASE_URL: String
86
+ DEFAULT_HOST: String
87
+ DEFAULT_PORT: Integer
88
88
  DEFAULT_OPEN_TIMEOUT: Integer
89
89
  DEFAULT_READ_TIMEOUT: Integer
90
90
  DEFAULT_WRITE_TIMEOUT: Integer
91
91
  NETWORK_ERRORS: Array[(singleton(::Errno::ECONNREFUSED) | singleton(::Net::OpenTimeout) | singleton(::Net::ReadTimeout))]
92
+ extend Forwardable
92
93
  @http_client: Net::HTTP
93
94
 
94
95
  attr_reader base_uri: URI::Generic
95
- attr_reader open_timeout: Float | Integer
96
- attr_reader read_timeout: Float | Integer
97
- attr_reader write_timeout: Float | Integer
98
- def initialize: (?base_url: URI::Generic | String, ?open_timeout: Float | Integer, ?read_timeout: Float | Integer, ?write_timeout: Float | Integer, ?debug_output: IO?) -> void
96
+ attr_reader proxy_uri: URI::Generic?
97
+ attr_reader open_timeout : Float | Integer
98
+ attr_reader read_timeout : Float | Integer
99
+ attr_reader write_timeout : Float | Integer
100
+ def initialize: (?base_url: URI::Generic | String, ?open_timeout: Float | Integer, ?read_timeout: Float | Integer, ?write_timeout: Float | Integer, ?proxy_url: URI::Generic? | String?, ?debug_output: IO?) -> void
99
101
  def send_request: (Net::HTTPRequest request) -> Net::HTTPResponse
100
- def base_uri=: (URI::Generic | String base_url) -> URI::Generic
102
+ def base_uri=: (URI::Generic | String base_url) -> void
101
103
  def debug_output: -> IO?
104
+
105
+ private
106
+ def apply_http_client_settings: (open_timeout: Float | Integer, read_timeout: Float | Integer, write_timeout: Float | Integer, debug_output: IO?) -> untyped
107
+ def current_http_client_settings: -> {open_timeout: Float | Integer, read_timeout: Float | Integer, write_timeout: Float | Integer, debug_output: IO?}
108
+ def update_http_client_settings: -> untyped
109
+ def build_http_client: (host: String, port: Integer) -> (Net::HTTP)
110
+ def conditionally_apply_http_client_settings: { -> untyped } -> untyped
102
111
  end
103
112
 
104
113
  class RequestBuilder
@@ -161,7 +170,7 @@ module X
161
170
  @response_handler: ResponseHandler
162
171
 
163
172
  attr_reader base_uri: URI::Generic
164
- def initialize: (?bearer_token: String?, ?api_key: String?, ?api_key_secret: String?, ?access_token: String?, ?access_token_secret: String?, ?base_url: URI::Generic | String, ?content_type: String, ?user_agent: String, ?open_timeout: Float | Integer, ?read_timeout: Float | Integer, ?write_timeout: Float | Integer, ?debug_output: IO?, ?array_class: Class, ?object_class: Class, ?max_redirects: Integer) -> void
173
+ def initialize: (?bearer_token: String?, ?api_key: String?, ?api_key_secret: String?, ?access_token: String?, ?access_token_secret: String?, ?base_url: URI::Generic | String, ?content_type: String, ?user_agent: String, ?open_timeout: Float | Integer, ?read_timeout: Float | Integer, ?write_timeout: Float | Integer, ?proxy_url: URI::Generic? | String?, ?debug_output: IO?, ?array_class: Class, ?object_class: Class, ?max_redirects: Integer) -> void
165
174
  def get: (String endpoint) -> Hash[String, untyped]
166
175
  def post: (String endpoint, ?nil body) -> Hash[String, untyped]
167
176
  def put: (String endpoint, ?nil body) -> Hash[String, untyped]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: x
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Erik Berlin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-09-14 00:00:00.000000000 Z
11
+ date: 2023-09-26 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -17,13 +17,11 @@ executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
- - ".rubocop.yml"
21
20
  - CHANGELOG.md
22
- - Gemfile
23
21
  - LICENSE.txt
24
22
  - README.md
25
- - Rakefile
26
- - Steepfile
23
+ - bin/console
24
+ - bin/setup
27
25
  - lib/x.rb
28
26
  - lib/x/bearer_token_authenticator.rb
29
27
  - lib/x/client.rb
@@ -50,10 +48,12 @@ licenses:
50
48
  - MIT
51
49
  metadata:
52
50
  allowed_push_host: https://rubygems.org
51
+ rubygems_mfa_required: 'true'
53
52
  homepage_uri: https://sferik.github.io/x-ruby
54
53
  source_code_uri: https://github.com/sferik/x-ruby
55
54
  changelog_uri: https://github.com/sferik/x-ruby/blob/master/CHANGELOG.md
56
- rubygems_mfa_required: 'true'
55
+ bug_tracker_uri: https://github.com/sferik/x-ruby/issues
56
+ documentation_uri: https://rubydoc.info/gems/x/
57
57
  post_install_message:
58
58
  rdoc_options: []
59
59
  require_paths:
@@ -69,7 +69,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
69
  - !ruby/object:Gem::Version
70
70
  version: '0'
71
71
  requirements: []
72
- rubygems_version: 3.4.18
72
+ rubygems_version: 3.4.19
73
73
  signing_key:
74
74
  specification_version: 4
75
75
  summary: A Ruby interface to the X API.
data/.rubocop.yml DELETED
@@ -1,54 +0,0 @@
1
- require:
2
- - standard
3
- - standard-performance
4
- - rubocop-minitest
5
- - rubocop-performance
6
- - rubocop-rake
7
-
8
- AllCops:
9
- NewCops: enable
10
- TargetRubyVersion: 3.0
11
-
12
- Layout/ArgumentAlignment:
13
- Enabled: true
14
- EnforcedStyle: with_fixed_indentation
15
-
16
- Layout/ArrayAlignment:
17
- Enabled: true
18
- EnforcedStyle: with_fixed_indentation
19
-
20
- Layout/EndAlignment:
21
- Enabled: true
22
- EnforcedStyleAlignWith: variable
23
-
24
- Layout/HashAlignment:
25
- Enabled: true
26
- EnforcedHashRocketStyle: key
27
- EnforcedColonStyle: key
28
- EnforcedLastArgumentHashStyle: always_inspect
29
-
30
- Layout/ParameterAlignment:
31
- Enabled: true
32
- EnforcedStyle: with_fixed_indentation
33
- IndentationWidth: ~
34
-
35
- Layout/SpaceInsideHashLiteralBraces:
36
- Enabled: false
37
-
38
- Metrics/ParameterLists:
39
- CountKeywordArgs: false
40
-
41
- Style/Alias:
42
- Enabled: true
43
- EnforcedStyle: prefer_alias_method
44
-
45
- Style/StringLiterals:
46
- Enabled: true
47
- EnforcedStyle: double_quotes
48
-
49
- Style/StringLiteralsInInterpolation:
50
- Enabled: true
51
- EnforcedStyle: double_quotes
52
-
53
- Style/FrozenStringLiteralComment:
54
- Enabled: false
data/Gemfile DELETED
@@ -1,18 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- # Specify your gem's dependencies in x.gemspec
4
- gemspec
5
-
6
- gem "hashie", ">= 5"
7
- gem "minitest", ">= 5.19"
8
- gem "rake", ">= 13.0.6"
9
- gem "rbs", ">= 3.2.1"
10
- gem "rubocop", ">= 1.21"
11
- gem "rubocop-minitest", ">= 0.31"
12
- gem "rubocop-performance", ">= 1.18"
13
- gem "rubocop-rake", ">= 0.6"
14
- gem "simplecov", ">= 0.22"
15
- gem "standard", ">= 1.30.1"
16
- gem "steep", ">= 1.5.3"
17
- gem "timecop", ">= 0.9.6"
18
- gem "webmock", ">= 3.18.1"
data/Rakefile DELETED
@@ -1,23 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
3
-
4
- Rake::TestTask.new(:test) do |t|
5
- t.libs << "test"
6
- t.libs << "lib"
7
- t.test_files = FileList["test/**/*_test.rb"]
8
- end
9
-
10
- require "standard/rake"
11
- require "rubocop/rake_task"
12
-
13
- RuboCop::RakeTask.new
14
-
15
- require "steep"
16
- require "steep/cli"
17
-
18
- desc "Type check with Steep"
19
- task :steep do
20
- Steep::CLI.new(argv: ["check"], stdout: $stdout, stderr: $stderr, stdin: $stdin).run
21
- end
22
-
23
- task default: %i[test rubocop standard steep]
data/Steepfile DELETED
@@ -1,13 +0,0 @@
1
- target :lib do
2
- signature "sig"
3
- check "lib"
4
- library "base64"
5
- library "cgi"
6
- library "forwardable"
7
- library "json"
8
- library "net-http"
9
- library "openssl"
10
- library "securerandom"
11
- library "uri"
12
- configure_code_diagnostics(Steep::Diagnostic::Ruby.strict)
13
- end