rudder-sdk-ruby 0.0.5 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bcce6b53dd45a0b6b4f19fbd9de5e96bf2d10ff1747d105aa734407dff704b6e
4
- data.tar.gz: 65b3a4d75753ff880fb48bf02a398176ccd7aa04b889b90c84ea65e36d55272a
3
+ metadata.gz: 3b095c62f2f1876eac8bece177cbc292ed6686ea13d6ba6988b7e8d5cdf17ef7
4
+ data.tar.gz: 8e5a23ec4064547130a6e9ed4c349a5278b51fca95cbe48d9df033696252e6a8
5
5
  SHA512:
6
- metadata.gz: c4e727dd53858f6b413278088447181fb184bd5d5ed7d7b9b129b07a20073e73c44cb0e26a4792bacaedb3349af3859403264a5ee5fee96cb61b704341c93a79
7
- data.tar.gz: 1ee11c6663d4edf1ac1237c4a2aa5872b7f4da009467bdc93fd0c2cde9d7083f6cdb499d73b6290cf4edb212dd9e93154ce7f80d45bee9bfe75b6372c84a81f6
6
+ metadata.gz: 4022a4715a1081a836001135496fc7770eaa734fd760607230eef3668d438ee8adad94b91e7e78fbf31051328d1ae3507a3b9221b6c6d7d8bd4a4165f5290ca9
7
+ data.tar.gz: fb34b8f93df5589eb0cabecd6b22b61e15092df4c661bd244d864978ffc4e816baa311d97d41ed9dbbbadcfe0950b7a8f81704d6fd4594cccbd970cff9afd60d
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ # !/usr/bin/env ruby
4
+
5
+ require 'rudder/analytics'
6
+ require 'rubygems'
7
+ require 'commander/import'
8
+ require 'time'
9
+ require 'json'
10
+ require 'yaml'
11
+
12
+ program :name, 'simulator.rb'
13
+ program :version, '0.0.1'
14
+ program :description, 'scripting simulator'
15
+
16
+ def json_hash(str)
17
+ return JSON.parse(str) if str
18
+ end
19
+
20
+ # Run in dev with ruby -ilib bin/analytics_rudder.rb --writeKey=<write_key> --dataPlaneUrl=<data_plane_url>
21
+ # --type=track --userId=123456 --event=Test --properties={\"key_1\" => \"value_1\"} --trace
22
+
23
+ default_command :send
24
+
25
+ command :send do |c|
26
+ c.description = 'send a Rudder message'
27
+
28
+ c.option '--writeKey=<writeKey>', String, 'the Rudder writeKey'
29
+ c.option '--dataPlaneUrl=<dataPlaneUrl>', String, 'the Rudder data plane URL'
30
+ c.option '--type=<type>', String, 'The Rudder message type'
31
+
32
+ c.option '--userId=<userId>', String, 'the user id to send the event as'
33
+ c.option '--anonymousId=<anonymousId>', String, 'the anonymous user id to send the event as'
34
+ c.option '--context=<context>', 'additional context for the event (JSON-encoded)'
35
+ c.option '--integrations=<integrations>', 'additional integrations for the event (JSON-encoded)'
36
+
37
+ c.option '--event=<event>', String, 'the event name to send with the event'
38
+ c.option '--properties=<properties>', 'the event properties to send (JSON-encoded)'
39
+
40
+ c.option '--name=<name>', 'name of the screen or page to send with the message'
41
+
42
+ c.option '--traits=<traits>', 'the identify/group traits to send (JSON-encoded)'
43
+
44
+ c.option '--groupId=<groupId>', String, 'the group id'
45
+ c.option '--previousId=<previousId>', String, 'the previous id'
46
+
47
+ c.action do |_, options|
48
+ Analytics = Rudder::Analytics.new({
49
+ :write_key => options.writeKey,
50
+ :data_plane_url => options.dataPlaneUrl,
51
+ :on_error => proc { |_status, msg| print msg }
52
+ })
53
+
54
+ case options.type
55
+ when 'track'
56
+ Analytics.track({
57
+ :user_id => options.userId,
58
+ :event => options.event,
59
+ :anonymous_id => options.anonymousId,
60
+ :properties => json_hash(options.properties),
61
+ :context => json_hash(options.context),
62
+ :integrations => json_hash(options.integrations)
63
+ })
64
+ when 'page'
65
+ Analytics.page({
66
+ :user_id => options.userId,
67
+ :anonymous_id => options.anonymousId,
68
+ :name => options.name,
69
+ :properties => json_hash(options.properties),
70
+ :context => json_hash(options.context),
71
+ :integrations => json_hash(options.integrations)
72
+ })
73
+ when 'screen'
74
+ Analytics.screen({
75
+ :user_id => options.userId,
76
+ :anonymous_id => options.anonymousId,
77
+ :name => options.name,
78
+ :properties => json_hash(options.properties),
79
+ :context => json_hash(options.context),
80
+ :integrations => json_hash(options.integrations)
81
+ })
82
+ when 'identify'
83
+ Analytics.identify({
84
+ :user_id => options.userId,
85
+ :anonymous_id => options.anonymousId,
86
+ :traits => json_hash(options.traits),
87
+ :context => json_hash(options.context),
88
+ :integrations => json_hash(options.integrations)
89
+ })
90
+ when 'group'
91
+ Analytics.group({
92
+ :user_id => options.userId,
93
+ :anonymous_id => options.anonymousId,
94
+ :group_id => options.groupId,
95
+ :traits => json_hash(options.traits),
96
+ :context => json_hash(options.context),
97
+ :integrations => json_hash(options.integrations)
98
+ })
99
+ when 'alias'
100
+ Analytics.alias({
101
+ :previous_id => options.previousId,
102
+ :user_id => options.userId,
103
+ :anonymous_id => options.anonymousId,
104
+ :context => json_hash(options.context),
105
+ :integrations => json_hash(options.integrations)
106
+ })
107
+ else
108
+ raise "Invalid Message Type #{options.type}"
109
+ end
110
+ Analytics.flush
111
+ end
112
+ end
@@ -8,6 +8,8 @@ require 'rudder/analytics/logging'
8
8
  require 'rudder/analytics/utils'
