readme-metrics 1.0.0 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17dad1b40b974d05ae3d57a664e6f95c393cbaf8e807071631c11800b2ee118c
4
- data.tar.gz: 2d3595d321dea5bac6b5577180f608a2f9045e333592e5c33cca4c52a3e997ea
3
+ metadata.gz: df0dd7efcddca3f324e4e6f7b61fd70a1bee3d3d81571ffa1dc72e090bf2934e
4
+ data.tar.gz: b99811540e9195cecad5c8ee3cf7807fb3340e87353327d021a166af5f34a112
5
5
  SHA512:
6
- metadata.gz: a586f8ae1273269473451ea60bfeccb5e9fd01b6fa4ddacfd99b5918dfed24aa340a9e61aba6036a96b572c1eef50c4d2e1a9cd87162442197a02a4ff2456b2c
7
- data.tar.gz: a7d894c039bdc676164f1dcd083650ae8ce026152baad7d83ea790a3ee4850e4de59246e922a641d6446943389c456557c1712b8b34c2085d4398e226c323c86
6
+ metadata.gz: 6d39db68ce16afde0e0139a4e31bfac53f5bef2066972777165502eac78fd09ed292a23e945d7584385148e3375d72f8d254275a7367e7e07278a16a79b85819
7
+ data.tar.gz: c8e5b439466d50cbac88269d6036c22f7aa84864590cc28862976bf005ada580431b3e9a4a8e652a6ae5ecadb687b2bf59e6607f0cd87e15315f26b86a003d12
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- readme-metrics (0.2.0)
4
+ readme-metrics (1.1.0)
5
5
  httparty (~> 0.18)
6
6
 
7
7
  GEM
@@ -21,7 +21,7 @@ GEM
21
21
  addressable (>= 2.4)
22
22
  mime-types (3.3.1)
23
23
  mime-types-data (~> 3.2015)
24
- mime-types-data (3.2020.0512)
24
+ mime-types-data (3.2021.0225)
25
25
  multi_xml (0.6.0)
26
26
  parallel (1.19.2)
27
27
  parser (2.7.1.4)
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
- # readmeio
1
+ # readme-metrics
2
2
 
3
3
  Track your API metrics within ReadMe.
4
4
 
