readme-metrics 2.0.1 → 2.2.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +72 -0
  3. data/Gemfile +9 -9
  4. data/Gemfile.lock +28 -34
  5. data/LICENSE +1 -1
  6. data/Makefile +20 -0
  7. data/README.md +21 -102
  8. data/Rakefile +2 -2
  9. data/bin/console +3 -3
  10. data/examples/metrics-rails/.gitattributes +7 -0
  11. data/examples/metrics-rails/.gitignore +20 -0
  12. data/examples/metrics-rails/.ruby-version +1 -0
  13. data/examples/metrics-rails/Gemfile +31 -0
  14. data/examples/metrics-rails/Gemfile.lock +189 -0
  15. data/examples/metrics-rails/README.md +38 -0
  16. data/examples/metrics-rails/Rakefile +6 -0
  17. data/examples/metrics-rails/app/controllers/application_controller.rb +2 -0
  18. data/examples/metrics-rails/app/controllers/metrics_controller.rb +34 -0
  19. data/examples/metrics-rails/app/models/application_record.rb +3 -0
  20. data/examples/metrics-rails/bin/bundle +116 -0
  21. data/examples/metrics-rails/bin/rails +4 -0
  22. data/examples/metrics-rails/bin/rake +4 -0
  23. data/examples/metrics-rails/bin/setup +33 -0
  24. data/examples/metrics-rails/config/application.rb +52 -0
  25. data/examples/metrics-rails/config/boot.rb +3 -0
  26. data/examples/metrics-rails/config/credentials.yml.enc +1 -0
  27. data/examples/metrics-rails/config/database.yml +25 -0
  28. data/examples/metrics-rails/config/environment.rb +5 -0
  29. data/examples/metrics-rails/config/environments/development.rb +56 -0
  30. data/examples/metrics-rails/config/environments/production.rb +68 -0
  31. data/examples/metrics-rails/config/environments/test.rb +50 -0
  32. data/examples/metrics-rails/config/initializers/cors.rb +16 -0
  33. data/examples/metrics-rails/config/initializers/filter_parameter_logging.rb +8 -0
  34. data/examples/metrics-rails/config/initializers/inflections.rb +16 -0
  35. data/examples/metrics-rails/config/locales/en.yml +33 -0
  36. data/examples/metrics-rails/config/puma.rb +43 -0
  37. data/examples/metrics-rails/config/routes.rb +5 -0
  38. data/examples/metrics-rails/config.ru +6 -0
  39. data/examples/metrics-rails/db/seeds.rb +7 -0
  40. data/examples/metrics-rails/public/robots.txt +1 -0
  41. data/lib/readme/content_type_helper.rb +6 -6
  42. data/lib/readme/errors.rb +7 -5
  43. data/lib/readme/filter.rb +2 -2
  44. data/lib/readme/har/collection.rb +3 -1
  45. data/lib/readme/har/request_serializer.rb +14 -14
  46. data/lib/readme/har/response_serializer.rb +3 -3
  47. data/lib/readme/har/serializer.rb +12 -10
  48. data/lib/readme/http_request.rb +18 -9
  49. data/lib/readme/http_response.rb +7 -7
  50. data/lib/readme/metrics/version.rb +3 -1
  51. data/lib/readme/metrics.rb +33 -42
  52. data/lib/readme/payload.rb +14 -6
  53. data/lib/readme/request_queue.rb +4 -4
  54. data/lib/readme/webhook.rb +42 -0
  55. data/readme-metrics.gemspec +14 -15
  56. metadata +38 -33
  57. data/SECURITY.md +0 -12
@@ -3,12 +3,12 @@ module Readme
3
3
  # Assumes the includer has a `content_type` method defined.
4
4
 
5
5
  JSON_MIME_TYPES = [
6
- "application/json",
7
- "application/x-json",
8
- "text/json",
9
- "text/x-json",
10
- "+json"
11
- ]
6
+ 'application/json',
7
+ 'application/x-json',
8
+ 'text/json',
9
+ 'text/x-json',
10
+ '+json'
11
+ ].freeze
12
12
 
13
13
  def json?
14
14
  JSON_MIME_TYPES.any? { |mime_type| content_type.include?(mime_type) }
data/lib/readme/errors.rb CHANGED
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Readme
2
4
  class Errors
