km 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.
- data/.gitignore +4 -0
- data/CHANGELOG +4 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +33 -0
- data/README.rdoc +10 -0
- data/Rakefile +5 -0
- data/bin/km_send +26 -0
- data/doc/Accept.html +546 -0
- data/doc/Gemfile.html +110 -0
- data/doc/Hash.html +283 -0
- data/doc/Helper.html +318 -0
- data/doc/KM/IdentError.html +159 -0
- data/doc/KM/InitError.html +159 -0
- data/doc/KM/SaaS.html +451 -0
- data/doc/KM.html +493 -0
- data/doc/KMError.html +159 -0
- data/doc/Object.html +211 -0
- data/doc/README_rdoc.html +122 -0
- data/doc/Rakefile.html +111 -0
- data/doc/String.html +244 -0
- data/doc/bin/km_send.html +54 -0
- data/doc/created.rid +15 -0
- data/doc/images/brick.png +0 -0
- data/doc/images/brick_link.png +0 -0
- data/doc/images/bug.png +0 -0
- data/doc/images/bullet_black.png +0 -0
- data/doc/images/bullet_toggle_minus.png +0 -0
- data/doc/images/bullet_toggle_plus.png +0 -0
- data/doc/images/date.png +0 -0
- data/doc/images/find.png +0 -0
- data/doc/images/loadingAnimation.gif +0 -0
- data/doc/images/macFFBgHack.png +0 -0
- data/doc/images/package.png +0 -0
- data/doc/images/page_green.png +0 -0
- data/doc/images/page_white_text.png +0 -0
- data/doc/images/page_white_width.png +0 -0
- data/doc/images/plugin.png +0 -0
- data/doc/images/ruby.png +0 -0
- data/doc/images/tag_green.png +0 -0
- data/doc/images/wrench.png +0 -0
- data/doc/images/wrench_orange.png +0 -0
- data/doc/images/zoom.png +0 -0
- data/doc/index.html +142 -0
- data/doc/js/darkfish.js +116 -0
- data/doc/js/jquery.js +32 -0
- data/doc/js/quicksearch.js +114 -0
- data/doc/js/thickbox-compressed.js +10 -0
- data/doc/lib/km/saas_rb.html +54 -0
- data/doc/lib/km/version_rb.html +52 -0
- data/doc/lib/km_rb.html +60 -0
- data/doc/rdoc.css +730 -0
- data/doc/spec/accept_rb.html +62 -0
- data/doc/spec/km_old_rb.html +54 -0
- data/doc/spec/km_saas_spec_rb.html +56 -0
- data/doc/spec/km_send_spec_rb.html +54 -0
- data/doc/spec/km_spec_rb.html +54 -0
- data/doc/spec/setup_rb.html +60 -0
- data/doc/spec/watchr_rb.html +52 -0
- data/km.gemspec +28 -0
- data/lib/km/saas.rb +39 -0
- data/lib/km/version.rb +3 -0
- data/lib/km.rb +263 -0
- data/spec/accept.rb +90 -0
- data/spec/km_old.rb +105 -0
- data/spec/km_saas_spec.rb +121 -0
- data/spec/km_send_spec.rb +82 -0
- data/spec/km_spec.rb +186 -0
- data/spec/log/.hold +0 -0
- data/spec/setup.rb +78 -0
- data/spec/watchr.rb +3 -0
- metadata +224 -0
data/lib/km.rb
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
require 'cgi'
|
|
2
|
+
require 'socket'
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'km/saas'
|
|
5
|
+
|
|
6
|
+
class Hash
|
|
7
|
+
def reverse_merge(other_hash)
|
|
8
|
+
other_hash.merge(self)
|
|
9
|
+
end if !respond_to?(:reverse_merge)
|
|
10
|
+
def reverse_merge!(other_hash)
|
|
11
|
+
replace(reverse_merge(other_hash))
|
|
12
|
+
end if !respond_to?(:reverse_merge)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class KMError < StandardError; end
|
|
16
|
+
|
|
17
|
+
class KM
|
|
18
|
+
@id = nil
|
|
19
|
+
@key = nil
|
|
20
|
+
@logs = {}
|
|
21
|
+
@host = 'trk.kissmetrics.com:80'
|
|
22
|
+
@log_dir = '/tmp'
|
|
23
|
+
@to_stderr = true
|
|
24
|
+
@use_cron = false
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
class IdentError < StandardError; end
|
|
28
|
+
class InitError < StandardError; end
|
|
29
|
+
|
|
30
|
+
def init(key, options={})
|
|
31
|
+
default = {
|
|
32
|
+
:host => @host,
|
|
33
|
+
:log_dir => @log_dir,
|
|
34
|
+
:to_stderr => @to_stderr,
|
|
35
|
+
:use_cron => @use_cron,
|
|
36
|
+
:env => set_env,
|
|
37
|
+
}
|
|
38
|
+
options.reverse_merge!(default)
|
|
39
|
+
begin
|
|
40
|
+
@key = key
|
|
41
|
+
@host = options[:host]
|
|
42
|
+
@log_dir = options[:log_dir]
|
|
43
|
+
@use_cron = options[:use_cron]
|
|
44
|
+
@to_stderr = options[:to_stderr]
|
|
45
|
+
@env = options[:env]
|
|
46
|
+
log_dir_writable?
|
|
47
|
+
rescue Exception => e
|
|
48
|
+
log_error(e)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def set_env
|
|
53
|
+
@env = Rails.env if defined? Rails
|
|
54
|
+
@env ||= ENV['RACK_ENV']
|
|
55
|
+
@env ||= 'production'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def identify(id)
|
|
59
|
+
@id = id
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def record(action,props={})
|
|
63
|
+
props = hash_keys_to_str(props)
|
|
64
|
+
begin
|
|
65
|
+
return unless is_initialized_and_identified?
|
|
66
|
+
return set(action) if action.class == Hash
|
|
67
|
+
|
|
68
|
+
props.update('_n' => action)
|
|
69
|
+
generate_query('e', props)
|
|
70
|
+
rescue Exception => e
|
|
71
|
+
log_error(e)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def alias(name, alias_to)
|
|
76
|
+
begin
|
|
77
|
+
return unless is_initialized?
|
|
78
|
+
generate_query('a', { '_n' => alias_to, '_p' => name }, false)
|
|
79
|
+
rescue Exception => e
|
|
80
|
+
log_error(e)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def set(data)
|
|
85
|
+
begin
|
|
86
|
+
return unless is_initialized_and_identified?
|
|
87
|
+
generate_query('s', data)
|
|
88
|
+
rescue Exception => e
|
|
89
|
+
log_error(e)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def send_logged_queries # :nodoc:
|
|
94
|
+
line = nil
|
|
95
|
+
begin
|
|
96
|
+
query_log = log_name(:query_old)
|
|
97
|
+
query_log = log_name(:query) unless File.exists?(query_log)
|
|
98
|
+
return unless File.exists?(query_log) # can't find logfile to send
|
|
99
|
+
FileUtils.move(query_log, log_name(:send))
|
|
100
|
+
File.open(log_name(:send)) do |fh|
|
|
101
|
+
while not fh.eof?
|
|
102
|
+
begin
|
|
103
|
+
line = fh.readline.chomp
|
|
104
|
+
send_query(line)
|
|
105
|
+
rescue Exception => e
|
|
106
|
+
log_query(line) if line
|
|
107
|
+
log_error(e)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
FileUtils.rm(log_name(:send))
|
|
112
|
+
rescue Exception => e
|
|
113
|
+
log_error(e)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def log_dir
|
|
118
|
+
@log_dir
|
|
119
|
+
end
|
|
120
|
+
def host
|
|
121
|
+
@host
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# :stopdoc:
|
|
125
|
+
protected
|
|
126
|
+
def hash_keys_to_str(hash)
|
|
127
|
+
Hash[*hash.map { |k,v| k.class == Symbol ? [k.to_s,v] : [k,v] }.flatten] # convert all keys to strings
|
|
128
|
+
end
|
|
129
|
+
def reset
|
|
130
|
+
@id = nil
|
|
131
|
+
@key = nil
|
|
132
|
+
@logs = {}
|
|
133
|
+
@host = 'trk.kissmetrics.com:80'
|
|
134
|
+
@log_dir = '/tmp'
|
|
135
|
+
@to_stderr = true
|
|
136
|
+
@use_cron = false
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def log_name(type)
|
|
140
|
+
return @logs[type] if @logs[type]
|
|
141
|
+
fname = ''
|
|
142
|
+
env = @env ? "_#{@env}" : ''
|
|
143
|
+
case type
|
|
144
|
+
when :error
|
|
145
|
+
fname = "kissmetrics#{env}_error.log"
|
|
146
|
+
when :query
|
|
147
|
+
fname = "kissmetrics#{env}_query.log"
|
|
148
|
+
when :query_old # backwards compatibility
|
|
149
|
+
fname = "kissmetrics_query.log"
|
|
150
|
+
when :sent
|
|
151
|
+
fname = "kissmetrics#{env}_sent.log"
|
|
152
|
+
when :send
|
|
153
|
+
fname = Time.now.to_i.to_s + "kissmetrics_#{env}_sending.log"
|
|
154
|
+
end
|
|
155
|
+
@logs[type] = File.join(@log_dir,fname)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def log_query(msg)
|
|
159
|
+
log(:query,msg)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def log_sent(msg)
|
|
163
|
+
log(:sent,msg)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def log_send(msg)
|
|
167
|
+
log(:send,msg)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def log_error(error)
|
|
171
|
+
if defined?(HoptoadNotifier)
|
|
172
|
+
HoptoadNotifier.notify_or_ignore(KMError.new(error))
|
|
173
|
+
end
|
|
174
|
+
msg = Time.now.strftime("<%c> ") + error.message
|
|
175
|
+
$stderr.puts msg if @to_stderr
|
|
176
|
+
log(:error, msg)
|
|
177
|
+
rescue Exception # rescue incase hoptoad has issues
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def log(type,msg)
|
|
181
|
+
begin
|
|
182
|
+
File.open(log_name(type), 'a') do |fh|
|
|
183
|
+
fh.puts(msg)
|
|
184
|
+
end
|
|
185
|
+
rescue Exception => e
|
|
186
|
+
raise KMError.new(e) if type.to_s == 'query'
|
|
187
|
+
# just discard at this point otherwise
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def generate_query(type, data, update=true)
|
|
193
|
+
data = hash_keys_to_str(data)
|
|
194
|
+
query_arr = []
|
|
195
|
+
query = ''
|
|
196
|
+
data.update('_p' => @id) unless update == false
|
|
197
|
+
data.update('_k' => @key)
|
|
198
|
+
data.update '_d' => 1 if data['_t']
|
|
199
|
+
data.reverse_merge!('_t' => Time.now.to_i)
|
|
200
|
+
data.inject(query) do |query,key_val|
|
|
201
|
+
query_arr << key_val.collect { |i| CGI.escape i.to_s }.join('=')
|
|
202
|
+
end
|
|
203
|
+
query = '/' + type + '?' + query_arr.join('&')
|
|
204
|
+
if @use_cron
|
|
205
|
+
log_query(query)
|
|
206
|
+
else
|
|
207
|
+
begin
|
|
208
|
+
send_query(query)
|
|
209
|
+
rescue Exception => e
|
|
210
|
+
log_query(query)
|
|
211
|
+
log_error(e)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def send_query(line)
|
|
217
|
+
if @env != 'production'
|
|
218
|
+
log_sent(line)
|
|
219
|
+
return
|
|
220
|
+
end
|
|
221
|
+
host,port = @host.split(':')
|
|
222
|
+
begin
|
|
223
|
+
sock = TCPSocket.open(host,port)
|
|
224
|
+
request = 'GET ' + line + " HTTP/1.1\r\n"
|
|
225
|
+
request += "Host: " + Socket.gethostname + "\r\n"
|
|
226
|
+
request += "Connection: Close\r\n\r\n";
|
|
227
|
+
sock.print(request)
|
|
228
|
+
sock.close
|
|
229
|
+
rescue Exception => e
|
|
230
|
+
raise KMError.new("#{e} for host #{@host}")
|
|
231
|
+
end
|
|
232
|
+
log_sent(line)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def log_dir_writable?
|
|
236
|
+
if not FileTest.writable? @log_dir
|
|
237
|
+
$stderr.puts("Could't open #{log_name(:query)} for writing. Does #{@log_dir} exist? Permissions?") if @to_stderr
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def is_identified?
|
|
242
|
+
if @id == nil
|
|
243
|
+
log_error IdentError.new("Need to identify first (KM::identify <user>)")
|
|
244
|
+
return false
|
|
245
|
+
end
|
|
246
|
+
return true
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def is_initialized_and_identified?
|
|
250
|
+
return false unless is_initialized?
|
|
251
|
+
return is_identified?
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def is_initialized?
|
|
255
|
+
if @key == nil
|
|
256
|
+
log_error InitError.new("Need to initialize first (KM::init <your_key>)")
|
|
257
|
+
return false
|
|
258
|
+
end
|
|
259
|
+
return true
|
|
260
|
+
end
|
|
261
|
+
# :startdoc:
|
|
262
|
+
end
|
|
263
|
+
end
|
data/spec/accept.rb
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require 'socket'
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'uri'
|
|
6
|
+
require 'cgi'
|
|
7
|
+
|
|
8
|
+
# a library to accept connections as a server, and send back what it received on request.
|
|
9
|
+
|
|
10
|
+
class Accept
|
|
11
|
+
attr_accessor :server, :session
|
|
12
|
+
attr_reader :opts
|
|
13
|
+
URI_REXEGP = /^\s*(\w+)\s+([^ ]*)\s+(.*)$/
|
|
14
|
+
def initialize(args = {})
|
|
15
|
+
opts = { :port => 9292, :debug => false }
|
|
16
|
+
opts.update(args)
|
|
17
|
+
|
|
18
|
+
puts "Starting up server on port #{opts[:port]} ..."
|
|
19
|
+
@opts = opts
|
|
20
|
+
@server = TCPServer.new(opts[:port])
|
|
21
|
+
@@input_history = []
|
|
22
|
+
@handle = Thread.start do
|
|
23
|
+
while (@session = server.accept)
|
|
24
|
+
Thread.start do
|
|
25
|
+
# puts "log: Connection from #{session.peeraddr[2]} at #{session.peeraddr[3]}"
|
|
26
|
+
# session.puts "Server: Connection from #{session.peeraddr[2]}\n"
|
|
27
|
+
handle_input
|
|
28
|
+
session.close
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
def input_history
|
|
34
|
+
@@input_history
|
|
35
|
+
end
|
|
36
|
+
def wait
|
|
37
|
+
@handle.join
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def handle_input
|
|
41
|
+
input = session.gets
|
|
42
|
+
if input
|
|
43
|
+
puts "received: #{input.inspect}" if opts[:debug]
|
|
44
|
+
case input
|
|
45
|
+
when /clear/
|
|
46
|
+
clear
|
|
47
|
+
when /history/
|
|
48
|
+
session.puts input_history.to_json
|
|
49
|
+
when /exit/
|
|
50
|
+
begin
|
|
51
|
+
close
|
|
52
|
+
rescue Exception
|
|
53
|
+
end
|
|
54
|
+
return
|
|
55
|
+
when /^\s*(GET|POST|PUT|DELETE)\s+([^ ]*)\s+(.*)$/
|
|
56
|
+
@@input_history << parse_input(input)
|
|
57
|
+
else
|
|
58
|
+
@@input_history << input.chomp
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
def parse_input(input)
|
|
63
|
+
data = {}
|
|
64
|
+
data[:raw] = input.chomp
|
|
65
|
+
(method,uri,http) = input.scan(/^\s*(\w+)\s+([^ ]*)\s+(.*)$/).flatten
|
|
66
|
+
|
|
67
|
+
data[:method] = method
|
|
68
|
+
data[:http] = http.chomp
|
|
69
|
+
data[:uri] = uri
|
|
70
|
+
u = URI(uri)
|
|
71
|
+
data[:path] = u.path
|
|
72
|
+
data[:query] = CGI.parse(u.query)
|
|
73
|
+
return data
|
|
74
|
+
end
|
|
75
|
+
def close
|
|
76
|
+
session = nil
|
|
77
|
+
server.close
|
|
78
|
+
end
|
|
79
|
+
# clear history
|
|
80
|
+
def clear
|
|
81
|
+
@@input_history.clear
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
__END__
|
|
85
|
+
% ruby -r './lib/ruby/accept.rb' -e 'Accept.new(:debug => true, :port => 9292).wait'
|
|
86
|
+
Starting up server on port 9292 ...
|
|
87
|
+
|
|
88
|
+
echo rain | nc localhost 9292
|
|
89
|
+
% echo history | nc localhost 9292
|
|
90
|
+
["rain"]
|
data/spec/km_old.rb
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
require 'setup'
|
|
2
|
+
|
|
3
|
+
describe KM do
|
|
4
|
+
attr_accessor :send_query, :log
|
|
5
|
+
before do
|
|
6
|
+
@send_query = []
|
|
7
|
+
@log = []
|
|
8
|
+
KM.stub(:send_query).and_return { |*args| send_query << args }
|
|
9
|
+
KM.stub(:log).and_return { |*args| log << Hash[*args] }
|
|
10
|
+
time = Time.at 1234567890
|
|
11
|
+
Time.stub!(:now).and_return(time)
|
|
12
|
+
KM.reset
|
|
13
|
+
end
|
|
14
|
+
context "initialization" do
|
|
15
|
+
it "should not record without initialization" do
|
|
16
|
+
KM::record 'My Action'
|
|
17
|
+
log.first[:error].should =~ /Need to initialize first \(KM::init <your_key>\)/
|
|
18
|
+
end
|
|
19
|
+
it "should not set initialization" do
|
|
20
|
+
KM::set :day => 'friday'
|
|
21
|
+
log.first[:error].should =~ /Need to initialize first \(KM::init <your_key>\)/
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
context "identification" do
|
|
25
|
+
before do
|
|
26
|
+
KM::init 'KM_KEY'
|
|
27
|
+
end
|
|
28
|
+
it "should not record without identification" do
|
|
29
|
+
KM::record 'My Action'
|
|
30
|
+
log.first[:error].should include("Need to identify first (KM::identify <user>)")
|
|
31
|
+
end
|
|
32
|
+
it "should set without identification" do
|
|
33
|
+
KM::record 'My Action'
|
|
34
|
+
log.first[:error].should include("Need to identify first (KM::identify <user>)")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
context "aliasing" do
|
|
38
|
+
it "shouldn't fail on alias without identifying" do
|
|
39
|
+
KM::alias 'peter','joe' # Alias "bob" to "robert"
|
|
40
|
+
send_query.first.first.should have_query_string("/a?_n=joe&_p=peter&_k=KM_KEY&_t=1234567890")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
context "events" do
|
|
46
|
+
before do
|
|
47
|
+
KM::init 'KM_KEY'
|
|
48
|
+
KM::identify 'bob'
|
|
49
|
+
end
|
|
50
|
+
it "should record an action with no specific props" do
|
|
51
|
+
KM::record 'My Action'
|
|
52
|
+
send_query.first.first.should have_query_string("/e?_n=My+Action&_p=bob&_k=KM_KEY&_t=1234567890")
|
|
53
|
+
end
|
|
54
|
+
it "should record an action with properties" do
|
|
55
|
+
KM::record 'Signup', 'age' => 26
|
|
56
|
+
send_query.first.first.should have_query_string("/e?age=26&_n=Signup&_p=bob&_k=KM_KEY&_t=1234567890")
|
|
57
|
+
end
|
|
58
|
+
it "should reocrd properties with spaces in key and value" do
|
|
59
|
+
KM::record 'Signup', 'age' => 26, 'city of residence' => 'eug ene'
|
|
60
|
+
send_query.first.first.should have_query_string("/e?age=26&city+of+residence=eug+ene&_n=Signup&_p=bob&_k=KM_KEY&_t=1234567890")
|
|
61
|
+
end
|
|
62
|
+
it "should not over-write special keys" do
|
|
63
|
+
KM::record 'Signup', 'age' => 26, '_p' => 'billybob', '_k' => 'foo', '_n' => 'something else'
|
|
64
|
+
send_query.first.first.should have_query_string("/e?age=26&_p=bob&_k=KM_KEY&_n=Signup&_t=1234567890")
|
|
65
|
+
end
|
|
66
|
+
it "should not over-write special keys with symbols" do
|
|
67
|
+
KM::record 'Signup', 'age' => 26, '_p' => 'billybob', :'_k' => 'foo', :'_n' => 'something else'
|
|
68
|
+
send_query.first.first.should have_query_string("/e?age=26&_p=bob&_k=KM_KEY&_n=Signup&_t=1234567890")
|
|
69
|
+
end
|
|
70
|
+
it "should work with properties with @" do
|
|
71
|
+
KM::record 'Signup', 'email' => 'test@blah.com', '_p' => 'billybob', '_k' => 'foo', '_n' => 'something else'
|
|
72
|
+
send_query.first.first.should have_query_string("/e?email=test%40blah.com&_p=bob&_k=KM_KEY&_n=Signup&_t=1234567890")
|
|
73
|
+
end
|
|
74
|
+
it "should work with just set" do
|
|
75
|
+
KM::record 'age' => 26
|
|
76
|
+
send_query.first.first.should have_query_string("/s?age=26&_p=bob&_k=KM_KEY&_t=1234567890")
|
|
77
|
+
end
|
|
78
|
+
it "should record ok with multiple calls" do
|
|
79
|
+
KM::record 'Signup', 'age' => 26
|
|
80
|
+
KM::record 'Signup', 'age' => 36
|
|
81
|
+
send_query.first.first.should have_query_string("/e?age=26&_n=Signup&_p=bob&_k=KM_KEY&_t=1234567890")
|
|
82
|
+
send_query.last.first.should have_query_string("/e?age=36&_n=Signup&_p=bob&_k=KM_KEY&_t=1234567890")
|
|
83
|
+
end
|
|
84
|
+
it "shouldn't store the key anywhere" do
|
|
85
|
+
KM::init 'KM_OTHER'
|
|
86
|
+
KM::alias 'truman','harry' # Alias "bob" to "robert"
|
|
87
|
+
send_query.first.first.should have_query_string("/a?_n=harry&_p=truman&_k=KM_OTHER&_t=1234567890")
|
|
88
|
+
end
|
|
89
|
+
it "should override the time if defined" do
|
|
90
|
+
KM::record 'Signup', 'age' => 36, '_t' => 1234567891
|
|
91
|
+
send_query.last.first.should have_query_string("/e?age=36&_n=Signup&_p=bob&_k=KM_KEY&_t=1234567891&_d=1")
|
|
92
|
+
end
|
|
93
|
+
it "should work with either symbols or strings" do
|
|
94
|
+
KM::record :Signup, :age => 36, :_t => 1234567891
|
|
95
|
+
send_query.last.first.should have_query_string("/e?age=36&_n=Signup&_p=bob&_k=KM_KEY&_t=1234567891&_d=1")
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "should test cron" do
|
|
100
|
+
pending
|
|
101
|
+
end
|
|
102
|
+
it "should send logged queries" do
|
|
103
|
+
pending
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
require 'setup'
|
|
2
|
+
require 'km/saas'
|
|
3
|
+
describe KM do
|
|
4
|
+
before do
|
|
5
|
+
KM::reset
|
|
6
|
+
now = Time.now
|
|
7
|
+
Time.stub!(:now).and_return(now)
|
|
8
|
+
FileUtils.rm_f KM::log_name(:error)
|
|
9
|
+
FileUtils.rm_f KM::log_name(:query)
|
|
10
|
+
Helper.clear
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe "should record events" do
|
|
14
|
+
before do
|
|
15
|
+
KM::init 'KM_KEY', :log_dir => __('log'), :host => '127.0.0.1:9292'
|
|
16
|
+
KM::identify 'bob'
|
|
17
|
+
end
|
|
18
|
+
context "plain usage" do
|
|
19
|
+
it "records a signup event" do
|
|
20
|
+
KM.signed_up 'Premium'
|
|
21
|
+
sleep 0.1
|
|
22
|
+
res = Helper.accept(:history).first.indifferent
|
|
23
|
+
res[:path].should == '/e'
|
|
24
|
+
res[:query]['_n'].first.should == 'Signed Up'
|
|
25
|
+
res[:query]['Plan Name'].first.should == 'Premium'
|
|
26
|
+
end
|
|
27
|
+
it "records an upgraded event" do
|
|
28
|
+
KM.upgraded 'Unlimited'
|
|
29
|
+
sleep 0.1
|
|
30
|
+
res = Helper.accept(:history).first.indifferent
|
|
31
|
+
res[:path].should == '/e'
|
|
32
|
+
res[:query]['_n'].first.should == 'Upgraded'
|
|
33
|
+
res[:query]['Plan Name'].first.should == 'Unlimited'
|
|
34
|
+
end
|
|
35
|
+
it "records a downgraded event" do
|
|
36
|
+
KM.downgraded 'Free'
|
|
37
|
+
sleep 0.1
|
|
38
|
+
res = Helper.accept(:history).first.indifferent
|
|
39
|
+
res[:path].should == '/e'
|
|
40
|
+
res[:query]['_n'].first.should == 'Downgraded'
|
|
41
|
+
res[:query]['Plan Name'].first.should == 'Free'
|
|
42
|
+
end
|
|
43
|
+
it "records a billed event" do
|
|
44
|
+
KM.billed 32, 'Upgraded'
|
|
45
|
+
sleep 0.1
|
|
46
|
+
res = Helper.accept(:history).first.indifferent
|
|
47
|
+
res[:path].should == '/e'
|
|
48
|
+
res[:query]['_n'].first.should == 'Billed'
|
|
49
|
+
res[:query]['Billing Amount'].first.should == '32'
|
|
50
|
+
res[:query]['Billing Description'].first.should == 'Upgraded'
|
|
51
|
+
end
|
|
52
|
+
it "records a canceled event" do
|
|
53
|
+
KM.canceled
|
|
54
|
+
sleep 0.1
|
|
55
|
+
res = Helper.accept(:history).first.indifferent
|
|
56
|
+
res[:path].should == '/e'
|
|
57
|
+
res[:query]['_n'].first.should == 'Canceled'
|
|
58
|
+
end
|
|
59
|
+
it "records a cancelled event" do
|
|
60
|
+
KM.cancelled
|
|
61
|
+
sleep 0.1
|
|
62
|
+
res = Helper.accept(:history).first.indifferent
|
|
63
|
+
res[:path].should == '/e'
|
|
64
|
+
res[:query]['_n'].first.should == 'Canceled'
|
|
65
|
+
end
|
|
66
|
+
it "records a visited site event" do
|
|
67
|
+
KM.visited_site 'http://duckduckgo.com', 'http://kissmetrics.com'
|
|
68
|
+
sleep 0.1
|
|
69
|
+
res = Helper.accept(:history).first.indifferent
|
|
70
|
+
res[:path].should == '/e'
|
|
71
|
+
res[:query]['_n'].first.should == 'Visited Site'
|
|
72
|
+
res[:query]['URL'].first.should == 'http://duckduckgo.com'
|
|
73
|
+
res[:query]['Referrer'].first.should == 'http://kissmetrics.com'
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
context "usage with props" do
|
|
77
|
+
it "records a signup event" do
|
|
78
|
+
KM.signed_up 'Premium', :foo => 'bar'
|
|
79
|
+
sleep 0.1
|
|
80
|
+
res = Helper.accept(:history).first.indifferent
|
|
81
|
+
res[:query]['foo'].first.should == 'bar'
|
|
82
|
+
end
|
|
83
|
+
it "records an upgraded event" do
|
|
84
|
+
KM.upgraded 'Unlimited', :foo => 'bar'
|
|
85
|
+
sleep 0.1
|
|
86
|
+
res = Helper.accept(:history).first.indifferent
|
|
87
|
+
res[:query]['foo'].first.should == 'bar'
|
|
88
|
+
end
|
|
89
|
+
it "records a downgraded event" do
|
|
90
|
+
KM.downgraded 'Free', :foo => 'bar'
|
|
91
|
+
sleep 0.1
|
|
92
|
+
res = Helper.accept(:history).first.indifferent
|
|
93
|
+
res[:query]['foo'].first.should == 'bar'
|
|
94
|
+
end
|
|
95
|
+
it "records a billed event" do
|
|
96
|
+
KM.billed 32, 'Upgraded', :foo => 'bar'
|
|
97
|
+
sleep 0.1
|
|
98
|
+
res = Helper.accept(:history).first.indifferent
|
|
99
|
+
res[:query]['foo'].first.should == 'bar'
|
|
100
|
+
end
|
|
101
|
+
it "records a canceled event" do
|
|
102
|
+
KM.canceled :foo => 'bar'
|
|
103
|
+
sleep 0.1
|
|
104
|
+
res = Helper.accept(:history).first.indifferent
|
|
105
|
+
res[:query]['foo'].first.should == 'bar'
|
|
106
|
+
end
|
|
107
|
+
it "records a cancelled event" do
|
|
108
|
+
KM.cancelled :foo => 'bar'
|
|
109
|
+
sleep 0.1
|
|
110
|
+
res = Helper.accept(:history).first.indifferent
|
|
111
|
+
res[:query]['foo'].first.should == 'bar'
|
|
112
|
+
end
|
|
113
|
+
it "records a visited site event" do
|
|
114
|
+
KM.visited_site 'http://duckduckgo.com', 'http://kissmetrics.com', :foo => 'bar'
|
|
115
|
+
sleep 0.1
|
|
116
|
+
res = Helper.accept(:history).first.indifferent
|
|
117
|
+
res[:query]['foo'].first.should == 'bar'
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require 'setup'
|
|
2
|
+
|
|
3
|
+
describe 'km_send' do
|
|
4
|
+
context "using cron for sending logs" do
|
|
5
|
+
before do
|
|
6
|
+
now = Time.now
|
|
7
|
+
Time.stub!(:now).and_return(now)
|
|
8
|
+
Dir.glob(__('log','*')).each do |file|
|
|
9
|
+
FileUtils.rm file
|
|
10
|
+
end
|
|
11
|
+
KM.reset
|
|
12
|
+
Helper.clear
|
|
13
|
+
end
|
|
14
|
+
it "should test commandline version" do
|
|
15
|
+
KM::init 'KM_KEY', :log_dir => __('log'), :host => '127.0.0.1:9292', :use_cron => true
|
|
16
|
+
KM::identify 'bob'
|
|
17
|
+
KM::record 'Signup', 'age' => 26
|
|
18
|
+
`bundle exec km_send #{__('log/')} 127.0.0.1:9292`
|
|
19
|
+
sleep 0.1
|
|
20
|
+
res = Helper.accept(:history).first.indifferent
|
|
21
|
+
res[:path].should == '/e'
|
|
22
|
+
res[:query]['_k'].first.should == 'KM_KEY'
|
|
23
|
+
res[:query]['_p'].first.should == 'bob'
|
|
24
|
+
res[:query]['_n'].first.should == 'Signup'
|
|
25
|
+
res[:query]['_t'].first.should == Time.now.to_i.to_s
|
|
26
|
+
res[:query]['age'].first.should == '26'
|
|
27
|
+
end
|
|
28
|
+
it "should send from query_log" do
|
|
29
|
+
write_log :query, "/e?_t=1297105499&_n=Signup&_p=bob&_k=KM_KEY&age=26"
|
|
30
|
+
`bundle exec km_send #{__('log/')} 127.0.0.1:9292`
|
|
31
|
+
sleep 0.1
|
|
32
|
+
res = Helper.accept(:history).first.indifferent
|
|
33
|
+
res[:path].should == '/e'
|
|
34
|
+
res[:query]['_k'].first.should == 'KM_KEY'
|
|
35
|
+
res[:query]['_p'].first.should == 'bob'
|
|
36
|
+
res[:query]['_n'].first.should == 'Signup'
|
|
37
|
+
res[:query]['_t'].first.should == '1297105499'
|
|
38
|
+
res[:query]['age'].first.should == '26'
|
|
39
|
+
end
|
|
40
|
+
it "should send from query_log_old" do
|
|
41
|
+
write_log :query_old, "/e?_t=1297105499&_n=Signup&_p=bob&_k=KM_KEY&age=26"
|
|
42
|
+
`bundle exec km_send #{__('log/')} 127.0.0.1:9292`
|
|
43
|
+
sleep 0.1
|
|
44
|
+
res = Helper.accept(:history).first.indifferent
|
|
45
|
+
res[:path].should == '/e'
|
|
46
|
+
res[:query]['_k'].first.should == 'KM_KEY'
|
|
47
|
+
res[:query]['_p'].first.should == 'bob'
|
|
48
|
+
res[:query]['_n'].first.should == 'Signup'
|
|
49
|
+
res[:query]['_t'].first.should == '1297105499'
|
|
50
|
+
res[:query]['age'].first.should == '26'
|
|
51
|
+
end
|
|
52
|
+
it "should send from both query_log and query_log_old" do
|
|
53
|
+
File.open(__('log/kissmetrics_query.log'), 'w+') { |h| h.puts "/e?_t=1297105499&_n=Signup&_p=bob&_k=KM_KEY&age=27" }
|
|
54
|
+
File.open(__('log/kissmetrics_production_query.log'), 'w+') { |h| h.puts "/e?_t=1297105499&_n=Signup&_p=bob&_k=KM_KEY&age=26" }
|
|
55
|
+
`bundle exec km_send #{__('log/')} 127.0.0.1:9292`
|
|
56
|
+
sleep 0.1
|
|
57
|
+
res = Helper.accept(:history).first.indifferent
|
|
58
|
+
res[:path].should == '/e'
|
|
59
|
+
res[:query]['_k'].first.should == 'KM_KEY'
|
|
60
|
+
res[:query]['_p'].first.should == 'bob'
|
|
61
|
+
res[:query]['_n'].first.should == 'Signup'
|
|
62
|
+
res[:query]['_t'].first.should == '1297105499'
|
|
63
|
+
res[:query]['age'].first.should == '27'
|
|
64
|
+
Helper.clear
|
|
65
|
+
`bundle exec km_send #{__('log/')} 127.0.0.1:9292`
|
|
66
|
+
sleep 0.1
|
|
67
|
+
res = Helper.accept(:history).first.indifferent
|
|
68
|
+
res[:path].should == '/e'
|
|
69
|
+
res[:query]['_k'].first.should == 'KM_KEY'
|
|
70
|
+
res[:query]['_p'].first.should == 'bob'
|
|
71
|
+
res[:query]['_n'].first.should == 'Signup'
|
|
72
|
+
res[:query]['_t'].first.should == '1297105499'
|
|
73
|
+
res[:query]['age'].first.should == '26'
|
|
74
|
+
end
|
|
75
|
+
it "should not send from diff environment as we only send when env is production" do
|
|
76
|
+
File.open(__('log/kissmetrics_alpha_query.log'), 'w+') { |h| h.puts "/e?_t=1297105499&_n=Signup&_p=bob&_k=KM_KEY&age=26" }
|
|
77
|
+
`bundle exec km_send -e alpha #{__('log/')} 127.0.0.1:9292`
|
|
78
|
+
sleep 0.1
|
|
79
|
+
res = Helper.accept(:history).first.should == nil
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|