5
+ [![RubyGems](https://img.shields.io/gem/v/readme-metrics)](https://rubygems.org/gems/readme-metrics)
5
6
  [![Build](https://github.com/readmeio/metrics-sdks/workflows/ruby/badge.svg)](https://github.com/readmeio/metrics-sdks)
6
7
 
7
8
  [![](https://d3vv6lp55qjaqc.cloudfront.net/items/1M3C3j0I0s0j3T362344/Untitled-2.png)](https://readme.io)
@@ -26,8 +27,8 @@ current_user for a given request from the environment.
26
27
 
27
28
  ### Batching requests
28
29
 
29
- By default, the middleware will batch requests to the ReadMe API in groups of
30
- 10. For every 10 requests made to your application, the middleware will make a
30
+ By default, the middleware will batch requests to the ReadMe API in groups of 10.
31
+ For every 10 requests made to your application, the middleware will make a
31
32
  single request to ReadMe. If you wish to override this, provide a
32
33
  `buffer_length` option when configuring the middleware.
33
34
 
@@ -51,7 +52,7 @@ You may only specify either `reject_params` or `allow_only` keys, not both.
51
52
  require "readme/metrics"
52
53
 
53
54
  options = {
54
- api_key: "YOUR_API_KEY",
55
+ api_key: "<<apiKey>>",
55
56
  development: false,
56
57
  reject_params: ["not_included", "dont_send"],
57
58
  buffer_length: 5,
@@ -62,13 +63,13 @@ config.middleware.use Readme::Metrics, options do |env|
62
63
 
63
64
  if current_user.present?
64
65
  {
65
- id: current_user.id,
66
+ api_key: current_user.api_key, # Not the same as the ReadMe API Key
66
67
  label: current_user.name,
67
68
  email: current_user.email
68
69
  }
69
70
  else
70
71
  {
71
- id: "guest",
72
+ api_key: "guest",
72
73
  label: "Guest User",
73
74
  email: "guest@example.com"
74
75
  }
@@ -81,16 +82,16 @@ end
81
82
  ```ruby
82
83
  # config.ru
83
84
  options = {
84
- api_key: "YOUR_API_KEY",
85
+ api_key: "<<apiKey>>",
85
86
  development: false,
86
87
  reject_params: ["not_included", "dont_send"]
87
88
  }
88
89
 
89
90
  use Readme::Metrics, options do |env|
90
91
  {
91
- id: "my_application_id"
92
- label: "My Application",
93
- email: "my.application@example.com"
92
+ api_key: "owlbert_api_key"
93
+ label: "Owlbert",
94
+ email: "owlbert@example.com"
94
95
  }
95
96
  end
96
97
 
@@ -105,4 +106,4 @@ run YourApp.new
105
106
 
106
107
  ## License
107
108
 
108
- [View our license here](https://github.com/readmeio/metrics-sdks/tree/master/packages/ruby/LICENSE)
109
+ [View our license here](https://github.com/readmeio/metrics-sdks/tree/main/packages/ruby/LICENSE)
@@ -0,0 +1,17 @@
1
+ module Readme
2
+ module ContentTypeHelper
3
+ # Assumes the includer has a `content_type` method defined.
4
+
5
+ JSON_MIME_TYPES = [
6
+ "application/json",
7
+ "application/x-json",
8
+ "text/json",
9
+ "text/x-json",
10
+ "+json"
11
+ ]
12
+
13
+ def json?
14
+ JSON_MIME_TYPES.any? { |mime_type| content_type.include?(mime_type) }
15
+ end
16
+ end
17
+ end
data/lib/readme/errors.rb CHANGED
@@ -25,7 +25,7 @@ module Readme
25
25
  middleware.
26
26
 
27
27
  Expected a hash with the shape:
28
- { id: "unique_id", label: "Your user label", email: "Your user email" }
28
+ { api_key: "Your user api key", label: "Your user label", email: "Your user email" }
29
29
 
30
30
  Received value:
31
31
  #{result}
data/lib/readme/filter.rb CHANGED
@@ -1,58 +1,73 @@
1
- class Filter
2
- def self.for(reject: nil, allow_only: nil)
3
- if !reject.nil? && !allow_only.nil?
4
- raise FilterArgsError
5
- elsif !reject.nil?
6
- RejectParams.new(reject)
7
- elsif !allow_only.nil?
8
- AllowOnly.new(allow_only)
9
- else
10
- None.new
1
+ module Readme
2
+ class Filter
3
+ def self.for(reject: nil, allow_only: nil)
4
+ if !reject.nil? && !allow_only.nil?
5
+ raise FilterArgsError
6
+ elsif !reject.nil?
7
+ RejectParams.new(reject)
8
+ elsif !allow_only.nil?
9
+ AllowOnly.new(allow_only)
10
+ else
11
+ None.new
12
+ end
11
13
  end
12
- end
13
14
 
14
- class AllowOnly
15
- def initialize(filter_values)
16
- @filter_values = filter_values.map(&:downcase)
15
+ def self.redact(rejected_params)
16
+ rejected_params.each_with_object({}) do |(k, v), hash|
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}" : ""}]"
19
+ end
17
20
  end
18
21
 
19
- def filter(hash)
20
- hash.select { |key, _value| @filter_values.include?(key.downcase) }
21
- end
22
+ class AllowOnly
23
+ def initialize(filter_fields)
24
+ @allowed_fields = filter_fields
25
+ end
22
26
 
23
- def pass_through?
24
- false
25
- end
26
- end
27
+ def filter(hash)
28
+ allowed_fields = @allowed_fields.map(&:downcase)
29
+ allowed_params, rejected_params = hash.partition { |key, _value| allowed_fields.include?(key.downcase) }.map(&:to_h)
27
30
 
28
- class RejectParams
29
- def initialize(filter_values)
30
- @filter_values = filter_values.map(&:downcase)
31
- end
31
+ allowed_params.merge(Filter.redact(rejected_params))
32
+ end
32
33
 
33
- def filter(hash)
34
- hash.reject { |key, _value| @filter_values.include?(key.downcase) }
34
+ def pass_through?
35
+ false
36
+ end
35
37
  end
36
38
 
37
- def pass_through?
38
- false
39
- end
40
- end
39
+ class RejectParams
40
+ def initialize(filter_fields)
41
+ @rejected_fields = filter_fields
42
+ end
43
+
44
+ def filter(hash)
45
+ rejected_fields = @rejected_fields.map(&:downcase)
46
+ rejected_params, allowed_params = hash.partition { |key, _value| rejected_fields.include?(key.downcase) }.map(&:to_h)
47
+
48
+ allowed_params.merge(Filter.redact(rejected_params))
49
+ end
41
50
 
42
- class None
43
- def filter(hash)
44
- hash
51
+ def pass_through?
52
+ false
53
+ end
45
54
  end
46
55
 
47
- def pass_through?
48
- true
56
+ class None
57
+ def filter(hash)
58
+ hash
59
+ end
60
+
61
+ def pass_through?
62
+ true
63
+ end
49
64
  end
50
- end
51
65
 
52
- class FilterArgsError < StandardError
53
- def initialize
54
- msg = "Can only supply either reject_params or allow_only, not both."
55
- super(msg)
66
+ class FilterArgsError < StandardError
67
+ def initialize
68
+ msg = "Can only supply either reject_params or allow_only, not both."
69
+ super(msg)
70
+ end
56
71
  end
57
72
  end
58
73
  end
@@ -0,0 +1,101 @@
1
+ require "rack"
2
+ require "rack/request"
3
+ require_relative "content_type_helper"
4
+
5
+ module Readme
6
+ class HttpRequest
7
+ include ContentTypeHelper
8
+
9
+ HTTP_NON_HEADERS = [
10
+ Rack::HTTP_COOKIE,
11
+ Rack::HTTP_VERSION,
12
+ Rack::HTTP_HOST,
13
+ Rack::HTTP_PORT
14
+ ]
15
+
16
+ def initialize(env)
17
+ @request = Rack::Request.new(env)
18
+ end
19
+
20
+ def url
21
+ @request.url
22
+ end
23
+
24
+ def query_params
25
+ @request.GET
26
+ end
27
+
28
+ def cookies
29
+ @request.cookies
30
+ end
31
+
32
+ def http_version
33
+ @request.get_header(Rack::HTTP_VERSION)
34
+ end
35
+
36
+ def request_method
37
+ @request.request_method
38
+ end
39
+
40
+ def content_type
41
+ @request.content_type
42
+ end
43
+
44
+ def form_data?
45
+ @request.form_data?
46
+ end
47
+
48
+ def content_length
49
+ @request.content_length.to_i
50
+ end
51
+
52
+ def options?
53
+ @request.request_method == "OPTIONS"
54
+ end
55
+
56
+ def headers
57
+ @request
58
+ .each_header
59
+ .select { |key, _| http_header?(key) }
60
+ .to_h
61
+ .transform_keys { |header| normalize_header_name(header) }
62
+ .merge unprefixed_headers
63
+ end
64
+
65
+ def body
66
+ @request.body.rewind
67
+ content = @request.body.read
68
+ @request.body.rewind
69
+
70
+ content
71
+ end
72
+
73
+ def parsed_form_data
74
+ @request.POST
75
+ end
76
+
77
+ private
78
+
79
+ # "headers" in Rack::Request just means any key in the env. The HTTP headers
80
+ # are all the headers prefixed with `HTTP_` as per the spec:
81
+ # https://github.com/rack/rack/blob/master/SPEC.rdoc#the-environment-
82
+ # Other "headers" like version and host are prefixed with `HTTP_` by Rack but
83
+ # don't seem to be considered legit HTTP headers.
84
+ def http_header?(name)
85
+ name.start_with?("HTTP") && !HTTP_NON_HEADERS.include?(name)
86
+ end
87
+
88
+ # Headers like `Content-Type: application/json` come into rack like
89
+ # `"HTTP_CONTENT_TYPE" => "application/json"`.
90
+ def normalize_header_name(header)
91
+ header.delete_prefix("HTTP_").split("_").map(&:capitalize).join("-")
92
+ end
93
+
94
+ # These special headers are explicitly _not_ prefixed with HTTP_ in the Rack
95
+ # env so we need to add them in manually
96
+ def unprefixed_headers
97
+ {"Content-Type" => @request.content_type,
98
+ "Content-Length" => @request.content_length}.compact
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,45 @@
1
+ require "rack"
2
+ require "rack/response"
3
+ require_relative "content_type_helper"
4
+
5
+ module Readme
6
+ class HttpResponse < SimpleDelegator
7
+ include ContentTypeHelper
8
+
9
+ def self.from_parts(status, headers, body)
10
+ new(Rack::Response.new(body, status, headers))
11
+ end
12
+
13
+ def body
14
+ if raw_body.respond_to?(:rewind)
15
+ raw_body.rewind
16
+ content = raw_body.each.reduce("", :+)
17
+ raw_body.rewind
18
+
19
+ content
20
+ else
21
+ raw_body.each.reduce("", :+)
22
+ end
23
+ end
24
+
25
+ def content_length
26
+ if empty_body_status?
27
+ 0
28
+ elsif !headers["Content-Length"]
29
+ body.bytesize
30
+ else
31
+ headers["Content-Length"].to_i
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def raw_body
38
+ __getobj__.body
39
+ end
40
+
41
+ def empty_body_status?
42
+ Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status.to_i)
43
+ end
44
+ end
45
+ end
@@ -4,8 +4,8 @@ require "readme/filter"
4
4
  require "readme/payload"
5
5
  require "readme/request_queue"
6
6
  require "readme/errors"
7
- require "http_request"
8
- require "http_response"
7
+ require "readme/http_request"
8
+ require "readme/http_response"
9
9
  require "httparty"
10
10
  require "logger"
11
11
 
@@ -14,7 +14,8 @@ module Readme
14
14
  SDK_NAME = "Readme.io Ruby SDK"
15
15
  DEFAULT_BUFFER_LENGTH = 10
16
16
  ENDPOINT = "https://metrics.readme.io/v1/request"
17
- USER_INFO_KEYS = [:id, :label, :email]
17
+ USER_INFO_KEYS = [:api_key, :label, :email]
18
+ USER_INFO_KEYS_DEPRECATED = [:id, :label, :email]
18
19
 
19
20
  def self.logger
20
21
  @@logger
@@ -65,7 +66,7 @@ module Readme
65
66
  har = Har::Serializer.new(request, response, start_time, end_time, @filter)
66
67
  user_info = @get_user_info.call(env)
67
68
 
68
- if user_info_invalid?(user_info)
69
+ if !user_info_valid?(user_info)
69
70
  Readme::Metrics.logger.warn Errors.bad_block_message(user_info)
70
71
  elsif request.options?
71
72
  Readme::Metrics.logger.info "OPTIONS request omitted from ReadMe API logging"
@@ -132,10 +133,12 @@ module Readme
132
133
  arg == true || arg == false
133
134
  end
134
135
 
135
- def user_info_invalid?(user_info)
136
- user_info.nil? ||
137
- user_info.values.any?(&:nil?) ||
138
- USER_INFO_KEYS.sort != user_info.keys.sort
136
+ def user_info_valid?(user_info)
137
+ sorted_user_info_keys = user_info.keys.sort
138
+ !user_info.nil? &&
139
+ !user_info.values.any?(&:nil?) &&
140
+ (sorted_user_info_keys === USER_INFO_KEYS.sort ||
141
+ sorted_user_info_keys === USER_INFO_KEYS_DEPRECATED.sort)
139
142
  end
140
143
  end
141
144
  end
@@ -1,5 +1,5 @@
1
1
  module Readme
2
2
  class Metrics
3
- VERSION = "1.0.0"
3
+ VERSION = "1.1.0"
4
4
  end
5
5
  end
@@ -2,6 +2,8 @@ module Readme
2
2
  class Payload
3
3
  def initialize(har, user_info, development:)
4
4
  @har = har
5
+ # swap api_key for id
6
+ user_info[:id] = user_info.delete :api_key unless user_info[:api_key].nil?
5
7
  @user_info = user_info
6
8
  @development = development
7
9
  end
@@ -13,8 +13,8 @@ Gem::Specification.new do |spec|
13
13
  spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
14
14
 
15
15
  spec.metadata["homepage_uri"] = spec.homepage
16
- spec.metadata["source_code_uri"] = "https://github.com/readmeio/metrics-sdks/blob/master/packages/ruby"
17
- spec.metadata["changelog_uri"] = "https://github.com/readmeio/metrics-sdks/blob/master/packages/ruby/CHANGELOG.md"
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"
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.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: readme-metrics
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ReadMe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-28 00:00:00.000000000 Z
11
+ date: 2021-06-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -40,15 +40,15 @@ files:
40
40
  - Rakefile
41
41
  - bin/console
42
42
  - bin/setup
43
- - lib/content_type_helper.rb
44
- - lib/http_request.rb
45
- - lib/http_response.rb
43
+ - lib/readme/content_type_helper.rb
46
44
  - lib/readme/errors.rb
47
45
  - lib/readme/filter.rb
48
46
  - lib/readme/har/collection.rb
49
47
  - lib/readme/har/request_serializer.rb
50
48
  - lib/readme/har/response_serializer.rb
51
49
  - lib/readme/har/serializer.rb
50
+ - lib/readme/http_request.rb
51
+ - lib/readme/http_response.rb
52
52
  - lib/readme/metrics.rb
53
53
  - lib/readme/metrics/version.rb
54
54
  - lib/readme/payload.rb
@@ -59,8 +59,8 @@ licenses:
59
59
  - ISC
60
60
  metadata:
61
61
  homepage_uri: https://docs.readme.com/metrics/docs/getting-started-with-api-metrics
62
- source_code_uri: https://github.com/readmeio/metrics-sdks/blob/master/packages/ruby
63
- changelog_uri: https://github.com/readmeio/metrics-sdks/blob/master/packages/ruby/CHANGELOG.md
62
+ source_code_uri: https://github.com/readmeio/metrics-sdks/blob/main/packages/ruby
63
+ changelog_uri: https://github.com/readmeio/metrics-sdks/blob/main/packages/ruby/CHANGELOG.md
64
64
  post_install_message:
65
65
  rdoc_options: []
66
66
  require_paths:
@@ -76,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
76
  - !ruby/object:Gem::Version
77
77
  version: '0'
78
78
  requirements: []
79
- rubygems_version: 3.1.2
79
+ rubygems_version: 3.1.4
80
80
  signing_key:
81
81
  specification_version: 4
82
82
  summary: SDK for Readme's metrics API
@@ -1,15 +0,0 @@
1
- module ContentTypeHelper
2
- # Assumes the includer has a `content_type` method defined.
3
-
4
- JSON_MIME_TYPES = [
5
- "application/json",
6
- "application/x-json",
7
- "text/json",
8
- "text/x-json",
9
- "+json"
10
- ]
11
-
12
- def json?
13
- JSON_MIME_TYPES.any? { |mime_type| content_type.include?(mime_type) }
14
- end
15
- end
data/lib/http_request.rb DELETED
@@ -1,99 +0,0 @@
1
- require "rack"
2
- require "rack/request"
3
- require "content_type_helper"
4
-
5
- class HttpRequest
6
- include ContentTypeHelper
7
-
8
- HTTP_NON_HEADERS = [
9
- Rack::HTTP_COOKIE,
10
- Rack::HTTP_VERSION,
11
- Rack::HTTP_HOST,
12
- Rack::HTTP_PORT
13
- ]
14
-
15
- def initialize(env)
16
- @request = Rack::Request.new(env)
17
- end
18
-
19
- def url
20
- @request.url
21
- end
22
-
23
- def query_params
24
- @request.GET
25
- end
26
-
27
- def cookies
28
- @request.cookies
29
- end
30
-
31
- def http_version
32
- @request.get_header(Rack::HTTP_VERSION)
33
- end
34
-
35
- def request_method
36
- @request.request_method
37
- end
38
-
39
- def content_type
40
- @request.content_type
41
- end
42
-
43
- def form_data?
44
- @request.form_data?
45
- end
46
-
47
- def content_length
48
- @request.content_length.to_i
49
- end
50
-
51
- def options?
52
- @request.request_method == "OPTIONS"
53
- end
54
-
55
- def headers
56
- @request
57
- .each_header
58
- .select { |key, _| http_header?(key) }
59
- .to_h
60
- .transform_keys { |header| normalize_header_name(header) }
61
- .merge unprefixed_headers
62
- end
63
-
64
- def body
65
- @request.body.rewind
66
- content = @request.body.read
67
- @request.body.rewind
68
-
69
- content
70
- end
71
-
72
- def parsed_form_data
73
- @request.POST
74
- end
75
-
76
- private
77
-
78
- # "headers" in Rack::Request just means any key in the env. The HTTP headers
79
- # are all the headers prefixed with `HTTP_` as per the spec:
80
- # https://github.com/rack/rack/blob/master/SPEC.rdoc#the-environment-
81
- # Other "headers" like version and host are prefixed with `HTTP_` by Rack but
82
- # don't seem to be considered legit HTTP headers.
83
- def http_header?(name)
84
- name.start_with?("HTTP") && !HTTP_NON_HEADERS.include?(name)
85
- end
86
-
87
- # Headers like `Content-Type: application/json` come into rack like
88
- # `"HTTP_CONTENT_TYPE" => "application/json"`.
89
- def normalize_header_name(header)
90
- header.delete_prefix("HTTP_").split("_").map(&:capitalize).join("-")
91
- end
92
-
93
- # These special headers are explicitly _not_ prefixed with HTTP_ in the Rack
94
- # env so we need to add them in manually
95
- def unprefixed_headers
96
- {"Content-Type" => @request.content_type,
97
- "Content-Length" => @request.content_length}.compact
98
- end
99
- end
data/lib/http_response.rb DELETED
@@ -1,43 +0,0 @@
1
- require "rack"
2
- require "rack/response"
3
- require "content_type_helper"
4
-
5
- class HttpResponse < SimpleDelegator
6
- include ContentTypeHelper
7
-
8
- def self.from_parts(status, headers, body)
9
- new(Rack::Response.new(body, status, headers))
10
- end
11
-
12
- def body
13
- if raw_body.respond_to?(:rewind)
14
- raw_body.rewind
15
- content = raw_body.each.reduce("", :+)
16
- raw_body.rewind
17
-
18
- content
19
- else
20
- raw_body.each.reduce("", :+)
21
- end
22
- end
23
-
24
- def content_length
25
- if empty_body_status?
26
- 0
27
- elsif !headers["Content-Length"]
28
- body.bytesize
29
- else
30
- headers["Content-Length"].to_i
31
- end
32
- end
33
-
34
- private
35
-
36
- def raw_body
37
- __getobj__.body
38
- end
39
-
40
- def empty_body_status?
41
- Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status.to_i)
42
- end
43
- end