3
- API_KEY_ERROR = "Missing API Key"
4
- REJECT_PARAMS_ERROR = "The `reject_params` option must be an array of strings"
5
- ALLOW_ONLY_ERROR = "The `allow_only` option must be an array of strings"
6
- BUFFER_LENGTH_ERROR = "The `buffer_length` must be an Integer"
7
- DEVELOPMENT_ERROR = "The `development` option must be a boolean"
5
+ API_KEY_ERROR = 'Missing API Key'
6
+ REJECT_PARAMS_ERROR = 'The `reject_params` option must be an array of strings'
7
+ ALLOW_ONLY_ERROR = 'The `allow_only` option must be an array of strings'
8
+ BUFFER_LENGTH_ERROR = 'The `buffer_length` must be an Integer'
9
+ DEVELOPMENT_ERROR = 'The `development` option must be a boolean'
8
10
  LOGGER_ERROR = <<~MESSAGE
9
11
  The `logger` option must be class that responds to the following messages:
10
12
  :unkown, :fatal, :error, :warn, :info, :debug, :level
data/lib/readme/filter.rb CHANGED
@@ -15,7 +15,7 @@ module Readme
15
15
  def self.redact(rejected_params)
16
16
  rejected_params.each_with_object({}) do |(k, v), hash|
17
17
  # If it's a string then return the length of the redacted field
18
- hash[k.to_str] = "[REDACTED#{v.is_a?(String) ? " #{v.length}" : ""}]"
18
+ hash[k.to_str] = "[REDACTED#{v.is_a?(String) ? " #{v.length}" : ''}]"
19
19
  end
20
20
  end
21
21
 
@@ -65,7 +65,7 @@ module Readme
65
65
 
66
66
  class FilterArgsError < StandardError
67
67
  def initialize
68
- msg = "Can only supply either reject_params or allow_only, not both."
68
+ msg = 'Can only supply either reject_params or allow_only, not both.'
69
69
  super(msg)
70
70
  end
71
71
  end
@@ -1,3 +1,5 @@
1
+ require 'json'
2
+
1
3
  module Readme
2
4
  module Har
3
5
  class Collection
@@ -11,7 +13,7 @@ module Readme
11
13
  end
12
14
 
13
15
  def to_a
14
- filtered_hash.map { |name, value| {name: name, value: value} }
16
+ filtered_hash.map { |name, value| { name: name, value: value.is_a?(Hash) ? value.to_json : value } }
15
17
  end
16
18
 
17
19
  private
@@ -1,6 +1,6 @@
1
- require "cgi"
2
- require "readme/har/collection"
3
- require "readme/filter"
1
+ require 'cgi'
2
+ require 'readme/har/collection'
3
+ require 'readme/filter'
4
4
 
5
5
  module Readme
6
6
  module Har
@@ -18,7 +18,7 @@ module Readme
18
18
  httpVersion: @request.http_version,
19
19
  headers: Har::Collection.new(@filter, @request.headers).to_a,
20
20
  cookies: Har::Collection.new(@filter, @request.cookies).to_a,
21
- postData: postData,
21
+ postData: post_data,
22
22
  headersSize: -1,
23
23
  bodySize: @request.content_length
24
24
  }.compact
@@ -29,14 +29,14 @@ module Readme
29
29
  def url
30
30
  url = URI(@request.url)
31
31
  headers = @request.headers
32
- forward_proto = headers["X-Forwarded-Proto"]
33
- forward_host = headers["X-Forwarded-Host"]
32
+ forward_proto = headers['X-Forwarded-Proto']
33
+ forward_host = headers['X-Forwarded-Host']
34
34
  url.host = forward_host if forward_host.is_a?(String)
35
35
  url.scheme = forward_proto if forward_proto.is_a?(String)
36
36
  url.to_s
37
37
  end
38
38
 
39
- def postData
39
+ def post_data
40
40
  if @request.content_type.nil?
41
41
  nil
42
42
  elsif @request.form_data?
@@ -59,22 +59,22 @@ module Readme
59
59
  def request_body
60
60
  if @filter.pass_through?
61
61
  pass_through_body
62
- elsif is_form_urlencoded?
62
+ elsif form_urlencoded?
63
63
  form_urlencoded_body
64
- elsif is_json?
64
+ elsif json?
65
65
  json_body
66
66
  else
67
67
  @request.body
68
68
  end
69
69
  end
70
70
 
