sofia 0.1.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.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/.byebug_history +6 -0
  3. data/.rubocop.yml +179 -0
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +5 -0
  6. data/CODE_OF_CONDUCT.md +132 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +21 -0
  9. data/Rakefile +16 -0
  10. data/lib/sofia/adapter/net_http.rb +67 -0
  11. data/lib/sofia/adapter.rb +20 -0
  12. data/lib/sofia/client.rb +55 -0
  13. data/lib/sofia/error/argument_error.rb +8 -0
  14. data/lib/sofia/error/connection_failed.rb +8 -0
  15. data/lib/sofia/error/invalid_json.rb +8 -0
  16. data/lib/sofia/error/ssl_error.rb +8 -0
  17. data/lib/sofia/error/timeout_error.rb +8 -0
  18. data/lib/sofia/error.rb +14 -0
  19. data/lib/sofia/helper.rb +9 -0
  20. data/lib/sofia/request.rb +56 -0
  21. data/lib/sofia/response.rb +57 -0
  22. data/lib/sofia/sorbet_types.rb +19 -0
  23. data/lib/sofia/types/adapter.rb +39 -0
  24. data/lib/sofia/types/base_url.rb +33 -0
  25. data/lib/sofia/types/body.rb +60 -0
  26. data/lib/sofia/types/headers.rb +56 -0
  27. data/lib/sofia/types/params.rb +56 -0
  28. data/lib/sofia/types/path.rb +28 -0
  29. data/lib/sofia/types.rb +13 -0
  30. data/lib/sofia/version.rb +5 -0
  31. data/lib/sofia.rb +25 -0
  32. data/sig/sofia.rbs +4 -0
  33. data/sorbet/config +6 -0
  34. data/sorbet/rbi/annotations/.gitattributes +1 -0
  35. data/sorbet/rbi/annotations/activesupport.rbi +495 -0
  36. data/sorbet/rbi/annotations/minitest.rbi +120 -0
  37. data/sorbet/rbi/annotations/mocha.rbi +34 -0
  38. data/sorbet/rbi/annotations/rainbow.rbi +269 -0
  39. data/sorbet/rbi/dsl/.gitattributes +1 -0
  40. data/sorbet/rbi/dsl/active_support/callbacks.rbi +21 -0
  41. data/sorbet/rbi/gems/.gitattributes +1 -0
  42. data/sorbet/rbi/gems/activesupport@8.1.2.rbi +20360 -0
  43. data/sorbet/rbi/gems/ast@2.4.3.rbi +585 -0
  44. data/sorbet/rbi/gems/base64@0.3.0.rbi +545 -0
  45. data/sorbet/rbi/gems/benchmark@0.4.1.rbi +619 -0
  46. data/sorbet/rbi/gems/bigdecimal@4.0.1.rbi +239 -0
  47. data/sorbet/rbi/gems/concurrent-ruby@1.3.6.rbi +11674 -0
  48. data/sorbet/rbi/gems/connection_pool@3.0.2.rbi +9 -0
  49. data/sorbet/rbi/gems/date@3.4.1.rbi +75 -0
  50. data/sorbet/rbi/gems/drb@2.2.3.rbi +1661 -0
  51. data/sorbet/rbi/gems/erb@5.0.2.rbi +878 -0
  52. data/sorbet/rbi/gems/erubi@1.13.1.rbi +155 -0
  53. data/sorbet/rbi/gems/factory_bot@6.5.6.rbi +2748 -0
  54. data/sorbet/rbi/gems/i18n@1.14.8.rbi +2359 -0
  55. data/sorbet/rbi/gems/io-console@0.8.1.rbi +9 -0
  56. data/sorbet/rbi/gems/json@2.12.2.rbi +2051 -0
  57. data/sorbet/rbi/gems/language_server-protocol@3.17.0.5.rbi +9 -0
  58. data/sorbet/rbi/gems/lint_roller@1.1.0.rbi +86 -0
  59. data/sorbet/rbi/gems/logger@1.7.0.rbi +963 -0
  60. data/sorbet/rbi/gems/minitest@5.25.5.rbi +1547 -0
  61. data/sorbet/rbi/gems/mocha@3.0.1.rbi +28 -0
  62. data/sorbet/rbi/gems/netrc@0.11.0.rbi +159 -0
  63. data/sorbet/rbi/gems/openssl@4.0.0.rbi +1908 -0
  64. data/sorbet/rbi/gems/parallel@1.27.0.rbi +291 -0
  65. data/sorbet/rbi/gems/parser@3.3.8.0.rbi +5535 -0
  66. data/sorbet/rbi/gems/pp@0.6.2.rbi +368 -0
  67. data/sorbet/rbi/gems/prettyprint@0.2.0.rbi +477 -0
  68. data/sorbet/rbi/gems/prism@1.5.1.rbi +42049 -0
  69. data/sorbet/rbi/gems/psych@5.2.6.rbi +2469 -0
  70. data/sorbet/rbi/gems/racc@1.8.1.rbi +160 -0
  71. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +403 -0
  72. data/sorbet/rbi/gems/rake@13.2.1.rbi +3033 -0
  73. data/sorbet/rbi/gems/rbi@0.3.3.rbi +6742 -0
  74. data/sorbet/rbi/gems/rbs@3.9.5.rbi +6978 -0
  75. data/sorbet/rbi/gems/rdoc@6.14.2.rbi +12688 -0
  76. data/sorbet/rbi/gems/regexp_parser@2.10.0.rbi +3795 -0
  77. data/sorbet/rbi/gems/reline@0.6.2.rbi +2441 -0
  78. data/sorbet/rbi/gems/rexml@3.4.1.rbi +5240 -0
  79. data/sorbet/rbi/gems/rubocop-ast@1.45.1.rbi +7721 -0
  80. data/sorbet/rbi/gems/rubocop@1.76.1.rbi +62517 -0
  81. data/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +1318 -0
  82. data/sorbet/rbi/gems/ruby2_keywords@0.0.5.rbi +9 -0
  83. data/sorbet/rbi/gems/securerandom@0.4.1.rbi +75 -0
  84. data/sorbet/rbi/gems/spoom@1.6.3.rbi +6985 -0
  85. data/sorbet/rbi/gems/stringio@3.1.7.rbi +9 -0
  86. data/sorbet/rbi/gems/tapioca@0.16.11.rbi +3628 -0
  87. data/sorbet/rbi/gems/thor@1.4.0.rbi +4399 -0
  88. data/sorbet/rbi/gems/tzinfo@2.0.6.rbi +5918 -0
  89. data/sorbet/rbi/gems/unicode-display_width@3.1.4.rbi +132 -0
  90. data/sorbet/rbi/gems/unicode-emoji@4.0.4.rbi +251 -0
  91. data/sorbet/rbi/gems/uri@1.1.1.rbi +2405 -0
  92. data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +435 -0
  93. data/sorbet/rbi/gems/yard@0.9.37.rbi +18379 -0
  94. data/sorbet/rbi/shims/byebug.rbi +6 -0
  95. data/sorbet/rbi/shims/client.rbi +22 -0
  96. data/sorbet/rbi/todo.rbi +7 -0
  97. data/sorbet/tapioca/config.yml +13 -0
  98. data/sorbet/tapioca/require.rb +4 -0
  99. metadata +137 -0