9
9
  require 'rudder/analytics/worker'
10
10
  require 'rudder/analytics/defaults'
11
+ require 'rudder/analytics/configuration'
12
+ require 'rudder/analytics/test_queue'
11
13
  require 'net/http'
12
14
 
13
15
  module Rudder
@@ -23,23 +25,11 @@ module Rudder
23
25
  # remain queued.
24
26
  # @option opts [Proc] :on_error Handles error calls from the API.
25
27
  def initialize(opts = {})
26
- symbolize_keys!(opts)
27
-
28
+ @config = Configuration.new(opts)
28
29
  @queue = Queue.new
29
- @write_key = opts[:write_key]
30
- @data_plane_url = opts[:data_plane_url]
31
- @max_queue_size = opts[:max_queue_size] || Defaults::Queue::MAX_SIZE
32
30
  @worker_mutex = Mutex.new
33
- @worker = Worker.new(@queue, @data_plane_url, @write_key, opts)
31
+ @worker = Worker.new(@queue, @config)
34
32
  @worker_thread = nil
35
-
36
- uri = URI(opts[:data_plane_url])
37
-
38
- @host = uri.host
39
- @port = uri.port
40
-
41
- check_write_key!
42
-
43
33
  at_exit { @worker_thread && @worker_thread[:should_exit] = true }
44
34
  end
45
35
 
@@ -155,6 +145,12 @@ module Rudder
155
145
  @queue.length
156
146
  end
157
147
 
148
+ def test_queue
149
+ raise 'Test queue only available when setting :test to true.' unless @config.test
150
+
151
+ @test_queue ||= TestQueue.new
152
+ end
153
+
158
154
  private
159
155
 
160
156
  # private: Enqueues the action.
@@ -165,25 +161,33 @@ module Rudder
165
161
  # add our request id for tracing purposes
166
162
  action[:messageId] ||= uid
167
163
 
168
- if @queue.length < @max_queue_size
164
+ if @config.test
165
+ test_queue << action
166
+ return true
167
+ end
168
+
169
+ if @queue.length < @config.max_queue_size
169
170
  @queue << action
170
171
  ensure_worker_running
171
172
 
172
173
  true
173
174
  else
174
175
  logger.warn(
175
- 'Queue is full, dropping events. The :max_queue_size ' \
176
- 'configuration parameter can be increased to prevent this from ' \
177
- 'happening.'
176
+ 'Queue is full, dropping events. The :max_queue_size configuration parameter can be increased to prevent this from happening.'
178
177
  )
179
178
  false
180
179
  end
181
180
  end
182
181
 
183
182
  # private: Checks that the write_key is properly initialized
184
- def check_write_key!
185
- raise ArgumentError, 'Write key must be initialized' if @write_key.nil?
186
- end
183
+ # def check_write_key!
184
+ # raise ArgumentError, 'Write key must be initialized' if @write_key.nil?
185
+ # end
186
+
187
+ # private: Checks that the data_plane_url is properly initialized
188
+ # def check_data_plane_url!
189
+ # raise ArgumentError, 'Data plane url must be initialized' if @data_plane_url.nil?
190
+ # end
187
191
 
