analytics-ruby 1.1.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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