@@ -0,0 +1,57 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Sofia
5
+ class Response
6
+
7
+ #: Integer
8
+ attr_reader :status
9
+
10
+ #: Hash[String, String]
11
+ attr_reader :headers
12
+
13
+ #: String?
14
+ attr_reader :raw_body
15
+
16
+ #: Sofia::Request
17
+ attr_reader :request
18
+
19
+ #: (status: Integer, headers: Hash[String, String], raw_body: String?, request: Sofia::Request) -> void
20
+ def initialize(status:, headers:, raw_body:, request:)
21
+ @status = status
22
+ @headers = headers
23
+ @raw_body = raw_body
24
+ @request = request
25
+ end
26
+
27
+ #: -> bool
28
+ def success?
29
+ (200..299).include?(@status)
30
+ end
31
+
32
+ #: -> bool
33
+ def failure?
34
+ !success?
35
+ end
36
+
37
+ #: -> JSONValue?
38
+ def body
39
+ body = raw_body
40
+ return unless body
41
+
42
+ JSON.parse(body)
43
+ rescue JSON::ParserError
44
+ raise Sofia::Error::InvalidJSON
45
+ end
46
+
47
+ #: -> bool
48
+ def client_error?
49
+ (400..499).include?(@status)
50
+ end
51
+
52
+ #: -> bool
53
+ def server_error?
54
+ (500..599).include?(@status)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,19 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Sofia
5
+ # JSONValue represents any valid JSON data
6
+ JSONValue = T.type_alias do
7
+ T.any(
8
+ String,
9
+ Integer,
10
+ Float,
11
+ TrueClass,
12
+ FalseClass,
13
+ NilClass,
14
+ T::Array[T.untyped],
15
+ T::Hash[String, T.untyped],
16
+ )
17
+ end
18
+
19
+ end
@@ -0,0 +1,39 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Sofia
5
+ module Types
6
+ class Adapter
7
+
8
+ #: singleton(Sofia::Adapter::Base)
9
+ attr_reader :klass
10
+
11
+ #: (?untyped adapter) -> void
12
+ def initialize(adapter = :net_http)
13
+ name, klass = validate_and_set(adapter || :net_http)
14
+ @name = T.let(name, Symbol)
15
+ @klass = T.let(klass, T.class_of(Sofia::Adapter::Base))
16
+ end
17
+
18
+ #: -> Symbol
19
+ def to_sym
20
+ @name
21
+ end
22
+
23
+ private
24
+
25
+ #: (untyped) -> [Symbol, singleton(Sofia::Adapter::Base)]
26
+ def validate_and_set(adapter)
27
+ case adapter&.to_sym
28
+ when :net_http
29
+ [:net_http, Sofia::Adapter::NetHTTP]
30
+ else
31
+ Kernel.raise Sofia::Error::ArgumentError, "unknown adapter #{adapter}"
32
+ end
33
+
34
+ rescue NoMethodError
35
+ Kernel.raise Sofia::Error::ArgumentError, "unknown adapter #{adapter}"
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,33 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Sofia
5
+ module Types
6
+ class BaseUrl
7
+
8
+ #: (untyped base_url) -> void
9
+ def initialize(base_url)
10
+ @base_url = validate_and_normalize(base_url).freeze #: String
11
+ end
12
+
13
+ #: -> String
14
+ def to_s
15
+ @base_url
16
+ end
17
+
18
+ private
19
+
20
+ #: (untyped) -> String
21
+ def validate_and_normalize(base_url)
22
+ unless base_url.is_a?(String) && !base_url.strip.empty?
23
+ raise Sofia::Error::ArgumentError, 'base_url must be a non-empty String'
24
+ end
25
+
26
+ normalized = base_url.chomp('/')
27
+ return normalized if normalized.start_with?('http://', 'https://')
28
+
29
+ "https://#{normalized}"
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,60 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Sofia
5
+ module Types
6
+ class Body
7
+
8
+ #: Hash[String, untyped]
9
+ attr_reader :body
10
+
11
+ #: (?untyped? body) -> void
12
+ def initialize(body = nil)
13
+ @body = validate_and_normalize(body) #: Hash[String, untyped]
14
+ end
15
+
16
+ #: -> Hash[String, untyped]
17
+ def to_h
18
+ @body.dup
19
+ end
20
+
21
+ private
22
+
23
+ #: (untyped? body) -> Hash[String, untyped]
24
+ def validate_and_normalize(body)
25
+ return {} unless body
26
+
27
+ Kernel.raise Sofia::Error::ArgumentError, 'body must be a Hash' unless body.is_a?(Hash)
28
+ body.each_with_object({}) do |(key, value), acc|
29
+ next if value.nil?
30
+
31
+ acc[key.to_s] =
32
+ case value
33
+ when Hash
34
+ validate_and_normalize(value)
35
+ when Array
36
+ validate_and_normalize_array(value)
37
+ else
38
+ value
39
+ end
40
+ end
41
+ end
42
+
43
+ #: (Array[untyped]) -> Array[untyped]
44
+ def validate_and_normalize_array(array)
45
+ array.each_with_object([]) do |value, acc|
46
+ next if value.nil?
47
+
48
+ acc << case value
49
+ when Hash
50
+ validate_and_normalize(value)
51
+ when Array
52
+ validate_and_normalize_array(value)
53
+ else
54
+ value
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,56 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Sofia
5
+ module Types
6
+ class Headers
7
+
8
+ #: (?untyped headers) -> void
9
+ def initialize(headers = {})
10
+ defaults = { 'Content-Type' => 'application/json', 'Accept' => 'application/json' }
11
+ headers = validate_and_normalize(headers)
12
+ @headers = defaults.merge(headers) #: Hash[String, String]
13
+ end
14
+
15
+ #: (untyped key) -> void
16
+ def [](key)
17
+ @headers[key.to_s]
18
+ end
19
+
20
+ #: (untyped key, untyped value) -> void
21
+ def []=(key, value)
22
+ @headers[key.to_s] = value.to_s
23
+ end
24
+
25
+ #: -> Hash[String, String]
26
+ def to_h
27
+ @headers.dup
28
+ end
29
+
30
+ #: () { ([String, String]) -> untyped } -> Hash[String, String]
31
+ def each(&block) # rubocop:disable Naming/BlockForwarding
32
+ @headers.each(&block) # rubocop:disable Naming/BlockForwarding
33
+ end
34
+
35
+ private
36
+
37
+ #: (untyped) -> Hash[String, String]
38
+ def validate_and_normalize(headers)
39
+ Kernel.raise Sofia::Error::ArgumentError, 'headers must be a Hash' unless headers.is_a?(Hash)
40
+ normalized = {}
41
+
42
+ headers.each do |key, value|
43
+ next if value.nil?
44
+
45
+ if value.is_a?(Hash) || value.is_a?(Array)
46
+ Kernel.raise Sofia::Error::ArgumentError, "invalid header value for #{key}"
47
+ end
48
+
49
+ normalized[key.to_s] = value.to_s
50
+ end
51
+
52
+ normalized
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,56 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Sofia
5
+ module Types
6
+ class Params
7
+ extend T::Sig
8
+
9
+
10
+ #: (?untyped params) -> void
11
+ def initialize(params = {}) # rubocop:disable Style/OptionHash
12
+ @params = validate_and_normalize(params || {}) #: Hash[String, String]
13
+ end
14
+
15
+ #: (untyped key) -> void
16
+ def [](key)
17
+ @params[key.to_s]
18
+ end
19
+
20
+ #: (untyped key, untyped value) -> void
21
+ def []=(key, value)
22
+ @params[key.to_s] = value.to_s
23
+ end
24
+
25
+ #: -> Hash[String, String]
26
+ def to_h
27
+ @params.dup
28
+ end
29
+
30
+ #: -> String
31
+ def to_s
32
+ URI.encode_www_form(@params)
33
+ end
34
+
35
+ private
36
+
37
+ #: (untyped) -> Hash[String, String]
38
+ def validate_and_normalize(headers)
39
+ Kernel.raise Sofia::Error::ArgumentError, 'params must be a Hash' unless headers.is_a?(Hash)
40
+ normalized = {}
41
+
42
+ headers.each do |key, value|
43
+ next if value.nil?
44
+
45
+ if value.is_a?(Hash) || value.is_a?(Array)
46
+ Kernel.raise Sofia::Error::ArgumentError, "invalid param value for #{key}"
47
+ end
48
+
49
+ normalized[key.to_s] = value.to_s
50
+ end
51
+
52
+ normalized
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,28 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Sofia
5
+ module Types
6
+ class Path
7
+
8
+ #: (?untyped path) -> void
9
+ def initialize(path = '/')
10
+ @path = validate_and_normalize(path).freeze #: String
11
+ end
12
+
13
+ #: -> String
14
+ def to_s
15
+ @path
16
+ end
17
+
18
+ private
19
+
20
+ #: (untyped) -> String
21
+ def validate_and_normalize(path)
22
+ Kernel.raise Sofia::Error::ArgumentError, 'path must be a String' unless path.is_a?(String)
23
+
24
+ path.start_with?('/') ? path : "/#{path}"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,13 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Sofia
5
+ module Types; end
6
+ end
7
+
8
+ require_relative 'types/adapter'
9
+ require_relative 'types/base_url'
10
+ require_relative 'types/headers'
11
+ require_relative 'types/body'
12
+ require_relative 'types/params'
13
+ require_relative 'types/path'
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sofia
4
+ VERSION = '0.1.0'
5
+ end
data/lib/sofia.rb ADDED
@@ -0,0 +1,25 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'uri'
5
+ require 'net/http'
6
+ require 'json'
7
+ require 'sorbet-runtime'
8
+ require 'byebug'
9
+
10
+ require_relative 'sofia/version'
11
+ require_relative 'sofia/request'
12
+ require_relative 'sofia/response'
13
+ require_relative 'sofia/adapter'
14
+ require_relative 'sofia/client'
15
+ require_relative 'sofia/error'
16
+ require_relative 'sofia/types'
17
+
18
+ module Sofia
19
+ class << self
20
+ #: (base_url: untyped, ?adapter: untyped) -> Sofia::Client
21
+ def new(base_url:, adapter: nil)
22
+ Client.new(base_url: base_url, adapter: adapter)
23
+ end
24
+ end
25
+ end
data/sig/sofia.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Sofia
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
data/sorbet/config ADDED
@@ -0,0 +1,6 @@
1
+ --dir
2
+ .
3
+ --ignore=tmp/
4
+ --ignore=vendor/
5
+ --enable-experimental-rbs-comments
6
+ --enable-experimental-requires-ancestor
@@ -0,0 +1 @@
1
+ **/*.rbi linguist-vendored=true