188
192
  def ensure_worker_running
189
193
  return if worker_running?
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rudder/analytics/utils'
4
+
5
+ module Rudder
6
+ class Analytics
7
+ class Configuration
8
+ include Rudder::Analytics::Utils
9
+
10
+ attr_reader :write_key, :data_plane_url, :on_error, :stub, :gzip, :ssl, :batch_size, :test, :max_queue_size, :backoff_policy, :retries
11
+
12
+ def initialize(settings = {})
13
+ symbolized_settings = symbolize_keys(settings)
14
+
15
+ @test = symbolized_settings[:test]
16
+ @write_key = symbolized_settings[:write_key]
17
+ @data_plane_url = symbolized_settings[:data_plane_url]
18
+ @max_queue_size = symbolized_settings[:max_queue_size] || Defaults::Queue::MAX_SIZE
19
+ @ssl = symbolized_settings[:ssl]
20
+ @on_error = symbolized_settings[:on_error] || proc { |status, error| }
21
+ @stub = symbolized_settings[:stub]
22
+ @batch_size = symbolized_settings[:batch_size] || Defaults::MessageBatch::MAX_SIZE
23
+ @gzip = symbolized_settings[:gzip]
24
+ @backoff_policy = symbolized_settings[:backoff_policy]
25
+ @retries = symbolized_settings[:retries]
26
+ raise ArgumentError, 'Missing required option :write_key' \
27
+ unless @write_key
28
+ raise ArgumentError, 'Data plane url must be initialized' \
29
+ unless @data_plane_url
30
+ end
31
+ end
32
+ end
33
+ end
@@ -7,11 +7,9 @@ module Rudder
7
7
  HOST = 'localhost'
8
8
  PORT = 8080
9
9
  PATH = '/v1/batch'
10
- DATA_PLANE_URL = 'http://localhost:8080/v1/batch'
11
- SSL = false
12
10
  HEADERS = { 'Accept' => 'application/json',
13
11
  'Content-Type' => 'application/json',
14
- 'User-Agent' => "rudderanalytics-ruby/#{Analytics::VERSION}" }
12
+ 'Content-Encoding' => 'gzip' }
15
13
  RETRIES = 10
16
14
  end
17
15
 
@@ -2,10 +2,10 @@
2
2
 
3
3
  module Rudder
4
4
  class Analytics
5
- # Handles parsing fields according to the Segment Spec
5
+ # Handles parsing fields according to the RudderStack Spec
6
6
  #
7
- # @see https://segment.com/docs/spec/
8
- class FieldParser
7
+ # @see https://www.rudderstack.com/docs/event-spec/standard-events/
8
+ class FieldParser # rubocop:disable Metrics/ClassLength
9
9
  class << self
10
10
  include Rudder::Analytics::Utils
11
11
 
@@ -17,17 +17,11 @@ module Rudder
17
17
  common = parse_common_fields(fields)
18
18
 
19
19
  event = fields[:event]
20
- properties = fields[:properties] || {}
21
-
22
20
  check_presence!(event, 'event')
23
- check_is_hash!(properties, 'properties')
24
-
25
- isoify_dates! properties
26
21
 
27
22
  common.merge({
28
23
  :type => 'track',
29
- :event => event.to_s,
30
- :properties => properties
24
+ :event => event.to_s
31
25
  })
32
26
  end
33
27
 
@@ -37,13 +31,18 @@ module Rudder
37
31
  def parse_for_identify(fields)
38
32
  common = parse_common_fields(fields)
39
33
 
40
- traits = fields[:traits] || {}
41
- check_is_hash!(traits, 'traits')
42
- isoify_dates! traits
34
+ # add the traits if present
35
+ if fields[:traits]
36
+ traits = fields[:traits]
37
+ context = common[:context].merge({ :traits => traits })
38
+
39
+ common = common.merge({
40
+ :context => context
41
+ })
42
+ end
43
43
 
44
44
  common.merge({
45
- :type => 'identify',
46
- :traits => traits
45
+ :type => 'identify'
47
46
  })
48
47
  end
49
48
 
@@ -55,6 +54,7 @@ module Rudder
55
54
 
56
55
  previous_id = fields[:previous_id]
57
56
  check_presence!(previous_id, 'previous_id')
57
+ check_string(previous_id, 'previous_id')
58
58
 
