rudder_analytics_sync 1.0.7 → 2.0.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.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/pull_request_template.md +3 -0
- data/.github/workflows/build-and-quality-checks.yml +31 -0
- data/.github/workflows/check-pr-title.yml +16 -0
- data/.github/workflows/notion-pr-sync.yml +55 -0
- data/.github/workflows/slack-notify.yml +41 -0
- data/.gitignore +6 -1
- data/.hound.yml +1 -1
- data/.rubocop.yml +30 -4
- data/CODEOWNERS +1 -0
- data/Gemfile +2 -1
- data/LICENSE.txt +1 -1
- data/README.md +75 -22
- data/SECURITY.md +19 -0
- data/bin/console +1 -1
- data/example/example.rb +151 -41
- data/lib/rudder_analytics_sync/batch.rb +20 -6
- data/lib/rudder_analytics_sync/client.rb +18 -0
- data/lib/rudder_analytics_sync/configuration.rb +2 -2
- data/lib/rudder_analytics_sync/constants.rb +21 -0
- data/lib/rudder_analytics_sync/logging.rb +1 -1
- data/lib/rudder_analytics_sync/operations/alias.rb +8 -1
- data/lib/rudder_analytics_sync/operations/group.rb +8 -1
- data/lib/rudder_analytics_sync/operations/identify.rb +15 -5
- data/lib/rudder_analytics_sync/operations/operation.rb +22 -18
- data/lib/rudder_analytics_sync/operations/page.rb +8 -1
- data/lib/rudder_analytics_sync/operations/screen.rb +9 -2
- data/lib/rudder_analytics_sync/operations/track.rb +13 -5
- data/lib/rudder_analytics_sync/request.rb +21 -10
- data/lib/rudder_analytics_sync/utils.rb +26 -22
- data/lib/rudder_analytics_sync/version.rb +1 -1
- data/rudder_analytics_sync.gemspec +5 -2
- metadata +18 -10
- data/.travis.yml +0 -6
- data/CHANGELOG.md +0 -35
@@ -1,10 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'rudder_analytics_sync/constants'
|
4
|
+
|
3
5
|
module RudderAnalyticsSync
|
4
6
|
class Batch
|
5
7
|
include RudderAnalyticsSync::Utils
|
8
|
+
include RudderAnalyticsSync::Constants
|
6
9
|
|
7
|
-
attr_reader :client, :payload
|
10
|
+
attr_reader :client, :payload, :batch_context, :batch_integrations
|
8
11
|
|
9
12
|
def self.deserialize(client, payload)
|
10
13
|
new(client, symbolize_keys(payload))
|
@@ -40,11 +43,14 @@ module RudderAnalyticsSync
|
|
40
43
|
end
|
41
44
|
|
42
45
|
def context=(context)
|
43
|
-
|
46
|
+
if context # rubocop:disable Style/SafeNavigation
|
47
|
+
context.delete(:library)
|
48
|
+
end
|
49
|
+
@batch_context = context
|
44
50
|
end
|
45
51
|
|
46
52
|
def integrations=(integrations)
|
47
|
-
|
53
|
+
@batch_integrations = integrations
|
48
54
|
end
|
49
55
|
|
50
56
|
def serialize
|
@@ -56,17 +62,25 @@ module RudderAnalyticsSync
|
|
56
62
|
raise ArgumentError, 'A batch must contain at least one action'
|
57
63
|
end
|
58
64
|
|
65
|
+
if payload[:batch].inspect.length > MAX_BATCH_SIZE
|
66
|
+
raise ArgumentError, 'Max batch size is 500 KB'
|
67
|
+
end
|
68
|
+
|
59
69
|
Request.new(client).post('/v1/batch', payload)
|
60
70
|
end
|
61
71
|
|
62
72
|
private
|
63
73
|
|
64
|
-
def add(operation_class, options, action)
|
74
|
+
def add(operation_class, options, action) # rubocop:disable Metrics/AbcSize
|
65
75
|
operation = operation_class.new(client, symbolize_keys(options))
|
66
76
|
operation_payload = operation.build_payload
|
67
|
-
operation_payload[:context] = operation_payload[:context].merge(
|
68
|
-
operation_payload[:integrations] =
|
77
|
+
operation_payload[:context] = operation_payload[:context].merge(batch_context || {})
|
78
|
+
operation_payload[:integrations] = batch_integrations || operation_payload[:integrations]
|
69
79
|
operation_payload[:type] = action
|
80
|
+
if operation_payload.inspect.length > MAX_MESSAGE_SIZE
|
81
|
+
raise ArgumentError, 'Max message size is 32 KB'
|
82
|
+
end
|
83
|
+
|
70
84
|
payload[:batch] << operation_payload
|
71
85
|
end
|
72
86
|
end
|
@@ -22,6 +22,7 @@ module RudderAnalyticsSync
|
|
22
22
|
# @option :context [Hash]
|
23
23
|
# @option :integrations [Hash]
|
24
24
|
# @option :timestamp [#iso8601] (Time.now)
|
25
|
+
# @option :message_id
|
25
26
|
def identify(options)
|
26
27
|
Operations::Identify.new(self, symbolize_keys(options)).call
|
27
28
|
end
|
@@ -34,6 +35,7 @@ module RudderAnalyticsSync
|
|
34
35
|
# @option :context [Hash]
|
35
36
|
# @option :integrations [Hash]
|
36
37
|
# @option :timestamp [#iso8601] (Time.now)
|
38
|
+
# @option :message_id
|
37
39
|
def track(options)
|
38
40
|
Operations::Track.new(self, symbolize_keys(options)).call
|
39
41
|
end
|
@@ -46,10 +48,24 @@ module RudderAnalyticsSync
|
|
46
48
|
# @option :context [Hash]
|
47
49
|
# @option :integrations [Hash]
|
48
50
|
# @option :timestamp [#iso8601] (Time.now)
|
51
|
+
# @option :message_id
|
49
52
|
def page(options)
|
50
53
|
Operations::Page.new(self, symbolize_keys(options)).call
|
51
54
|
end
|
52
55
|
|
56
|
+
# @param [Hash] options
|
57
|
+
# @option :user_id
|
58
|
+
# @option :anonymous_id
|
59
|
+
# @option :name [String]
|
60
|
+
# @option :properties [Hash]
|
61
|
+
# @option :context [Hash]
|
62
|
+
# @option :integrations [Hash]
|
63
|
+
# @option :timestamp [#iso8601] (Time.now)
|
64
|
+
# @option :message_id
|
65
|
+
def screen(options)
|
66
|
+
Operations::Screen.new(self, symbolize_keys(options)).call
|
67
|
+
end
|
68
|
+
|
53
69
|
# @param [Hash] options
|
54
70
|
# @option :user_id
|
55
71
|
# @option :anonymous_id
|
@@ -58,6 +74,7 @@ module RudderAnalyticsSync
|
|
58
74
|
# @option :context [Hash]
|
59
75
|
# @option :integrations [Hash]
|
60
76
|
# @option :timestamp [#iso8601] (Time.now)
|
77
|
+
# @option :message_id
|
61
78
|
def group(options)
|
62
79
|
Operations::Group.new(self, symbolize_keys(options)).call
|
63
80
|
end
|
@@ -70,6 +87,7 @@ module RudderAnalyticsSync
|
|
70
87
|
# @option :context [Hash]
|
71
88
|
# @option :integrations [Hash]
|
72
89
|
# @option :timestamp [#iso8601] (Time.now)
|
90
|
+
# @option :message_id
|
73
91
|
def alias(options)
|
74
92
|
Operations::Alias.new(self, symbolize_keys(options)).call
|
75
93
|
end
|
@@ -6,8 +6,7 @@ module RudderAnalyticsSync
|
|
6
6
|
class Configuration
|
7
7
|
include RudderAnalyticsSync::Utils
|
8
8
|
include RudderAnalyticsSync::Logging
|
9
|
-
|
10
|
-
attr_reader :write_key, :data_plane_url, :on_error, :stub, :logger, :http_options
|
9
|
+
attr_reader :write_key, :data_plane_url, :on_error, :stub, :logger, :http_options, :gzip
|
11
10
|
|
12
11
|
def initialize(settings = {})
|
13
12
|
symbolized_settings = symbolize_keys(settings)
|
@@ -18,6 +17,7 @@ module RudderAnalyticsSync
|
|
18
17
|
@logger = default_logger(symbolized_settings[:logger])
|
19
18
|
@http_options = { use_ssl: true }
|
20
19
|
.merge(symbolized_settings[:http_options] || {})
|
20
|
+
@gzip = symbolized_settings[:gzip]
|
21
21
|
raise ArgumentError, 'Missing required option :write_key' \
|
22
22
|
unless @write_key
|
23
23
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RudderAnalyticsSync
|
4
|
+
module Constants
|
5
|
+
MAX_BATCH_SIZE = 500_000 # ~500 KB
|
6
|
+
MAX_MESSAGE_SIZE = 32_000 # ~32 KB
|
7
|
+
BASE_URL = 'https://hosted.rudderlabs.com'
|
8
|
+
|
9
|
+
DEFAULT_HEADERS = {
|
10
|
+
'Content-Type' => 'application/json',
|
11
|
+
'accept' => 'application/json'
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
DEFAULT_CONTEXT = {
|
15
|
+
library: {
|
16
|
+
name: 'rudder-sdk-ruby-sync',
|
17
|
+
version: RudderAnalyticsSync::VERSION
|
18
|
+
}
|
19
|
+
}.freeze
|
20
|
+
end
|
21
|
+
end
|
@@ -4,7 +4,14 @@ module RudderAnalyticsSync
|
|
4
4
|
module Operations
|
5
5
|
class Alias < Operation
|
6
6
|
def call
|
7
|
-
|
7
|
+
batch = build_payload.merge(
|
8
|
+
type: 'alias'
|
9
|
+
)
|
10
|
+
if batch.inspect.length > MAX_MESSAGE_SIZE
|
11
|
+
raise ArgumentError, 'Max message size is 32 KB'
|
12
|
+
end
|
13
|
+
|
14
|
+
request.post('/v1/batch', { 'batch' => [batch] })
|
8
15
|
end
|
9
16
|
|
10
17
|
def build_payload
|
@@ -4,7 +4,14 @@ module RudderAnalyticsSync
|
|
4
4
|
module Operations
|
5
5
|
class Group < Operation
|
6
6
|
def call
|
7
|
-
|
7
|
+
batch = build_payload.merge(
|
8
|
+
type: 'group'
|
9
|
+
)
|
10
|
+
if batch.inspect.length > MAX_MESSAGE_SIZE
|
11
|
+
raise ArgumentError, 'Max message size is 32 KB'
|
12
|
+
end
|
13
|
+
|
14
|
+
request.post('/v1/batch', { 'batch' => [batch] })
|
8
15
|
end
|
9
16
|
|
10
17
|
def build_payload
|
@@ -4,14 +4,24 @@ module RudderAnalyticsSync
|
|
4
4
|
module Operations
|
5
5
|
class Identify < Operation
|
6
6
|
def call
|
7
|
-
|
7
|
+
batch = build_payload.merge(
|
8
|
+
type: 'identify'
|
9
|
+
)
|
10
|
+
if batch.inspect.length > MAX_MESSAGE_SIZE
|
11
|
+
raise ArgumentError, 'Max message size is 32 KB'
|
12
|
+
end
|
13
|
+
|
14
|
+
request.post('/v1/batch', { 'batch' => [batch] })
|
8
15
|
end
|
9
16
|
|
10
17
|
def build_payload
|
11
|
-
merged_payload = base_payload
|
12
|
-
|
13
|
-
|
14
|
-
|
18
|
+
merged_payload = base_payload
|
19
|
+
if options[:traits]
|
20
|
+
merged_payload = merged_payload.merge(
|
21
|
+
traits: options[:traits] && isoify_dates!(options[:traits])
|
22
|
+
)
|
23
|
+
merged_payload[:context][:traits] = options[:traits] && isoify_dates!(options[:traits])
|
24
|
+
end
|
15
25
|
merged_payload
|
16
26
|
end
|
17
27
|
end
|
@@ -1,28 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'rudder_analytics_sync/utils'
|
4
|
+
require 'rudder_analytics_sync/constants'
|
4
5
|
|
5
6
|
module RudderAnalyticsSync
|
6
7
|
module Operations
|
7
8
|
class Operation
|
8
9
|
include RudderAnalyticsSync::Utils
|
9
|
-
|
10
|
-
DEFAULT_CONTEXT = {
|
11
|
-
library: {
|
12
|
-
name: 'rudder-sdk-ruby-sync',
|
13
|
-
version: RudderAnalyticsSync::VERSION
|
14
|
-
},
|
15
|
-
traits: {
|
16
|
-
|
17
|
-
}
|
18
|
-
}.freeze
|
10
|
+
include RudderAnalyticsSync::Constants
|
19
11
|
|
20
12
|
def initialize(client, options = {})
|
21
13
|
@options = options
|
22
|
-
@context =
|
14
|
+
@context = make_context(options[:context])
|
23
15
|
@request = Request.new(client)
|
24
16
|
end
|
25
17
|
|
18
|
+
def make_context(option_context)
|
19
|
+
if option_context # rubocop:disable Style/SafeNavigation
|
20
|
+
option_context.delete(:library)
|
21
|
+
end
|
22
|
+
DEFAULT_CONTEXT.merge(option_context.to_h)
|
23
|
+
end
|
24
|
+
|
26
25
|
def call
|
27
26
|
raise 'Must be implemented in a subclass'
|
28
27
|
end
|
@@ -31,7 +30,7 @@ module RudderAnalyticsSync
|
|
31
30
|
|
32
31
|
attr_reader :options, :request, :context
|
33
32
|
|
34
|
-
def base_payload
|
33
|
+
def base_payload # rubocop:disable Metrics/AbcSize
|
35
34
|
check_identity!
|
36
35
|
current_time = Time.now.utc
|
37
36
|
|
@@ -40,18 +39,23 @@ module RudderAnalyticsSync
|
|
40
39
|
integrations: options[:integrations] || { All: true },
|
41
40
|
timestamp: maybe_datetime_in_iso8601(options[:timestamp] || Time.now.utc),
|
42
41
|
sentAt: maybe_datetime_in_iso8601(current_time),
|
43
|
-
messageId: uid
|
44
|
-
|
42
|
+
messageId: options[:message_id] || uid,
|
43
|
+
channel: 'server'
|
45
44
|
}
|
46
45
|
|
46
|
+
# add the properties if present
|
47
|
+
if options[:properties]
|
48
|
+
payload = payload.merge({ properties: options[:properties] })
|
49
|
+
end
|
50
|
+
|
47
51
|
# add the userId if present
|
48
|
-
if
|
49
|
-
|
52
|
+
if options[:user_id]
|
53
|
+
payload = payload.merge({ userId: options[:user_id] })
|
50
54
|
end
|
51
55
|
|
52
56
|
# add the anonymousId if present
|
53
|
-
if
|
54
|
-
|
57
|
+
if options[:anonymous_id]
|
58
|
+
payload = payload.merge({ anonymousId: options[:anonymous_id] })
|
55
59
|
end
|
56
60
|
payload
|
57
61
|
end
|
@@ -4,7 +4,14 @@ module RudderAnalyticsSync
|
|
4
4
|
module Operations
|
5
5
|
class Page < Operation
|
6
6
|
def call
|
7
|
-
|
7
|
+
batch = build_payload.merge(
|
8
|
+
type: 'page'
|
9
|
+
)
|
10
|
+
if batch.inspect.length > MAX_MESSAGE_SIZE
|
11
|
+
raise ArgumentError, 'Max message size is 32 KB'
|
12
|
+
end
|
13
|
+
|
14
|
+
request.post('/v1/batch', { 'batch' => [batch] })
|
8
15
|
end
|
9
16
|
|
10
17
|
def build_payload
|
@@ -4,7 +4,14 @@ module RudderAnalyticsSync
|
|
4
4
|
module Operations
|
5
5
|
class Screen < Operation
|
6
6
|
def call
|
7
|
-
|
7
|
+
batch = build_payload.merge(
|
8
|
+
type: 'screen'
|
9
|
+
)
|
10
|
+
if batch.inspect.length > MAX_MESSAGE_SIZE
|
11
|
+
raise ArgumentError, 'Max message size is 32 KB'
|
12
|
+
end
|
13
|
+
|
14
|
+
request.post('/v1/batch', { 'batch' => [batch] })
|
8
15
|
end
|
9
16
|
|
10
17
|
def build_payload
|
@@ -13,7 +20,7 @@ module RudderAnalyticsSync
|
|
13
20
|
base_payload.merge(
|
14
21
|
name: options[:name],
|
15
22
|
event: options[:name],
|
16
|
-
properties: properties.merge({name: options[:name]})
|
23
|
+
properties: properties.merge({ name: options[:name] })
|
17
24
|
)
|
18
25
|
end
|
19
26
|
end
|
@@ -4,18 +4,26 @@ module RudderAnalyticsSync
|
|
4
4
|
module Operations
|
5
5
|
class Track < Operation
|
6
6
|
def call
|
7
|
-
|
7
|
+
batch = build_payload.merge(
|
8
|
+
type: 'track'
|
9
|
+
)
|
10
|
+
if batch.inspect.length > MAX_MESSAGE_SIZE
|
11
|
+
raise ArgumentError, 'Max message size is 32 KB'
|
12
|
+
end
|
13
|
+
|
14
|
+
request.post('/v1/batch', { 'batch' => [batch] })
|
8
15
|
end
|
9
16
|
|
10
17
|
def build_payload
|
11
|
-
raise ArgumentError, 'event name must be present'
|
12
|
-
unless options[:event]
|
18
|
+
raise ArgumentError, 'event name must be present' unless options[:event]
|
13
19
|
|
14
20
|
properties = options[:properties] && isoify_dates!(options[:properties])
|
15
21
|
|
22
|
+
if properties
|
23
|
+
base_payload[:properties] = properties
|
24
|
+
end
|
16
25
|
base_payload.merge(
|
17
|
-
event: options[:event]
|
18
|
-
properties: properties || {}
|
26
|
+
event: options[:event]
|
19
27
|
)
|
20
28
|
end
|
21
29
|
end
|
@@ -1,38 +1,49 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'rudder_analytics_sync/constants'
|
4
|
+
require 'rudder_analytics_sync/logging'
|
5
|
+
require('zlib')
|
6
|
+
|
3
7
|
module RudderAnalyticsSync
|
4
8
|
class Request
|
5
|
-
|
6
|
-
|
7
|
-
'Content-Type' => 'application/json',
|
8
|
-
'accept' => 'application/json'
|
9
|
-
}.freeze
|
9
|
+
include RudderAnalyticsSync::Logging
|
10
|
+
include RudderAnalyticsSync::Constants
|
10
11
|
|
11
|
-
attr_reader :write_key, :data_plane_url, :error_handler, :stub, :logger, :http_options
|
12
|
+
attr_reader :write_key, :data_plane_url, :error_handler, :stub, :logger, :http_options, :gzip
|
12
13
|
|
13
|
-
def initialize(client)
|
14
|
+
def initialize(client) # rubocop:disable Metrics/AbcSize
|
14
15
|
@write_key = client.config.write_key
|
15
16
|
@data_plane_url = client.config.data_plane_url || BASE_URL
|
16
17
|
@error_handler = client.config.on_error
|
17
18
|
@stub = client.config.stub
|
18
19
|
@logger = client.config.logger
|
19
20
|
@http_options = client.config.http_options
|
21
|
+
@gzip = client.config.gzip.nil? ? true : client.config.gzip
|
20
22
|
end
|
21
23
|
|
22
|
-
def post(path, payload, headers: DEFAULT_HEADERS) # rubocop:disable Metrics/AbcSize
|
24
|
+
def post(path, payload, headers: DEFAULT_HEADERS) # rubocop:disable Metrics/AbcSize
|
23
25
|
response = nil
|
24
26
|
status_code = nil
|
25
27
|
response_body = nil
|
26
28
|
|
27
29
|
uri = URI(data_plane_url)
|
28
|
-
payload = JSON.generate(payload)
|
29
30
|
if stub
|
30
31
|
logger.debug "stubbed request to \
|
31
32
|
#{path}: write key = #{write_key}, \
|
32
|
-
payload = #{payload}"
|
33
|
+
payload = #{JSON.generate(payload)}"
|
33
34
|
|
34
35
|
{ status: 200, error: nil }
|
35
36
|
else
|
37
|
+
if gzip
|
38
|
+
headers = headers.merge(
|
39
|
+
'Content-Encoding': 'gzip'
|
40
|
+
)
|
41
|
+
gzip = Zlib::GzipWriter.new(StringIO.new)
|
42
|
+
gzip << payload.to_json
|
43
|
+
payload = gzip.close.string
|
44
|
+
else
|
45
|
+
payload = JSON.generate(payload)
|
46
|
+
end
|
36
47
|
Net::HTTP.start(uri.host, uri.port, :ENV, http_options) do |http|
|
37
48
|
request = Net::HTTP::Post.new(path, headers)
|
38
49
|
request.basic_auth write_key, nil
|
@@ -9,9 +9,7 @@ module RudderAnalyticsSync
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def symbolize_keys(hash)
|
12
|
-
hash.
|
13
|
-
result[key.to_sym] = value
|
14
|
-
end
|
12
|
+
hash.transform_keys(&:to_sym)
|
15
13
|
end
|
16
14
|
|
17
15
|
# public: Converts all the date values in the into iso8601 strings in place
|
@@ -24,35 +22,35 @@ module RudderAnalyticsSync
|
|
24
22
|
# strings
|
25
23
|
#
|
26
24
|
def isoify_dates(hash)
|
27
|
-
hash.
|
28
|
-
|
25
|
+
hash.transform_values do |v|
|
26
|
+
maybe_datetime_in_iso8601(v)
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
32
30
|
# public: Returns a uid string
|
33
31
|
#
|
34
32
|
def uid
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
33
|
+
arr = SecureRandom.random_bytes(16).unpack('NnnnnN')
|
34
|
+
arr[2] = (arr[2] & 0x0fff) | 0x4000
|
35
|
+
arr[3] = (arr[3] & 0x3fff) | 0x8000
|
36
|
+
'%08x-%04x-%04x-%04x-%04x%08x' % arr # rubocop:disable Style/FormatString
|
39
37
|
end
|
40
38
|
|
41
39
|
def maybe_datetime_in_iso8601(datetime)
|
42
40
|
case datetime
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
41
|
+
when Time
|
42
|
+
time_in_iso8601 datetime
|
43
|
+
when DateTime
|
44
|
+
time_in_iso8601 datetime.to_time
|
45
|
+
when Date
|
46
|
+
date_in_iso8601 datetime
|
47
|
+
else
|
48
|
+
datetime
|
51
49
|
end
|
52
50
|
end
|
53
51
|
|
54
52
|
def time_in_iso8601(time, fraction_digits = 3)
|
55
|
-
fraction = (('.%06i'
|
53
|
+
fraction = (format('.%06i', time.usec)[0, fraction_digits + 1] if fraction_digits.positive?)
|
56
54
|
|
57
55
|
"#{time.strftime('%Y-%m-%dT%H:%M:%S')}#{fraction}#{formatted_offset(time, true, 'Z')}"
|
58
56
|
end
|
@@ -61,12 +59,18 @@ module RudderAnalyticsSync
|
|
61
59
|
date.strftime('%F')
|
62
60
|
end
|
63
61
|
|
64
|
-
def formatted_offset(time, colon = true, alternate_utc_string = nil)
|
65
|
-
time.utc? && alternate_utc_string || seconds_to_utc_offset(time.utc_offset, colon)
|
62
|
+
def formatted_offset(time, colon = true, alternate_utc_string = nil) # rubocop:disable Style/OptionalBooleanParameter
|
63
|
+
(time.utc? && alternate_utc_string) || seconds_to_utc_offset(time.utc_offset, colon)
|
64
|
+
end
|
65
|
+
|
66
|
+
def seconds_to_utc_offset(seconds, colon = true) # rubocop:disable Style/OptionalBooleanParameter
|
67
|
+
format((colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON), (seconds.negative? ? '-' : '+'), (seconds.abs / 3600), ((seconds.abs % 3600) / 60))
|
66
68
|
end
|
67
69
|
|
68
|
-
def
|
69
|
-
(
|
70
|
+
def valid_date?(string)
|
71
|
+
!!(string.match(/\d{4}-\d{2}-\d{2}/) && Date.strptime(string, '%Y-%m-%d'))
|
72
|
+
rescue ArgumentError
|
73
|
+
false
|
70
74
|
end
|
71
75
|
|
72
76
|
UTC_OFFSET_WITH_COLON = '%s%02d:%02d'
|
@@ -24,7 +24,10 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_development_dependency 'pry'
|
25
25
|
spec.add_development_dependency 'rake', '>= 10.0'
|
26
26
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
27
|
-
spec.add_development_dependency 'rubocop', '
|
28
|
-
spec.add_development_dependency 'timecop', '~> 0.
|
27
|
+
spec.add_development_dependency 'rubocop', '1.23.0'
|
28
|
+
spec.add_development_dependency 'timecop', '~> 0.9.5'
|
29
29
|
spec.add_development_dependency 'webmock', '~> 3.7'
|
30
|
+
spec.metadata = {
|
31
|
+
'rubygems_mfa_required' => 'true'
|
32
|
+
}
|
30
33
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rudder_analytics_sync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- RudderStack
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -72,28 +72,28 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - '='
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: 1.23.0
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - '='
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 1.23.0
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: timecop
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 0.
|
89
|
+
version: 0.9.5
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: 0.
|
96
|
+
version: 0.9.5
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: webmock
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -118,16 +118,22 @@ executables: []
|
|
118
118
|
extensions: []
|
119
119
|
extra_rdoc_files: []
|
120
120
|
files:
|
121
|
+
- ".github/dependabot.yml"
|
122
|
+
- ".github/pull_request_template.md"
|
123
|
+
- ".github/workflows/build-and-quality-checks.yml"
|
124
|
+
- ".github/workflows/check-pr-title.yml"
|
125
|
+
- ".github/workflows/notion-pr-sync.yml"
|
126
|
+
- ".github/workflows/slack-notify.yml"
|
121
127
|
- ".gitignore"
|
122
128
|
- ".hound.yml"
|
123
129
|
- ".rspec"
|
124
130
|
- ".rubocop.yml"
|
125
|
-
-
|
126
|
-
- CHANGELOG.md
|
131
|
+
- CODEOWNERS
|
127
132
|
- Gemfile
|
128
133
|
- LICENSE.txt
|
129
134
|
- README.md
|
130
135
|
- Rakefile
|
136
|
+
- SECURITY.md
|
131
137
|
- bin/console
|
132
138
|
- bin/setup
|
133
139
|
- example/example.rb
|
@@ -135,6 +141,7 @@ files:
|
|
135
141
|
- lib/rudder_analytics_sync/batch.rb
|
136
142
|
- lib/rudder_analytics_sync/client.rb
|
137
143
|
- lib/rudder_analytics_sync/configuration.rb
|
144
|
+
- lib/rudder_analytics_sync/constants.rb
|
138
145
|
- lib/rudder_analytics_sync/logging.rb
|
139
146
|
- lib/rudder_analytics_sync/operations.rb
|
140
147
|
- lib/rudder_analytics_sync/operations/alias.rb
|
@@ -151,7 +158,8 @@ files:
|
|
151
158
|
homepage: https://github.com/rudderlabs/rudder-sdk-ruby-sync
|
152
159
|
licenses:
|
153
160
|
- MIT
|
154
|
-
metadata:
|
161
|
+
metadata:
|
162
|
+
rubygems_mfa_required: 'true'
|
155
163
|
post_install_message:
|
156
164
|
rdoc_options: []
|
157
165
|
require_paths:
|
@@ -167,7 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
175
|
- !ruby/object:Gem::Version
|
168
176
|
version: '0'
|
169
177
|
requirements: []
|
170
|
-
rubygems_version: 3.0.3
|
178
|
+
rubygems_version: 3.0.3.1
|
171
179
|
signing_key:
|
172
180
|
specification_version: 4
|
173
181
|
summary: Privacy and Security focused Segment-alternative. Ruby SDK (sync)
|