71
- def is_json?
72
- ["application/json", "application/x-json", "text/json", "text/x-json"]
73
- .include?(@request.content_type) || @request.content_type.include?("+json")
71
+ def json?
72
+ ['application/json', 'application/x-json', 'text/json', 'text/x-json']
73
+ .include?(@request.content_type) || @request.content_type.include?('+json')
74
74
  end
75
75
 
76
- def is_form_urlencoded?
77
- @request.content_type == "application/x-www-form-urlencoded"
76
+ def form_urlencoded?
77
+ @request.content_type == 'application/x-www-form-urlencoded'
78
78
  end
79
79
 
80
80
  def json_body
@@ -1,5 +1,5 @@
1
- require "rack/utils"
2
- require "readme/har/collection"
1
+ require 'rack/utils'
2
+ require 'readme/har/collection'
3
3
 
4
4
  module Readme
5
5
  module Har
@@ -37,7 +37,7 @@ module Readme
37
37
  end
38
38
 
39
39
  def empty_content
40
- {mimeType: "", size: 0}
40
+ { mimeType: '', size: 0 }
41
41
  end
42
42
 
43
43
  def json_content
@@ -1,13 +1,15 @@
1
- require "rack"
2
- require "readme/metrics"
3
- require "readme/har/request_serializer"
4
- require "readme/har/response_serializer"
5
- require "readme/har/collection"
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack'
4
+ require 'readme/metrics'
5
+ require 'readme/har/request_serializer'
6
+ require 'readme/har/response_serializer'
7
+ require 'readme/har/collection'
6
8
 
7
9
  module Readme
8
10
  module Har
9
11
  class Serializer
10
- HAR_VERSION = "1.2"
12
+ HAR_VERSION = '1.2'
11
13
 
12
14
  def initialize(request, response, start_time, end_time, filter)
13
15
  @http_request = request
@@ -17,7 +19,7 @@ module Readme
17
19
  @filter = filter
18
20
  end
19
21
 
20
- def to_json
22
+ def to_json(*_args)
21
23
  {
22
24
  log: {
23
25
  version: HAR_VERSION,
@@ -31,9 +33,9 @@ module Readme
31
33
 
32
34
  def creator
33
35
  {
34
- name: Readme::Metrics::SDK_NAME,
36
+ name: 'readme-metrics (ruby)',
35
37
  version: Readme::Metrics::VERSION,
36
- comment: "#{Readme::Metrics::PLATFORM}/#{RUBY_VERSION}"
38
+ comment: "#{RUBY_PLATFORM}/#{RUBY_VERSION}" # arm64-darwin21/2.7.2
37
39
  }
38
40
  end
39
41
 
@@ -44,7 +46,7 @@ module Readme
44
46
  timings: timings,
45
47
  request: request,
46
48
  response: response,
47
- startedDateTime: @start_time.iso8601,
49
+ startedDateTime: @start_time.utc.iso8601(3),
48
50
  time: elapsed_time
49
51
  }
50
52
  ]
@@ -1,6 +1,6 @@
1
- require "rack"
2
- require "rack/request"
3
- require_relative "content_type_helper"
1
+ require 'rack'
2
+ require 'rack/request'
3
+ require_relative 'content_type_helper'
4
4
 
5
5
  module Readme
6
6
  class HttpRequest
@@ -11,7 +11,7 @@ module Readme
11
11
  Rack::HTTP_VERSION,
12
12
  Rack::HTTP_HOST,
13
13
  Rack::HTTP_PORT
14
- ]
14
+ ].freeze
15
15
 
16
16
  def initialize(env)
17
17
  @request = Rack::Request.new(env)
@@ -50,7 +50,7 @@ module Readme
50
50
  end
51
51
 
52
52
  def options?
53
- @request.request_method == "OPTIONS"
53
+ @request.request_method == 'OPTIONS'
54
54
  end
55
55
 
56
56
  def headers
@@ -60,6 +60,7 @@ module Readme
60
60
  .to_h
61
61
  .transform_keys { |header| normalize_header_name(header) }
62
62
  .merge unprefixed_headers
63
+ .merge host_header
63
64
  end
64
65
 
65
66
  def body
@@ -82,20 +83,28 @@ module Readme
82
83
  # Other "headers" like version and host are prefixed with `HTTP_` by Rack but
83
84
  # don't seem to be considered legit HTTP headers.
84
85
  def http_header?(name)
85
- name.start_with?("HTTP") && !HTTP_NON_HEADERS.include?(name)
86
+ name.start_with?('HTTP') && !HTTP_NON_HEADERS.include?(name)
86
87
  end
