analytics-ruby 1.1.0 → 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.
@@ -1,76 +0,0 @@
1
-
2
- require 'analytics-ruby/defaults'
3
- require 'analytics-ruby/request'
4
- require 'analytics-ruby/util'
5
-
6
- module AnalyticsRuby
7
-
8
- class Consumer
9
-
10
- # public: Creates a new consumer
11
- #
12
- # The consumer continuously takes messages off the queue
13
- # and makes requests to the segment.io api
14
- #
15
- # queue - Queue synchronized between client and consumer
16
- # secret - String of the project's secret
17
- # options - Hash of consumer options
18
- # batch_size - Fixnum of how many items to send in a batch
19
- # on_error - Proc of what to do on an error
20
- #
21
- def initialize(queue, secret, options = {})
22
- Util.symbolize_keys! options
23
-
24
- @queue = queue
25
- @secret = secret
26
- @batch_size = options[:batch_size] || Defaults::Queue::BATCH_SIZE
27
- @on_error = options[:on_error] || Proc.new { |status, error| }
28
-
29
- @current_batch = []
30
-
31
- @mutex = Mutex.new
32
- end
33
-
34
- # public: Continuously runs the loop to check for new events
35
- #
36
- def run
37
- until Thread.current[:should_exit]
38
- flush
39
- end
40
- end
41
-
42
- # public: Flush some events from our queue
43
- #
44
- def flush
45
- # Block until we have something to send
46
- item = @queue.pop
47
- return if item.nil?
48
-
49
- # Synchronize on additions to the current batch
50
- @mutex.synchronize {
51
- @current_batch << item
52
- until @current_batch.length >= @batch_size || @queue.empty?
53
- @current_batch << @queue.pop
54
- end
55
- }
56
-
57
- req = Request.new
58
- res = req.post @secret, @current_batch
59
- @on_error.call res.status, res.error unless res.status == 200
60
- @mutex.synchronize {
61
- @current_batch = []
62
- }
63
- end
64
-
65
- # public: Check whether we have outstanding requests.
66
- #
67
- def is_requesting?
68
- requesting = nil
69
- @mutex.synchronize {
70
- requesting = !@current_batch.empty?
71
- }
72
- requesting
73
- end
74
-
75
- end
76
- end
@@ -1,21 +0,0 @@
1
-
2
- module AnalyticsRuby
3
- module Defaults
4
-
5
- module Request
6
- HOST = 'api.segment.io' unless defined? AnalyticsRuby::Defaults::Request::HOST
7
- PORT = 443 unless defined? AnalyticsRuby::Defaults::Request::PORT
8
- PATH = '/v1/import' unless defined? AnalyticsRuby::Defaults::Request::PATH
9
- SSL = true unless defined? AnalyticsRuby::Defaults::Request::SSL
10
- HEADERS = { :accept => 'application/json' } unless defined? AnalyticsRuby::Defaults::Request::HEADERS
11
- RETRIES = 4 unless defined? AnalyticsRuby::Defaults::Request::RETRIES
12
- BACKOFF = 30.0 unless defined? AnalyticsRuby::Defaults::Request::BACKOFF
13
- end
14
-
15
- module Queue
16
- BATCH_SIZE = 100 unless defined? AnalyticsRuby::Defaults::Queue::BATCH_SIZE
17
- MAX_SIZE = 10000 unless defined? AnalyticsRuby::Defaults::Queue::MAX_SIZE
18
- end
19
-
20
- end
21
- end
@@ -1,61 +0,0 @@
1
-
2
- require 'analytics-ruby/defaults'
3
- require 'analytics-ruby/response'
4
- require 'net/http'
5
- require 'net/https'
6
- require 'json'
7
-
8
- module AnalyticsRuby
9
-
10
- class Request
11
-
12
- # public: Creates a new request object to send analytics batch
13
- #
14
- def initialize(options = {})
15
- options[:host] ||= Defaults::Request::HOST
16
- options[:port] ||= Defaults::Request::PORT
17
- options[:ssl] ||= Defaults::Request::SSL
18
- options[:headers] ||= Defaults::Request::HEADERS
19
- @path = options[:path] || Defaults::Request::PATH
20
- @retries = options[:retries] || Defaults::Request::RETRIES
21
- @backoff = options[:backoff] || Defaults::Request::BACKOFF
22
-
23
- http = Net::HTTP.new(options[:host], options[:port])
24
- http.use_ssl = options[:ssl]
25
- http.read_timeout = 8
26
- http.open_timeout = 4
27
-
28
- @http = http
29
- end
30
-
31
- # public: Posts the secret and batch of messages to the API.
32
- #
33
- # returns - Response of the status and error if it exists
34
- def post(secret, batch)
35
-
36
- status, error = nil, nil
37
- remaining_retries = @retries
38
- backoff = @backoff
39
- headers = { 'Content-Type' => 'application/json', 'accept' => 'application/json' }
40
- begin
41
- payload = JSON.generate :secret => secret, :batch => batch
42
- res = @http.request(Net::HTTP::Post.new(@path, headers), payload)
43
- status = res.code.to_i
44
- body = JSON.parse(res.body)
45
- error = body["error"]
46
-
47
- rescue Exception => err
48
- puts "err: #{err}"
49
- status = -1
50
- error = "Connection error: #{err}"
51
- puts "retries: #{remaining_retries}"
52
- unless (remaining_retries -=1).zero?
53
- sleep(backoff)
54
- retry
55
- end
56
- end
57
-
58
- Response.new status, error
59
- end
60
- end
61
- end
@@ -1,17 +0,0 @@
1
-
2
- module AnalyticsRuby
3
-
4
- class Response
5
-
6
- attr_reader :status
7
- attr_reader :error
8
-
9
- # public: Simple class to wrap responses from the API
10
- #
11
- #
12
- def initialize(status = 200, error = nil)
13
- @status = status
14
- @error = error
15
- end
16
- end
17
- end
@@ -1,42 +0,0 @@
1
- module Util
2
-
3
- # public: Return a new hash with keys converted from strings to symbols
4
- #
5
- def self.symbolize_keys(hash)
6
- hash.inject({}) { |memo, (k,v)| memo[k.to_sym] = v; memo }
7
- end
8
-
9
- # public: Convert hash keys from strings to symbols in place
10
- #
11
- def self.symbolize_keys!(hash)
12
- hash.replace symbolize_keys hash
13
- end
14
-
15
- # public: Return a new hash with keys as strings
16
- #
17
- def self.stringify_keys(hash)
18
- hash.inject({}) { |memo, (k,v)| memo[k.to_s] = v; memo }
19
- end
20
-
21
- # public: Returns a new hash with all the date values in the into iso8601
22
- # strings
23
- #
24
- def self.isoify_dates(hash)
25
- hash.inject({}) { |memo, (k, v)|
26
- memo[k] = v.respond_to?(:iso8601) ? v.iso8601 : v
27
- memo
28
- }
29
- end
30
-
31
- # public: Converts all the date values in the into iso8601 strings in place
32
- #
33
- def self.isoify_dates!(hash)
34
- hash.replace isoify_dates hash
35
- end
36
-
37
- # public: Returns a uid string
38
- #
39
- def self.uid
40
- (0..16).to_a.map{|x| rand(16).to_s(16)}.join
41
- end
42
- end
@@ -1,3 +0,0 @@
1
- module AnalyticsRuby
2
- VERSION = '1.1.0'
3
- end
data/spec/client_spec.rb DELETED
@@ -1,212 +0,0 @@
1
- require 'analytics-ruby'
2
- require 'spec_helper'
3
-
4
-
5
- describe AnalyticsRuby::Client do
6
-
7
- describe '#init' do
8
-
9
- it 'should error if no secret is supplied' do
10
- expect { AnalyticsRuby::Client.new }.to raise_error(RuntimeError)
11
- end
12
-
13
- it 'should not error if a secret is supplied' do
14
- AnalyticsRuby::Client.new :secret => AnalyticsRubyHelpers::SECRET
15
- end
16
-
17
- it 'should not error if a secret is supplied as a string' do
18
- AnalyticsRuby::Client.new 'secret' => AnalyticsRubyHelpers::SECRET
19
- end
20
- end
21
-
22
- describe '#track' do
23
-
24
- before(:all) do
25
- @client = AnalyticsRuby::Client.new :secret => AnalyticsRubyHelpers::SECRET
26
- @client.instance_variable_get(:@thread).kill
27
- @queue = @client.instance_variable_get :@queue
28
- end
29
-
30
- it 'should error without an event' do
31
- expect { @client.track(:user_id => 'user') }.to raise_error(ArgumentError)
32
- end
33
-
34
- it 'should error without a user_id' do
35
- expect { @client.track(:event => 'Event') }.to raise_error(ArgumentError)
36
- end
37
-
38
- it 'should error if properties is not a hash' do
39
- expect {
40
- @client.track({
41
- :user_id => 'user',
42
- :event => 'Event',
43
- :properties => [1,2,3]
44
- })
45
- }.to raise_error(ArgumentError)
46
- end
47
-
48
- it 'should not error with the required options' do
49
- @client.track AnalyticsRubyHelpers::Queued::TRACK
50
- @queue.pop
51
- end
52
-
53
- it 'should not error when given string keys' do
54
- @client.track Util.stringify_keys(AnalyticsRubyHelpers::Queued::TRACK)
55
- @queue.pop
56
- end
57
-
58
- it 'should convert Time properties into iso8601 format' do
59
- @client.track({
60
- :user_id => 'user',
61
- :event => 'Event',
62
- :properties => {
63
- :time => Time.utc(2013),
64
- :nottime => 'x'
65
- }
66
- })
67
- message = @queue.pop
68
- message[:properties][:time].should == '2013-01-01T00:00:00Z'
69
- message[:properties][:nottime].should == 'x'
70
- end
71
- end
72
-
73
-
74
- describe '#identify' do
75
-
76
- before(:all) do
77
- @client = AnalyticsRuby::Client.new :secret => AnalyticsRubyHelpers::SECRET
78
- @client.instance_variable_get(:@thread).kill
79
- @queue = @client.instance_variable_get :@queue
80
- end
81
-
82
- it 'should error without any user id' do
83
- expect { @client.identify({}) }.to raise_error(ArgumentError)
84
- end
85
-
86
- it 'should not error with the required options' do
87
- @client.identify AnalyticsRubyHelpers::Queued::IDENTIFY
88
- @queue.pop
89
- end
90
-
91
- it 'should not error with the required options as strings' do
92
- @client.identify Util.stringify_keys(AnalyticsRubyHelpers::Queued::IDENTIFY)
93
- @queue.pop
94
- end
95
-
96
- it 'should convert Time traits into iso8601 format' do
97
- @client.identify({
98
- :user_id => 'user',
99
- :traits => {
100
- :time => Time.utc(2013),
101
- :nottime => 'x'
102
- }
103
- })
104
- message = @queue.pop
105
- message[:traits][:time].should == '2013-01-01T00:00:00Z'
106
- message[:traits][:nottime].should == 'x'
107
- end
108
- end
109
-
110
- describe '#alias' do
111
- before :all do
112
- @client = AnalyticsRuby::Client.new :secret => AnalyticsRubyHelpers::SECRET
113
- end
114
-
115
- it 'should error without from' do
116
- expect { @client.alias :to => 1234 }.to raise_error(ArgumentError)
117
- end
118
-
119
- it 'should error without to' do
120
- expect { @client.alias :from => 1234 }.to raise_error(ArgumentError)
121
- end
122
-
123
- it 'should not error with the required options' do
124
- @client.alias AnalyticsRubyHelpers::ALIAS
125
- end
126
-
127
- it 'should not error with the required options as strings' do
128
- @client.alias Util.stringify_keys(AnalyticsRubyHelpers::ALIAS)
129
- end
130
- end
131
-
132
- describe '#group' do
133
- before :all do
134
- @client = AnalyticsRuby::Client.new :secret => AnalyticsRubyHelpers::SECRET
135
- end
136
-
137
- it 'should error without group_id' do
138
- expect { @client.group :user_id => 'foo' }.to raise_error(ArgumentError)
139
- end
140
-
141
- it 'should error without user_id' do
142
- expect { @client.group :group_id => 'foo' }.to raise_error(ArgumentError)
143
- end
144
-
145
- it 'should not error with the required options' do
146
- @client.group AnalyticsRubyHelpers::Queued::GROUP
147
- end
148
-
149
- it 'should not error with the required options as strings' do
150
- @client.group Util.stringify_keys(AnalyticsRubyHelpers::Queued::GROUP)
151
- end
152
- end
153
-
154
- describe '#page' do
155
- before :all do
156
- @client = AnalyticsRuby::Client.new :secret => AnalyticsRubyHelpers::SECRET
157
- end
158
-
159
- it 'should error without user_id' do
160
- expect { @client.page :name => 'foo' }.to raise_error(ArgumentError)
161
- end
162
-
163
- it 'should error without name' do
164
- expect { @client.page :user_id => 1 }.to raise_error(ArgumentError)
165
- end
166
-
167
- it 'should not error with the required options' do
168
- @client.page AnalyticsRubyHelpers::Queued::PAGE
169
- end
170
-
171
- it 'should not error with the required options as strings' do
172
- @client.page Util.stringify_keys(AnalyticsRubyHelpers::Queued::PAGE)
173
- end
174
- end
175
-
176
- describe '#screen' do
177
- before :all do
178
- @client = AnalyticsRuby::Client.new :secret => AnalyticsRubyHelpers::SECRET
179
- end
180
-
181
- it 'should error without user_id' do
182
- expect { @client.screen :name => 'foo' }.to raise_error(ArgumentError)
183
- end
184
-
185
- it 'should error without name' do
186
- expect { A@client.screen :user_id => 1 }.to raise_error(ArgumentError)
187
- end
188
-
189
- it 'should not error with the required options' do
190
- @client.screen AnalyticsRubyHelpers::Queued::SCREEN
191
- end
192
-
193
- it 'should not error with the required options as strings' do
194
- @client.screen Util.stringify_keys(AnalyticsRubyHelpers::Queued::SCREEN)
195
- end
196
- end
197
-
198
- describe '#flush' do
199
- before(:all) do
200
- @client = AnalyticsRuby::Client.new :secret => AnalyticsRubyHelpers::SECRET
201
- end
202
-
203
- it 'should wait for the queue to finish on a flush' do
204
- @client.identify AnalyticsRubyHelpers::Queued::IDENTIFY
205
- @client.track AnalyticsRubyHelpers::Queued::TRACK
206
- @client.flush
207
- @client.queued_messages.should == 0
208
- end
209
- end
210
- end
211
-
212
-
@@ -1,103 +0,0 @@
1
- require 'analytics-ruby'
2
- require 'thread'
3
- require 'spec_helper'
4
-
5
- describe AnalyticsRuby::Consumer do
6
-
7
- describe "#init" do
8
- it 'accepts string keys' do
9
- queue = Queue.new
10
- consumer = AnalyticsRuby::Consumer.new(queue, 'secret', 'batch_size' => 100)
11
- consumer.instance_variable_get(:@batch_size).should == 100
12
- end
13
- end
14
-
15
- describe '#flush' do
16
-
17
- before :all do
18
- AnalyticsRuby::Defaults::Request::BACKOFF = 0.1
19
- end
20
-
21
- after :all do
22
- AnalyticsRuby::Defaults::Request::BACKOFF = 30.0
23
- end
24
-
25
- it 'should not error if the endpoint is unreachable' do
26
-
27
- Net::HTTP.any_instance.stub(:post).and_raise(Exception)
28
-
29
- queue = Queue.new
30
- queue << {}
31
- consumer = AnalyticsRuby::Consumer.new(queue, 'secret')
32
- consumer.flush
33
-
34
- queue.should be_empty
35
-
36
- Net::HTTP.any_instance.unstub(:post)
37
- end
38
-
39
- it 'should execute the error handler if the request is invalid' do
40
-
41
- AnalyticsRuby::Request.any_instance.stub(:post).and_return(
42
- AnalyticsRuby::Response.new(400, "Some error"))
43
-
44
- on_error = Proc.new do |status, error|
45
- puts "#{status}, #{error}"
46
- end
47
-
48
- on_error.should_receive(:call).once
49
-
50
- queue = Queue.new
51
- queue << {}
52
- consumer = AnalyticsRuby::Consumer.new queue, 'secret', :on_error => on_error
53
- consumer.flush
54
-
55
- AnalyticsRuby::Request::any_instance.unstub(:post)
56
-
57
- queue.should be_empty
58
- end
59
-
60
- it 'should not call on_error if the request is good' do
61
-
62
- on_error = Proc.new do |status, error|
63
- puts "#{status}, #{error}"
64
- end
65
-
66
- on_error.should_receive(:call).at_most(0).times
67
-
68
- queue = Queue.new
69
- queue << AnalyticsRubyHelpers::Requested::TRACK
70
- consumer = AnalyticsRuby::Consumer.new queue, 'testsecret', :on_error => on_error
71
- consumer.flush
72
-
73
- queue.should be_empty
74
- end
75
- end
76
-
77
- describe '#is_requesting?' do
78
-
79
- it 'should not return true if there isn\'t a current batch' do
80
-
81
- queue = Queue.new
82
- consumer = AnalyticsRuby::Consumer.new(queue, 'testsecret')
83
-
84
- consumer.is_requesting?.should == false
85
- end
86
-
87
- it 'should return true if there is a current batch' do
88
-
89
- queue = Queue.new
90
- queue << AnalyticsRubyHelpers::Requested::TRACK
91
- consumer = AnalyticsRuby::Consumer.new(queue, 'testsecret')
92
-
93
- Thread.new {
94
- consumer.flush
95
- consumer.is_requesting?.should == false
96
- }
97
-
98
- # sleep barely long enough to let thread flush the queue.
99
- sleep(0.001)
100
- consumer.is_requesting?.should == true
101
- end
102
- end
103
- end