59
59
  common.merge({
60
60
  :type => 'alias',
@@ -70,17 +70,12 @@ module Rudder
70
70
  common = parse_common_fields(fields)
71
71
 
72
72
  group_id = fields[:group_id]
73
- traits = fields[:traits] || {}
74
-
75
73
  check_presence!(group_id, 'group_id')
76
- check_is_hash!(traits, 'traits')
77
-
78
- isoify_dates! traits
74
+ check_string(group_id, 'group_id')
79
75
 
80
76
  common.merge({
81
77
  :type => 'group',
82
- :groupId => group_id,
83
- :traits => traits
78
+ :groupId => group_id
84
79
  })
85
80
  end
86
81
 
@@ -92,15 +87,13 @@ module Rudder
92
87
  common = parse_common_fields(fields)
93
88
 
94
89
  name = fields[:name] || ''
95
- properties = fields[:properties] || {}
96
-
97
- check_is_hash!(properties, 'properties')
98
-
99
- isoify_dates! properties
90
+ properties = common[:properties] || {}
91
+ properties = properties.merge({ :name => name })
100
92
 
101
93
  common.merge({
102
94
  :type => 'page',
103
- :name => name.to_s,
95
+ :name => name,
96
+ :event => name,
104
97
  :properties => properties
105
98
  })
106
99
  end
@@ -109,22 +102,21 @@ module Rudder
109
102
  #
110
103
  # - "name"
111
104
  # - "properties"
112
- # - "category" (Not in spec, retained for backward compatibility"
113
105
  def parse_for_screen(fields)
114
106
  common = parse_common_fields(fields)
115
107
 
116
108
  name = fields[:name]
117
- properties = fields[:properties] || {}
109
+ properties = common[:properties] || {}
110
+ properties = properties.merge({ :name => name })
111
+
118
112
  category = fields[:category]
119
113
 
120
114
  check_presence!(name, 'name')
121
- check_is_hash!(properties, 'properties')
122
-
123
- isoify_dates! properties
124
115
 
125
116
  parsed = common.merge({
126
117
  :type => 'screen',
127
118
  :name => name,
119
+ :event => name,
128
120
  :properties => properties
129
121
  })
130
122
 
@@ -135,40 +127,68 @@ module Rudder
135
127
 
136
128
  private
137
129
 
138
- def parse_common_fields(fields)
139
- timestamp = fields[:timestamp] || Time.new
140
- message_id = fields[:message_id].to_s if fields[:message_id]
141
- context = fields[:context] || {}
142
-
130
+ def parse_common_fields(fields) # rubocop:disable Metrics/AbcSize Metrics/CyclomaticComplexity Metrics/PerceivedComplexity
143
131
  check_user_id! fields
132
+
133
+ current_time = Time.now.utc
134
+ timestamp = fields[:timestamp] || current_time
144
135
  check_timestamp! timestamp
145
136
 
137
+ context = fields[:context] || {}
138
+ delete_library_from_context! context
146
139
  add_context! context
147
140
 
148
141
  parsed = {
149
142
  :context => context,
150
- :messageId => message_id,
151
- :timestamp => datetime_in_iso8601(timestamp)
143
+ :integrations => fields[:integrations] || { :All => true },
144
+ :timestamp => datetime_in_iso8601(timestamp),
145
+ :sentAt => datetime_in_iso8601(current_time),
146
+ :messageId => fields[:message_id] || uid,
147
+ :channel => 'server'
152
148
  }
153
149
 
154
- parsed[:userId] = fields[:user_id] if fields[:user_id]
155
- parsed[:anonymousId] = fields[:anonymous_id] if fields[:anonymous_id]
156
- parsed[:integrations] = fields[:integrations] if fields[:integrations]
157
-
158
- # Not in spec, retained for backward compatibility
159
- parsed[:options] = fields[:options] if fields[:options]
160
-
150
+ # add the userId if present
151
+ if fields[:user_id]
152
+ check_string(fields[:user_id], 'user_id')
153
+ parsed = parsed.merge({ :userId => fields[:user_id] })
154
+ end
155
+ # add the anonymousId if present
156
+ if fields[:anonymous_id]
157
+ check_string(fields[:anonymous_id], 'anonymous_id')
158
+ parsed = parsed.merge({ :anonymousId => fields[:anonymous_id] })
159
+ end
160
+ # add the properties if present
161
+ if fields[:properties]
162
+ properties = fields[:properties]
163
+ check_is_hash!(properties, 'properties')
164
+ isoify_dates! properties
165
+ parsed = parsed.merge({ :properties => properties })
166
+ end
167
+ # add the traits if present
168
+ if fields[:traits]
169
+ traits = fields[:traits]
170
+ check_is_hash!(traits, 'traits')
171
+ isoify_dates! traits
172
+ parsed = parsed.merge({ :traits => traits })
173
+ end
161
174
  parsed
162
175
  end
163
176
 
164
177
  def check_user_id!(fields)
165
- raise ArgumentError, 'Must supply either user_id or anonymous_id' unless fields[:user_id] || fields[:anonymous_id]
178
+ return unless blank?(fields[:user_id])
179
+ return unless blank?(fields[:anonymous_id])
180
+
181
+ raise ArgumentError, 'Must supply either user_id or anonymous_id'
166
182
  end
167
183
 
168
184
  def check_timestamp!(timestamp)
169
185
  raise ArgumentError, 'Timestamp must be a Time' unless timestamp.is_a? Time
170
186
  end
171
187
 
188
+ def delete_library_from_context!(context)
189
+ context.delete(:library) if context # rubocop:disable Style/SafeNavigation
190
+ end
191
+
172
192
  def add_context!(context)
173
193
  context[:library] = { :name => 'rudderanalytics-ruby', :version => Rudder::Analytics::VERSION.to_s }
174
194
  end
@@ -178,7 +198,11 @@ module Rudder
178
198
  # obj - String|Number that must be non-blank
179
199
  # name - Name of the validated value
180
200
  def check_presence!(obj, name)
181
- raise ArgumentError, "#{name} must be given" if obj.nil? || (obj.is_a?(String) && obj.empty?)
201
+ raise ArgumentError, "#{name} must be given" if blank?(obj)
202
+ end
203
+
204
+ def blank?(obj)
205
+ obj.nil? || (obj.is_a?(String) && obj.empty?)
182
206
  end
183
207
 
184
208
  def check_is_hash!(obj, name)
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rudder
4
+ class Analytics
5
+ class TestQueue
6
+ attr_reader :messages
7
+
8
+ def initialize
9
+ reset!
10
+ end
11
+
12
+ def [](key)
13
+ all[key]
14
+ end
15
+
16
+ def count
17
+ all.count
18
+ end
19
+
20
+ def <<(message)
21
+ all << message
22
+ send(message[:type]) << message
23
+ end
24
+
25
+ def alias
26
+ messages[:alias] ||= []
27
+ end
28
+
29
+ def all
30
+ messages[:all] ||= []
31
+ end
32
+
33
+ def group
34
+ messages[:group] ||= []
35
+ end
36
+
37
+ def identify
38
+ messages[:identify] ||= []
39
+ end
40
+
41
+ def page
42
+ messages[:page] ||= []
43
+ end
44
+
45
+ def screen
46
+ messages[:screen] ||= []
47
+ end
48
+
49
+ def track
50
+ messages[:track] ||= []
51
+ end
52
+
53
+ def reset!
54
+ @messages = {}
55
+ end
56
+ end
57
+ end
58
+ end
@@ -10,48 +10,38 @@ require 'net/https'
10
10
  require 'json'
11
11
  require 'pry'
12
12
  require 'uri'
13
+ require 'zlib'
13
14
 
14
15
  module Rudder
15
16
  class Analytics
16
- class Request
17
+ class Transport
17
18
  include Rudder::Analytics::Defaults::Request
18
19
  include Rudder::Analytics::Utils
19
20
  include Rudder::Analytics::Logging
20
21
 
21
- # public: Creates a new request object to send analytics batch
22
- #
23
- def initialize(options = {})
24
- options[:host] ||= HOST
25
- options[:port] ||= PORT
26
- options[:ssl] ||= SSL
27
- @headers = options[:headers] || HEADERS
28
- @path = options[:path] || PATH
29
- @retries = options[:retries] || RETRIES
30
- @backoff_policy =
31
- options[:backoff_policy] || Rudder::Analytics::BackoffPolicy.new
32
-
33
- uri = URI(options[:data_plane_url] || DATA_PLANE_URL)
34
- # printf("************\n")
35
- # printf("************\n")
36
- # printf(options[:data_plane_url] || DATA_PLANE_URL)
37
- # printf("\n************\n")
38
- # printf(uri.host)
39
- # printf("\n************\n")
40
- # printf(uri.port.to_s)
41
- # printf("************\n")
22
+ attr_reader :stub
23
+
24
+ def initialize(config)
25
+ @stub = config.stub || false
26
+ @path = PATH
27
+ @retries = config.retries || RETRIES
28
+ @backoff_policy = config.backoff_policy || Rudder::Analytics::BackoffPolicy.new
29
+
30
+ uri = URI(config.data_plane_url)
42
31
 
43
32
  http = Net::HTTP.new(uri.host, uri.port)
44
- http.use_ssl = options[:ssl]
33
+ http.use_ssl = config.ssl || true
45
34
  http.read_timeout = 8
46
35
  http.open_timeout = 4
47
36
 
48
37
  @http = http
38
+ @gzip = config.gzip.nil? ? true : config.gzip
49
39
  end
50
40
 
51
- # public: Posts the write key and batch of messages to the API.
41
+ # Sends a batch of messages to the API
52
42
  #
53
- # returns - Response of the status and error if it exists
54
- def post(write_key, batch)
43
+ # @return [Response] API response
44
+ def send(write_key, batch)
55
45
  logger.debug("Sending request for #{batch.length} items")
56
46
 
57
47
  last_response, exception = retry_with_backoff(@retries) do
@@ -81,6 +71,11 @@ module Rudder
81
71
  end
82
72
  end
83
73
 
74
+ # Closes a persistent connection if it exists
75
+ def shutdown
76
+ @http.finish if @http.started?
77
+ end
78
+
84
79
  private
85
80
 
86
81
  def should_retry_request?(status_code, body)
@@ -126,32 +121,34 @@ module Rudder
126
121
 
127
122
  # Sends a request for the batch, returns [status_code, body]
128
123
  def send_request(write_key, batch)
129
- payload = JSON.generate(
130
- :sentAt => datetime_in_iso8601(Time.now),
131
- :batch => batch
132
- )
133
- request = Net::HTTP::Post.new(@path, @headers)
134
- request.basic_auth(write_key, nil)
135
-
136
- if self.class.stub
124
+ if stub
137
125
  logger.debug "stubbed request to #{@path}: " \
138
126
  "write key = #{write_key}, batch = #{JSON.generate(batch)}"
139
127
 
140
128
  [200, '{}']
141
129
  else
142
- puts payload
130
+ payload = {
131
+ :batch => batch
132
+ }
133
+
134
+ headers = HEADERS
135
+
136
+ if @gzip
137
+ gzip = Zlib::GzipWriter.new(StringIO.new)
138
+ gzip << payload.to_json
139
+ payload = gzip.close.string
140
+ else
141
+ headers.delete('Content-Encoding')
142
+ payload = JSON.generate(payload)
143
+ end
144
+
145
+ request = Net::HTTP::Post.new(@path, headers)
146
+ request.basic_auth(write_key, nil)
147
+ @http.start unless @http.started? # Maintain a persistent connection
143
148
  response = @http.request(request, payload)
144
149
  [response.code.to_i, response.body]
145
150
  end
146
151
  end
147
-
148
- class << self
149
- attr_writer :stub
150
-
151
- def stub
152
- @stub || ENV['STUB']
153
- end
154
- end
155
152
  end
156
153
  end
157
154
  end
@@ -10,9 +10,7 @@ module Rudder
10
10
  # public: Return a new hash with keys converted from strings to symbols
11
11
  #
12
12
  def symbolize_keys(hash)
13
- hash.each_with_object({}) do |(k, v), memo|
14
- memo[k.to_sym] = v
15
- end
13
+ hash.transform_keys(&:to_sym)
16
14
  end
17
15
 
18
16
  # public: Convert hash keys from strings to symbols in place
@@ -50,7 +48,7 @@ module Rudder
50
48
  arr = SecureRandom.random_bytes(16).unpack('NnnnnN')
51
49
  arr[2] = (arr[2] & 0x0fff) | 0x4000
52
50
  arr[3] = (arr[3] & 0x3fff) | 0x8000
53
- '%08x-%04x-%04x-%04x-%04x%08x' % arr
51
+ '%08x-%04x-%04x-%04x-%04x%08x' % arr # rubocop:disable Style/FormatStringToken
54
52
  end
55
53
 
56
54
  def datetime_in_iso8601(datetime)
@@ -66,10 +64,8 @@ module Rudder
66
64
  end
67
65
  end
68
66
 
69
- def time_in_iso8601(time, fraction_digits = 3)
70
- fraction = (('.%06i' % time.usec)[0, fraction_digits + 1] if fraction_digits > 0)
71
-
72
- "#{time.strftime('%Y-%m-%dT%H:%M:%S')}#{fraction}#{formatted_offset(time, true, 'Z')}"
67
+ def time_in_iso8601(time)
68
+ "#{time.strftime('%Y-%m-%dT%H:%M:%S.%3N')}#{formatted_offset(time, true, 'Z')}"
73
69
  end
74
70
 
75
71
  def date_in_iso8601(date)
@@ -81,7 +77,11 @@ module Rudder
81
77
  end
82
78
 
83
79
  def seconds_to_utc_offset(seconds, colon = true)
84
- (colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON) % [(seconds < 0 ? '-' : '+'), (seconds.abs / 3600), ((seconds.abs % 3600) / 60)]
80
+ (colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON) % [(seconds.negative? ? '-' : '+'), (seconds.abs / 3600), ((seconds.abs % 3600) / 60)]
81
+ end
82
+
83
+ def check_string(obj, name)
84
+ raise ArgumentError, "#{name} must be a String" unless obj.is_a? String
85
85
  end
86
86
 
87
87
  UTC_OFFSET_WITH_COLON = '%s%02d:%02d'
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rudder
4
4
  class Analytics
5
- VERSION = '0.0.5'
5
+ VERSION = '2.0.0'
6
6
  end
7
7
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'rudder/analytics/defaults'
4
4
  require 'rudder/analytics/message_batch'
5
- require 'rudder/analytics/request'
5
+ require 'rudder/analytics/transport'
6
6
  require 'rudder/analytics/utils'
7
7
 
8
8
  module Rudder
@@ -23,16 +23,15 @@ module Rudder
23
23
  # batch_size - Fixnum of how many items to send in a batch
24
24
  # on_error - Proc of what to do on an error
25
25
  #
26
- def initialize(queue, data_plane_url, write_key, options = {})
27
- symbolize_keys! options
26
+ def initialize(queue, config)
28
27
  @queue = queue
29
- @data_plane_url = data_plane_url
30
- @write_key = write_key
31
- @ssl = options[:ssl]
32
- @on_error = options[:on_error] || proc { |status, error| }
33
- batch_size = options[:batch_size] || Defaults::MessageBatch::MAX_SIZE
34
- @batch = MessageBatch.new(batch_size)
28
+ @data_plane_url = config.data_plane_url
29
+ @write_key = config.write_key
30
+ @ssl = config.ssl
31
+ @on_error = config.on_error
32
+ @batch = MessageBatch.new(config.batch_size)
35
33
  @lock = Mutex.new
34
+ @transport = Transport.new(config)
36
35
  end
37
36
 
38
37
  # public: Continuously runs the loop to check for new events
@@ -45,11 +44,14 @@ module Rudder
45
44
  consume_message_from_queue! until @batch.full? || @queue.empty?
46
45
  end
47
46
 
48
- res = Request.new(:data_plane_url => @data_plane_url, :ssl => @ssl).post @write_key, @batch
47
+ # res = Request.new(:data_plane_url => @data_plane_url, :ssl => @ssl).post @write_key, @batch
48
+ res = @transport.send @write_key, @batch
49
49
  @on_error.call(res.status, res.error) unless res.status == 200
50
50
 
51
51
  @lock.synchronize { @batch.clear }
52
52
  end
53
+ ensure
54
+ @transport.shutdown
53
55
  end
54
56
 
55
57
  # public: Check whether we have outstanding requests.
@@ -6,7 +6,7 @@ require 'rudder/analytics/utils'
6
6
  require 'rudder/analytics/field_parser'
7
7
  require 'rudder/analytics/client'
8
8
  require 'rudder/analytics/worker'
9
- require 'rudder/analytics/request'
9
+ require 'rudder/analytics/transport'
10
10
  require 'rudder/analytics/response'
11
11
  require 'rudder/analytics/logging'
12
12
 
@@ -20,7 +20,6 @@ module Rudder
20
20
  # @option options [Boolean] :stub (false) If true, requests don't hit the
21
21
  # server and are stubbed to be successful.
22
22
  def initialize(options = {})
23
- Request.stub = options[:stub] if options.has_key?(:stub)
24
23
  @client = Rudder::Analytics::Client.new options
25
24
  end
26
25
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rudder-sdk-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rudder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-10 00:00:00.000000000 Z
11
+ date: 2023-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: commander
@@ -137,24 +137,26 @@ dependencies:
137
137
  - !ruby/object:Gem::Version
138
138
  version: 0.1.4
139
139
  description: The Rudder ruby analytics library
140
- email: soumya@rudderlabs.com
140
+ email: arnab@rudderstack.com
141
141
  executables:
142
- - analytics-rudder.rb
142
+ - analytics_rudder.rb
143
143
  extensions: []
144
144
  extra_rdoc_files: []
145
145
  files:
146
- - bin/analytics-rudder.rb
146
+ - bin/analytics_rudder.rb
147
147
  - lib/rudder-sdk-ruby.rb
148
148
  - lib/rudder.rb
149
149
  - lib/rudder/analytics.rb
150
150
  - lib/rudder/analytics/backoff_policy.rb
151
151
  - lib/rudder/analytics/client.rb
152
+ - lib/rudder/analytics/configuration.rb
152
153
  - lib/rudder/analytics/defaults.rb
153
154
  - lib/rudder/analytics/field_parser.rb
154
155
  - lib/rudder/analytics/logging.rb
155
156
  - lib/rudder/analytics/message_batch.rb
156
- - lib/rudder/analytics/request.rb
157
157
  - lib/rudder/analytics/response.rb
158
+ - lib/rudder/analytics/test_queue.rb
159
+ - lib/rudder/analytics/transport.rb
158
160
  - lib/rudder/analytics/utils.rb
159
161
  - lib/rudder/analytics/version.rb
160
162
  - lib/rudder/analytics/worker.rb
@@ -177,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
177
179
  - !ruby/object:Gem::Version
178
180
  version: '0'
179
181
  requirements: []
180
- rubygems_version: 3.0.3
182
+ rubygems_version: 3.0.3.1
181
183
  signing_key:
182
184
  specification_version: 4
183
185
  summary: Rudder analytics library
@@ -1,111 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'rudder/analytics'
4
- require 'rubygems'
5
- require 'commander/import'
6
- require 'time'
7
- require 'json'
8
-
9
- program :name, 'simulator.rb'
10
- program :version, '0.0.1'
11
- program :description, 'scripting simulator'
12
-
13
- def json_hash(str)
14
- if str
15
- return JSON.parse(str)
16
- end
17
- end
18
-
19
- # analytics -method=<method> -segment-write-key=<segmentWriteKey> [options]
20
- # Run in dev with ruby -ilib bin/analytics -method=<method> -segment-write-key=<segmentWriteKey> [options]
21
-
22
- default_command :send
23
-
24
- command :send do |c|
25
- c.description = 'send a segment message'
26
-
27
- c.option '--writeKey=<writeKey>', String, 'the Rudder writeKey'
28
- c.option '--dataPlaneUrl=<dataPlaneUrl>', String, 'the Rudder data plane URL'
29
- c.option '--type=<type>', String, 'The Segment message type'
30
-
31
- c.option '--userId=<userId>', String, 'the user id to send the event as'
32
- c.option '--anonymousId=<anonymousId>', String, 'the anonymous user id to send the event as'
33
- c.option '--context=<context>', 'additional context for the event (JSON-encoded)'
34
- c.option '--integrations=<integrations>', 'additional integrations for the event (JSON-encoded)'
35
-
36
- c.option '--event=<event>', String, 'the event name to send with the event'
37
- c.option '--properties=<properties>', 'the event properties to send (JSON-encoded)'
38
-
39
- c.option '--name=<name>', 'name of the screen or page to send with the message'
40
-
41
- c.option '--traits=<traits>', 'the identify/group traits to send (JSON-encoded)'
42
-
43
- c.option '--groupId=<groupId>', String, 'the group id'
44
- c.option '--previousId=<previousId>', String, 'the previous id'
45
-
46
- c.action do |args, options|
47
- Analytics = Rudder::Analytics.new({
48
- write_key: options.writeKey,
49
- data_plane_url: options.dataPlaneUrl,
50
- on_error: Proc.new { |status, msg| print msg }
51
- })
52
-
53
- case options.type
54
- when "track"
55
- Analytics.track({
56
- user_id: options.userId,
57
- event: options.event,
58
- anonymous_id: options.anonymousId,
59
- properties: json_hash(options.properties),
60
- context: json_hash(options.context),
61
- integrations: json_hash(options.integrations)
62
- })
63
- when "page"
64
- Analytics.page({
65
- user_id: options.userId,
66
- anonymous_id: options.anonymousId,
67
- name: options.name,
68
- properties: json_hash(options.properties),
69
- context: json_hash(options.context),
70
- integrations: json_hash(options.integrations)
71
- })
72
- when "screen"
73
- Analytics.screen({
74
- user_id: options.userId,
75
- anonymous_id: options.anonymousId,
76
- name: options.name,
77
- properties: json_hash(options.properties),
78
- context: json_hash(options.context),
79
- integrations: json_hash(options.integrations)
80
- })
81
- when "identify"
82
- Analytics.identify({
83
- user_id: options.userId,
84
- anonymous_id: options.anonymousId,
85
- traits: json_hash(options.traits),
86
- context: json_hash(options.context),
87
- integrations: json_hash(options.integrations)
88
- })
89
- when "group"
90
- Analytics.group({
91
- user_id: options.userId,
92
- anonymous_id: options.anonymousId,
93
- group_id: options.groupId,
94
- traits: json_hash(options.traits),
95
- context: json_hash(options.context),
96
- integrations: json_hash(options.integrations)
97
- })
98
- when "alias"
99
- Analytics.alias({
100
- previous_id: options.previousId,
101
- user_id: options.userId,
102
- anonymous_id: options.anonymousId,
103
- context: json_hash(options.context),
104
- integrations: json_hash(options.integrations)
105
- })
106
- else
107
- raise "Invalid Message Type #{options.type}"
108
- end
109
- Analytics.flush
110
- end
111
- end