87
88
 
88
89
  # Headers like `Content-Type: application/json` come into rack like
89
90
  # `"HTTP_CONTENT_TYPE" => "application/json"`.
90
91
  def normalize_header_name(header)
91
- header.delete_prefix("HTTP_").split("_").map(&:capitalize).join("-")
92
+ header.delete_prefix('HTTP_').split('_').map(&:capitalize).join('-')
92
93
  end
93
94
 
94
95
  # These special headers are explicitly _not_ prefixed with HTTP_ in the Rack
95
96
  # env so we need to add them in manually
96
97
  def unprefixed_headers
97
- {"Content-Type" => @request.content_type,
98
- "Content-Length" => @request.content_length}.compact
98
+ {
99
+ 'Content-Type' => @request.content_type,
100
+ 'Content-Length' => @request.content_length
101
+ }.compact
102
+ end
103
+
104
+ def host_header
105
+ {
106
+ 'Host' => @request.host
107
+ }.compact
99
108
  end
100
109
  end
101
110
  end
@@ -1,6 +1,6 @@
1
- require "rack"
2
- require "rack/response"
3
- require_relative "content_type_helper"
1
+ require 'rack'
2
+ require 'rack/response'
3
+ require_relative 'content_type_helper'
4
4
 
5
5
  module Readme
6
6
  class HttpResponse < SimpleDelegator
@@ -13,22 +13,22 @@ module Readme
13
13
  def body
14
14
  if raw_body.respond_to?(:rewind)
15
15
  raw_body.rewind
16
- content = raw_body.each.reduce("", :+)
16
+ content = raw_body.each.sum('')
17
17
  raw_body.rewind
18
18
 
19
19
  content
20
20
  else
21
- raw_body.each.reduce("", :+)
21
+ raw_body.each.sum('')
22
22
  end
23
23
  end
24
24
 
25
25
  def content_length
26
26
  if empty_body_status?
27
27
  0
28
- elsif !headers["Content-Length"]
28
+ elsif !headers['Content-Length']
29
29
  body.bytesize
30
30
  else
31
- headers["Content-Length"].to_i
31
+ headers['Content-Length'].to_i
32
32
  end
33
33
  end
34
34
 
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Readme
2
4
  class Metrics
3
- VERSION = "2.0.1"
5
+ VERSION = '2.2.0'
4
6
  end
5
7
  end
@@ -1,33 +1,21 @@
1
- require "readme/metrics/version"
2
- require "readme/har/serializer"
3
- require "readme/filter"
4
- require "readme/payload"
5
- require "readme/request_queue"
6
- require "readme/errors"
7
- require "readme/http_request"
8
- require "readme/http_response"
9
- require "httparty"
10
- require "logger"
11
- require "os"
1
+ # frozen_string_literal: true
2
+
3
+ require 'readme/metrics/version'
4
+ require 'readme/har/serializer'
5
+ require 'readme/filter'
6
+ require 'readme/payload'
7
+ require 'readme/request_queue'
8
+ require 'readme/errors'
9
+ require 'readme/http_request'
10
+ require 'readme/http_response'
11
+ require 'httparty'
12
+ require 'logger'
12
13
 
13
14
  module Readme
14
15
  class Metrics
15
- def self.platform
16
- if OS.windows?
17
- "windows"
18
- elsif OS.mac?
19
- "mac"
20
- elsif OS.linux?
21
- "linux"
22
- else
23
- "unknown"
24
- end
25
- end
26
-
27
- SDK_NAME = "Readme.io Ruby SDK"
28
- PLATFORM = platform
16
+ SDK_NAME = 'readme-metrics'
29
17
  DEFAULT_BUFFER_LENGTH = 1
30
- ENDPOINT = "https://metrics.readme.io/v1/request"
18
+ ENDPOINT = URI.join(ENV['README_METRICS_SERVER'] || 'https://metrics.readme.io', '/v1/request')
31
19
 
32
20
  def self.logger
33
21
  @@logger
@@ -77,15 +65,16 @@ module Readme
77
65
  request = HttpRequest.new(env)
78
66
  har = Har::Serializer.new(request, response, start_time, end_time, @filter)
79
67
  user_info = @get_user_info.call(env)
68
+ ip = env['REMOTE_ADDR']
80
69
 
81
70
  if !user_info_valid?(user_info)
82
71
  Readme::Metrics.logger.warn Errors.bad_block_message(user_info)
83
72
  elsif request.options?
