twelvedata_ruby 0.1.1

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 (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/CHANGELOG.md +5 -0
  4. data/CODE_OF_CONDUCT.md +84 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.lock +80 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +125 -0
  9. data/Rakefile +12 -0
  10. data/bin/console +22 -0
  11. data/bin/setup +8 -0
  12. data/doc/TwelvedataRuby.html +353 -0
  13. data/doc/TwelvedataRuby/BadRequestResponseError.html +178 -0
  14. data/doc/TwelvedataRuby/Client.html +1443 -0
  15. data/doc/TwelvedataRuby/Endpoint.html +1478 -0
  16. data/doc/TwelvedataRuby/EndpointError.html +247 -0
  17. data/doc/TwelvedataRuby/EndpointNameError.html +167 -0
  18. data/doc/TwelvedataRuby/EndpointParametersKeysError.html +167 -0
  19. data/doc/TwelvedataRuby/EndpointRequiredParametersError.html +167 -0
  20. data/doc/TwelvedataRuby/Error.html +318 -0
  21. data/doc/TwelvedataRuby/ForbiddenResponseError.html +178 -0
  22. data/doc/TwelvedataRuby/InternalServerResponseErro.html +178 -0
  23. data/doc/TwelvedataRuby/NotFoundResponseError.html +178 -0
  24. data/doc/TwelvedataRuby/PageNotFoundResponseError.html +178 -0
  25. data/doc/TwelvedataRuby/ParameterTooLongResponseError.html +178 -0
  26. data/doc/TwelvedataRuby/Request.html +683 -0
  27. data/doc/TwelvedataRuby/Response.html +1622 -0
  28. data/doc/TwelvedataRuby/ResponseError.html +565 -0
  29. data/doc/TwelvedataRuby/TooManyRequestsResponseError.html +178 -0
  30. data/doc/TwelvedataRuby/UnauthorizedResponseError.html +178 -0
  31. data/doc/TwelvedataRuby/Utils.html +503 -0
  32. data/doc/_index.html +315 -0
  33. data/doc/class_list.html +51 -0
  34. data/doc/css/common.css +1 -0
  35. data/doc/css/full_list.css +58 -0
  36. data/doc/css/style.css +497 -0
  37. data/doc/file.README.html +194 -0
  38. data/doc/file_list.html +56 -0
  39. data/doc/frames.html +17 -0
  40. data/doc/index.html +194 -0
  41. data/doc/js/app.js +314 -0
  42. data/doc/js/full_list.js +216 -0
  43. data/doc/js/jquery.js +4 -0
  44. data/doc/method_list.html +707 -0
  45. data/doc/top-level-namespace.html +110 -0
  46. data/lib/twelvedata_ruby.rb +43 -0
  47. data/lib/twelvedata_ruby/client.rb +148 -0
  48. data/lib/twelvedata_ruby/endpoint.rb +271 -0
  49. data/lib/twelvedata_ruby/error.rb +90 -0
  50. data/lib/twelvedata_ruby/request.rb +54 -0
  51. data/lib/twelvedata_ruby/response.rb +132 -0
  52. data/lib/twelvedata_ruby/utils.rb +36 -0
  53. data/twelvedata_ruby.gemspec +37 -0
  54. metadata +201 -0
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TwelvedataRuby
4
+ class Error < StandardError
5
+ DEFAULT_MSGS = {
6
+ "EndpointError" => "Endpoint is not valid. %{invalid}",
7
+ "EndpointNameError" => "`%{invalid}` is not a correct endpoint. Valid values are: `%{valid_names}`",
8
+ "EndpointParametersKeysError" => "Invalid parameters found: `%{invalid}`. Valid parameters for `%{name}` "\
9
+ "endpoint are: `%{parameters}`. Please see: `Twelvedata::Endpoint#parameters` for more details",
10
+ "EndpointRequiredParametersError" => "Missing values for required parameters: `%{invalid}`. "\
11
+ "`%{name}` endpoint required parameters are: `%{required}`.",
12
+ "ResponseError" => "Encountered an error from the response"
13
+ }.freeze
14
+
15
+ attr_reader :attrs
16
+
17
+ def initialize(args={})
18
+ @attrs = args[:attrs] || {}
19
+ super((args[:message] || DEFAULT_MSGS[Utils.demodulize(self.class)]) % @attrs)
20
+ end
21
+ end
22
+
23
+ class EndpointError < Error
24
+ def initialize(**args)
25
+ endpoint = args[:endpoint]
26
+ super(
27
+ attrs: {
28
+ name: endpoint.name,
29
+ invalid: args[:invalid],
30
+ valid_names: endpoint.class.names.join(", "),
31
+ parameters: endpoint&.parameters_keys&.send(:join, ", "),
32
+ required: endpoint&.required_parameters&.send(:join, ", ")
33
+ }
34
+ )
35
+ end
36
+ end
37
+
38
+ class EndpointNameError < EndpointError; end
39
+
40
+ class EndpointParametersKeysError < EndpointError; end
41
+
42
+ class EndpointRequiredParametersError < EndpointError; end
43
+
44
+ class ResponseError < Error
45
+ API_ERROR_CODES_MAP = {
46
+ 400 => "BadRequestResponseError",
47
+ 401 => "UnauthorizedResponseError",
48
+ 403 => "ForbiddenResponseError",
49
+ 404 => "NotFoundResponseError",
50
+ 414 => "ParameterTooLongResponseError",
51
+ 429 => "TooManyRequestsResponseError",
52
+ 500 => "InternalServerResponseError"
53
+ }.freeze
54
+ HTTP_ERROR_CODES_MAP = {
55
+ 404 => "PageNotFoundResponseError",
56
+ }.freeze
57
+
58
+ def self.error_code_klass(code, error_type=:api)
59
+ error_type = :api unless %i[api http].member?(error_type)
60
+
61
+ TwelvedataRuby::ResponseError.const_get("#{error_type.upcase}_ERROR_CODES_MAP")[code]
62
+ end
63
+
64
+ attr_reader :json, :code, :request
65
+
66
+ def initialize(json:, request:, attrs: nil, message: nil, code: nil)
67
+ @json = json.is_a?(Hash) ? json : {}
68
+ @code = code || @json[:code]
69
+ @attrs = attrs || {}
70
+ @request = request
71
+ super(attrs: @attrs, message: "#{@json[:message] || message}")
72
+ end
73
+ end
74
+
75
+ class BadRequestResponseError < ResponseError; end
76
+
77
+ class UnauthorizedResponseError < ResponseError; end
78
+
79
+ class ForbiddenResponseError < ResponseError; end
80
+
81
+ class NotFoundResponseError < ResponseError; end
82
+
83
+ class PageNotFoundResponseError < ResponseError; end
84
+
85
+ class ParameterTooLongResponseError < ResponseError; end
86
+
87
+ class TooManyRequestsResponseError < ResponseError; end
88
+
89
+ class InternalServerResponseErro < ResponseError; end
90
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+ module TwelvedataRuby
5
+ class Request
6
+ extend Forwardable
7
+
8
+ DEFAULT_HTTP_VERB = :get
9
+
10
+ attr_reader :endpoint
11
+
12
+ def initialize(name, **query_params)
13
+ self.endpoint = Endpoint.new(name, **query_params)
14
+ end
15
+ def_delegators :endpoint, :name, :valid?, :query_params, :errors
16
+
17
+ def fetch
18
+ Client.instance.fetch(self)
19
+ end
20
+
21
+ def http_verb
22
+ return_nil_unless_valid { endpoint.definition[:http_verb] || DEFAULT_HTTP_VERB }
23
+ end
24
+
25
+ def params
26
+ {params: endpoint.query_params}
27
+ end
28
+
29
+ def relative_url
30
+ return_nil_unless_valid { name.to_s }
31
+ end
32
+
33
+ def full_url
34
+ return_nil_unless_valid { "#{Client.origin[:origin]}/#{relative_url}" }
35
+ end
36
+
37
+ def to_h
38
+ return_nil_unless_valid { {http_verb: http_verb, relative_url: relative_url}.merge(params: params) }
39
+ end
40
+
41
+ def to_a
42
+ return_nil_unless_valid { [http_verb, relative_url, params] }
43
+ end
44
+ alias build to_a
45
+
46
+ private
47
+
48
+ attr_writer :endpoint
49
+
50
+ def return_nil_unless_valid(&block)
51
+ Utils.return_nil_unless_true(valid?) { block.call }
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "csv"
4
+ module TwelvedataRuby
5
+ class Response
6
+ CSV_COL_SEP = ";"
7
+ HTTP_STATUSES = {http_error: (400..600), success: (200..299)}.freeze
8
+ CONTENT_TYPE_HANDLERS = {
9
+ json: {parser: :json_parser, dumper: :json_dumper},
10
+ csv: {parser: :csv_parser, dumper: :csv_dumper},
11
+ plain: {parser: :plain_parser, dumper: :to_s}
12
+ }.freeze
13
+
14
+ class << self
15
+ def resolve(http_response, request)
16
+ if http_status_codes.member?(http_response.status)
17
+ new(http_response: http_response, request: request)
18
+ else
19
+ resolve_error(http_response, request)
20
+ end
21
+ end
22
+
23
+ def resolve_error(http_response, request)
24
+ error_attribs = if http_response.respond_to?(:error) && http_response.error
25
+ {message: http_response.error.message, code: http_response.error.class.name}
26
+ else
27
+ {message: http_response.body.to_s, code: http_response.status}
28
+ end
29
+ TwelvedataRuby::ResponseError.new(json: (error_attribs || {}), request: request)
30
+ end
31
+
32
+ def http_status_codes
33
+ @http_status_codes ||= HTTP_STATUSES.values.map(&:to_a).flatten
34
+ end
35
+ end
36
+
37
+ attr_reader :http_response, :headers, :body, :request
38
+
39
+ def initialize(http_response:, request:, headers: nil, body: nil)
40
+ self.http_response = http_response
41
+ self.headers = headers || http_response.headers
42
+ self.body = body || http_response.body
43
+ end
44
+
45
+ def attachment_filename
46
+ return nil unless headers["content-disposition"]
47
+
48
+ @attachment_filename ||= headers["content-disposition"].split("filename=").last.delete("\"")
49
+ end
50
+
51
+ def body_parser
52
+ CONTENT_TYPE_HANDLERS[content_type][:parser]
53
+ end
54
+
55
+ def content_type
56
+ @content_type ||= headers["content-type"].match(%r{^.+/([a-z]+).*$})&.send(:[], 1)&.to_sym
57
+ end
58
+
59
+ def csv_parser
60
+ chunk_parsed = nil
61
+ opts = {col_sep: CSV_COL_SEP}
62
+ body.each do |chk|
63
+ chunk_parsed = chunk_parsed&.send(:push, CSV.parse(chk, **opts)) || CSV.parse(chk, **opts.merge(headers: true))
64
+ end
65
+ chunk_parsed
66
+ end
67
+
68
+ def csv_dumper
69
+ parsed_body.is_a?(CSV::Table) ? parsed_body.to_csv : nil
70
+ end
71
+
72
+ def dumped_parsed_body
73
+ @dumped_parsed_body ||=
74
+ parsed_body.respond_to?(parsed_body_dumper) ? parsed_body.send(parsed_body_dumper) : send(parsed_body_dumper)
75
+ end
76
+
77
+ def error
78
+ klass_name = ResponseError.error_code_klass(status_code, success_http_status? ? :api : :http)
79
+ return unless klass_name
80
+ TwelvedataRuby.const_get(klass_name)
81
+ .new(json: parsed_body, request: request, code: status_code, message: parsed_body)
82
+ end
83
+
84
+ def http_status_code
85
+ http_response&.status
86
+ end
87
+
88
+ def json_dumper
89
+ parsed_body.is_a?(Hash) ? JSON.dump(parsed_body) : nil
90
+ end
91
+
92
+ def json_parser
93
+ JSON.parse(body, symbolize_names: true)
94
+ end
95
+
96
+ def parsed_body
97
+ @parsed_body ||= send(body_parser)
98
+ end
99
+
100
+ def parsed_body_dumper
101
+ CONTENT_TYPE_HANDLERS[content_type][:dumper]
102
+ end
103
+
104
+ def plain_parser
105
+ body.to_s
106
+ end
107
+
108
+ def status_code
109
+ @status_code ||= parsed_body.is_a?(Hash) ? parsed_body[:code] : http_status_code
110
+ end
111
+
112
+ def success_http_status?
113
+ @success_http_status ||= HTTP_STATUSES[:success].member?(http_status_code) || false
114
+ end
115
+
116
+ def to_disk_file(file_fullpath=attachment_filename)
117
+ return nil unless file_fullpath
118
+
119
+ begin
120
+ file = File.open(file_fullpath, "w")
121
+ file.puts dumped_parsed_body
122
+ file
123
+ ensure
124
+ file&.close
125
+ end
126
+ end
127
+
128
+ private
129
+
130
+ attr_writer :http_response, :headers, :body, :request
131
+ end
132
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TwelvedataRuby
4
+ module Utils
5
+ def self.demodulize(obj)
6
+ obj.to_s.gsub(/^.+::/, "")
7
+ end
8
+
9
+ # Converts a string to integer an all integer or nothing
10
+ def self.to_d(obj, default_value=nil)
11
+ return obj if obj.is_a?(Integer)
12
+
13
+ obj.to_s.match(/^\d+$/) {|m| Integer(m[0]) } || default_value
14
+ end
15
+
16
+ def self.camelize(str)
17
+ str.to_s.split("_").map(&:capitalize).join
18
+ end
19
+
20
+ def self.empty_to_nil(obj)
21
+ !obj.nil? && obj.empty? ? nil : obj
22
+ end
23
+
24
+ def self.to_a(objects)
25
+ objects.is_a?(Array) ? objects : [objects]
26
+ end
27
+
28
+ def self.call_block_if_truthy(truthy_val, return_this=nil, &block)
29
+ truthy_val ? block.call : return_this
30
+ end
31
+
32
+ def self.return_nil_unless_true(is_true, &block)
33
+ call_block_if_truthy(is_true == true) { block.call }
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/twelvedata_ruby"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "twelvedata_ruby"
7
+ spec.version = TwelvedataRuby::VERSION
8
+ spec.authors = ["Kendo Camajalan, KCD"]
9
+ spec.email = ["ken.d.camajalan@pm.me"]
10
+ spec.summary = "A Ruby client library that provides helping hands in accessing Twelve Data API's stock, \
11
+ forex, crypto, and other financial data"
12
+ spec.description = "A Ruby gem for accessing Twelve Data's API."
13
+ spec.homepage = "https://github.com/kanroyalhigh/twelvedata_ruby"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
16
+
17
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = "https://github.com/kanroyalhigh/twelvedata_ruby"
21
+ spec.metadata["changelog_uri"] = "https://github.com/kanroyalhigh/twelvedata_ruby/CHANGELOG.md"
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files -z`.split("\x0").reject {|f| f.match(%r{\A(?:test|spec|features)/}) }
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) {|f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+ spec.add_dependency "httpx", "~> 0.14.5"
32
+ spec.add_development_dependency "rake", "~> 13.0", ">= 13.0.3"
33
+ spec.add_development_dependency "rspec", "~> 3.10.0"
34
+ spec.add_development_dependency "rubocop", "~> 1.17.0"
35
+ spec.add_development_dependency "simplecov", "~> 0.21", ">= 0.21.2"
36
+ spec.add_development_dependency "webmock", "~> 3.13", ">= 3.13.0"
37
+ end
metadata ADDED
@@ -0,0 +1,201 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: twelvedata_ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Kendo Camajalan, KCD
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-07-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: httpx
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.14.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.14.5
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 13.0.3
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '13.0'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 13.0.3
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 3.10.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: 3.10.0
61
+ - !ruby/object:Gem::Dependency
62
+ name: rubocop
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: 1.17.0
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: 1.17.0
75
+ - !ruby/object:Gem::Dependency
76
+ name: simplecov
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.21'
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: 0.21.2
85
+ type: :development
86
+ prerelease: false
87
+ version_requirements: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: '0.21'
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: 0.21.2
95
+ - !ruby/object:Gem::Dependency
96
+ name: webmock
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '3.13'
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: 3.13.0
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '3.13'
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: 3.13.0
115
+ description: A Ruby gem for accessing Twelve Data's API.
116
+ email:
117
+ - ken.d.camajalan@pm.me
118
+ executables: []
119
+ extensions: []
120
+ extra_rdoc_files: []
121
+ files:
122
+ - ".gitignore"
123
+ - CHANGELOG.md
124
+ - CODE_OF_CONDUCT.md
125
+ - Gemfile
126
+ - Gemfile.lock
127
+ - LICENSE.txt
128
+ - README.md
129
+ - Rakefile
130
+ - bin/console
131
+ - bin/setup
132
+ - doc/TwelvedataRuby.html
133
+ - doc/TwelvedataRuby/BadRequestResponseError.html
134
+ - doc/TwelvedataRuby/Client.html
135
+ - doc/TwelvedataRuby/Endpoint.html
136
+ - doc/TwelvedataRuby/EndpointError.html
137
+ - doc/TwelvedataRuby/EndpointNameError.html
138
+ - doc/TwelvedataRuby/EndpointParametersKeysError.html
139
+ - doc/TwelvedataRuby/EndpointRequiredParametersError.html
140
+ - doc/TwelvedataRuby/Error.html
141
+ - doc/TwelvedataRuby/ForbiddenResponseError.html
142
+ - doc/TwelvedataRuby/InternalServerResponseErro.html
143
+ - doc/TwelvedataRuby/NotFoundResponseError.html
144
+ - doc/TwelvedataRuby/PageNotFoundResponseError.html
145
+ - doc/TwelvedataRuby/ParameterTooLongResponseError.html
146
+ - doc/TwelvedataRuby/Request.html
147
+ - doc/TwelvedataRuby/Response.html
148
+ - doc/TwelvedataRuby/ResponseError.html
149
+ - doc/TwelvedataRuby/TooManyRequestsResponseError.html
150
+ - doc/TwelvedataRuby/UnauthorizedResponseError.html
151
+ - doc/TwelvedataRuby/Utils.html
152
+ - doc/_index.html
153
+ - doc/class_list.html
154
+ - doc/css/common.css
155
+ - doc/css/full_list.css
156
+ - doc/css/style.css
157
+ - doc/file.README.html
158
+ - doc/file_list.html
159
+ - doc/frames.html
160
+ - doc/index.html
161
+ - doc/js/app.js
162
+ - doc/js/full_list.js
163
+ - doc/js/jquery.js
164
+ - doc/method_list.html
165
+ - doc/top-level-namespace.html
166
+ - lib/twelvedata_ruby.rb
167
+ - lib/twelvedata_ruby/client.rb
168
+ - lib/twelvedata_ruby/endpoint.rb
169
+ - lib/twelvedata_ruby/error.rb
170
+ - lib/twelvedata_ruby/request.rb
171
+ - lib/twelvedata_ruby/response.rb
172
+ - lib/twelvedata_ruby/utils.rb
173
+ - twelvedata_ruby.gemspec
174
+ homepage: https://github.com/kanroyalhigh/twelvedata_ruby
175
+ licenses:
176
+ - MIT
177
+ metadata:
178
+ homepage_uri: https://github.com/kanroyalhigh/twelvedata_ruby
179
+ source_code_uri: https://github.com/kanroyalhigh/twelvedata_ruby
180
+ changelog_uri: https://github.com/kanroyalhigh/twelvedata_ruby/CHANGELOG.md
181
+ post_install_message:
182
+ rdoc_options: []
183
+ require_paths:
184
+ - lib
185
+ required_ruby_version: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: 2.4.0
190
+ required_rubygems_version: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ requirements: []
196
+ rubygems_version: 3.2.15
197
+ signing_key:
198
+ specification_version: 4
199
+ summary: A Ruby client library that provides helping hands in accessing Twelve Data
200
+ API's stock, forex, crypto, and other financial data
201
+ test_files: []