84
- Readme::Metrics.logger.info "OPTIONS request omitted from ReadMe API logging"
73
+ Readme::Metrics.logger.info 'OPTIONS request omitted from ReadMe API logging'
85
74
  elsif !can_filter? request, response
86
75
  Readme::Metrics.logger.warn "Request or response body MIME type isn't supported for filtering. Omitting request from ReadMe API logging"
87
76
  else
88
- payload = Payload.new(har, user_info, development: @development)
77
+ payload = Payload.new(har, user_info, ip, development: @development)
89
78
  @@request_queue.push(payload.to_json) unless payload.ignore
90
79
  end
91
80
  end
@@ -121,34 +110,36 @@ module Readme
121
110
  raise Errors::ConfigurationError, Errors::BUFFER_LENGTH_ERROR
122
111
  end
123
112
 
124
- if options[:development] && !is_a_boolean?(options[:development])
113
+ if options[:development] && !a_boolean?(options[:development])
125
114
  raise Errors::ConfigurationError, Errors::DEVELOPMENT_ERROR
126
115
  end
127
116
 
128
- if options[:logger] && has_logger_inferface?(options[:logger])
117
+ if options[:logger] && logger_inferface?(options[:logger])
129
118
  raise Errors::ConfigurationError, Errors::LOGGER_ERROR
130
119
  end
131
120
  end
132
121
 
133
- def has_logger_inferface?(logger)
134
- [
135
- :unknown,
136
- :fatal,
137
- :error,
138
- :warn,
139
- :info,
140
- :debug
122
+ def logger_inferface?(logger)
123
+ %i[
124
+ unknown
125
+ fatal
126
+ error
127
+ warn
128
+ info
129
+ debug
141
130
  ].any? { |message| !logger.respond_to? message }
142
131
  end
143
132
 
144
- def is_a_boolean?(arg)
145
- arg == true || arg == false
133
+ def a_boolean?(arg)
134
+ [true, false].include?(arg)
146
135
  end
147
136
 
137
+ # rubocop:disable Style/InverseMethods
148
138
  def user_info_valid?(user_info)
149
- !user_info.nil? &&
139
+ (!user_info.nil? &&
150
140
  !user_info.values.any?(&:nil?) &&
151
- user_info.has_key?(:api_key) || user_info.has_key?(:id)
141
+ user_info.key?(:api_key)) || user_info.key?(:id)
152
142
  end
143
+ # rubocop:enable Style/InverseMethods
153
144
  end
154
145
  end
@@ -1,24 +1,32 @@
1
- require "uuid"
1
+ require 'socket'
2
+ require 'securerandom'
3
+
4
+ def validate_uuid(uuid)
5
+ return if uuid.nil?
6
+
7
+ uuid.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i)
8
+ end
2
9
 
3
10
  module Readme
4
11
  class Payload
5
12
  attr_reader :ignore
6
13
 
7
- def initialize(har, info, development:)
14
+ def initialize(har, info, ip_address, development:)
8
15
  @har = har
9
16
  @user_info = info.slice(:id, :label, :email)
10
17
  @user_info[:id] = info[:api_key] unless info[:api_key].nil? # swap api_key for id if api_key is present
11
18
  @log_id = info[:log_id]
12
19
  @ignore = info[:ignore]
20
+ @ip_address = ip_address
13
21
  @development = development
14
- @uuid = UUID.new
22
+ @uuid = SecureRandom.uuid
15
23
  end
16
24
 
17
- def to_json
25
+ def to_json(*_args)
18
26
  {
19
- logId: UUID.validate(@log_id) ? @log_id : @uuid.generate,
27
+ _id: validate_uuid(@log_id) ? @log_id : @uuid,
20
28
  group: @user_info,
21
- clientIPAddress: "1.1.1.1",
29
+ clientIPAddress: @ip_address,
22
30
  development: @development,
23
31
  request: JSON.parse(@har.to_json)
24
32
  }.to_json
@@ -1,4 +1,4 @@
1
- require "readme/metrics"
1
+ require 'readme/metrics'
2
2
 
3
3
  module Readme
4
4
  class RequestQueue
@@ -30,8 +30,8 @@ module Readme
30
30
  Thread.new do
31
31
  HTTParty.post(
32
32
  Readme::Metrics::ENDPOINT,
33
- basic_auth: {username: @api_key, password: ""},
34
- headers: {"Content-Type" => "application/json"},
33
+ basic_auth: { username: @api_key, password: '' },
34
+ headers: { 'Content-Type' => 'application/json' },
35
35
  body: to_json(payloads)
36
36
  )
37
37
  end
@@ -42,7 +42,7 @@ module Readme
42
42
  end
43
43
 
44
44
  def to_json(payloads)
45
- "[#{payloads.join(", ")}]"
45
+ "[#{payloads.join(', ')}]"
46
46
  end
47
47
  end
48
48
  end
@@ -0,0 +1,42 @@
1
+ require 'readme/metrics'
2
+
3
+ module Readme
4
+ class MissingSignatureError < ArgumentError
5
+ def message
6
+ 'Missing Signature'
7
+ end
8
+ end
9
+
10
+ class ExpiredSignatureError < RuntimeError
11
+ def message
12
+ 'Expired Signature'
13
+ end
14
+ end
15
+
16
+ class InvalidSignatureError < RuntimeError
17
+ def message
18
+ 'Invalid Signature'
19
+ end
20
+ end
21
+
22
+ class Webhook
23
+ def self.verify(body, signature, secret)
24
+ raise MissingSignatureError unless signature
25
+
26
+ parsed = signature.split(',').each_with_object({ time: -1, readme_signature: '' }) do |item, accum|
27
+ k, v = item.split('=')
28
+ accum[:time] = v if k.eql? 't'
29
+ accum[:readme_signature] = v if k.eql? 'v0'
30
+ end
31
+
32
+ # Make sure timestamp is recent to prevent replay attacks
33
+ thirty_minutes = 30 * 60
34
+ raise ExpiredSignatureError if Time.now.utc - Time.at(0, parsed[:time].to_i, :millisecond).utc > thirty_minutes
35
+
36
+ # Verify the signature is valid
37
+ unsigned = "#{parsed[:time]}.#{body}"
38
+ mac = OpenSSL::HMAC.hexdigest('SHA256', secret, unsigned)
39
+ raise InvalidSignatureError if mac != parsed[:readme_signature]
40
+ end
41
+ end
42
+ end
@@ -1,31 +1,30 @@
1
- require_relative "lib/readme/metrics/version"
1
+ require_relative 'lib/readme/metrics/version'
2
2
 
3
3
  Gem::Specification.new do |spec|
4
- spec.name = "readme-metrics"
4
+ spec.name = 'readme-metrics'
5
5
  spec.version = Readme::Metrics::VERSION
6
- spec.authors = ["ReadMe"]
7
- spec.email = ["support@readme.io"]
8
- spec.license = "ISC"
6
+ spec.authors = ['ReadMe']
7
+ spec.email = ['support@readme.io']
8
+ spec.license = 'ISC'
9
9
 
10
10
  spec.summary = "SDK for Readme's metrics API"
11
11
  spec.description = "Middleware for logging requests to Readme's metrics API"
12
- spec.homepage = "https://docs.readme.com/metrics/docs/getting-started-with-api-metrics"
13
- spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
12
+ spec.homepage = 'https://docs.readme.com/metrics/docs/getting-started-with-api-metrics'
13
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')
14
14
 
15
- spec.metadata["homepage_uri"] = spec.homepage
16
- spec.metadata["source_code_uri"] = "https://github.com/readmeio/metrics-sdks/blob/main/packages/ruby"
17
- spec.metadata["changelog_uri"] = "https://github.com/readmeio/metrics-sdks/blob/main/packages/ruby/CHANGELOG.md"
15
+ spec.metadata['homepage_uri'] = spec.homepage
16
+ spec.metadata['source_code_uri'] = 'https://github.com/readmeio/metrics-sdks/tree/main/packages/ruby'
17
+ spec.metadata['changelog_uri'] = 'https://github.com/readmeio/metrics-sdks/blob/main/CHANGELOG.md'
18
18
 
19
19
  # Specify which files should be added to the gem when it is released.
20
20
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
- spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
21
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
22
22
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
23
  end
24
+
24
25
  # spec.bindir = "exe"
25
26
  # spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
- spec.require_paths = ["lib"]
27
+ spec.require_paths = ['lib']
27
28
 
28
- spec.add_runtime_dependency "httparty", "~> 0.18"
29
- spec.add_runtime_dependency "uuid", "~> 2.3.8"
30
- spec.add_runtime_dependency "os", "~> 1.1.4"
29
+ spec.add_runtime_dependency 'httparty', '~> 0.18'
31
30
  end