mixpanel_client 1.0.1 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.md +12 -4
- data/lib/mixpanel/client.rb +81 -0
- data/lib/mixpanel/exceptions.rb +4 -0
- data/lib/{mixpanel_client → mixpanel}/uri.rb +2 -5
- data/lib/mixpanel/version.rb +6 -0
- data/lib/mixpanel_client.rb +3 -2
- data/mixpanel_client.gemspec +4 -4
- data/spec/mixpanel_client/events_externalspec.rb +2 -2
- data/spec/mixpanel_client/mixpanel_client_spec.rb +7 -6
- data/spec/mixpanel_client/properties_externalspec.rb +37 -0
- data/spec/mixpanel_client/uri_spec.rb +5 -5
- metadata +19 -12
- data/lib/mixpanel_client/mixpanel_client.rb +0 -79
- data/lib/mixpanel_client/version.rb +0 -3
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -7,27 +7,35 @@ Ruby access to the [Mixpanel](http://mixpanel.com/) web analytics tool.
|
|
7
7
|
|
8
8
|
gem install mixpanel_client
|
9
9
|
|
10
|
-
|
11
10
|
## Usage
|
12
11
|
|
13
12
|
require 'rubygems'
|
14
13
|
require 'mixpanel_client'
|
15
14
|
|
16
|
-
client =
|
15
|
+
client = Mixpanel::Client.new('api_key' => 'changeme', 'api_secret' => 'changeme')
|
17
16
|
|
18
17
|
data = client.request do
|
19
|
-
resource 'events/
|
18
|
+
resource 'events/properties'
|
20
19
|
event '["test-event"]'
|
20
|
+
name 'hello'
|
21
|
+
values '["uno", "dos"]'
|
21
22
|
type 'general'
|
22
23
|
unit 'hour'
|
23
24
|
interval 24
|
24
|
-
|
25
|
+
limit 5
|
26
|
+
bucket 'kicked'
|
25
27
|
end
|
26
28
|
|
27
29
|
puts data.inspect
|
28
30
|
|
29
31
|
## Changelog
|
30
32
|
|
33
|
+
### 2.0.0.beta1
|
34
|
+
* Reverted to namespacing via module name because it's a better practice.
|
35
|
+
I.e. Use `Mixpanel::Client` instead of `MixpanelClient`.
|
36
|
+
* Added 'values' as an optional parameter
|
37
|
+
* `gem install mixpanel_client --pre`
|
38
|
+
|
31
39
|
### 1.0.1
|
32
40
|
* Minor housekeeping and organizing
|
33
41
|
* Refactored specs
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#!/usr/bin/env ruby -Ku
|
2
|
+
|
3
|
+
# Mixpanel API Ruby Client Library
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009+ Keolo Keagy
|
6
|
+
# See LICENSE for details.
|
7
|
+
#
|
8
|
+
# Inspired by the official mixpanel php and python libraries.
|
9
|
+
# http://mixpanel.com/api/docs/guides/api/
|
10
|
+
|
11
|
+
require 'cgi'
|
12
|
+
require 'digest/md5'
|
13
|
+
require 'open-uri'
|
14
|
+
require 'json' unless defined?(JSON)
|
15
|
+
|
16
|
+
# Ruby library for the mixpanel.com web service
|
17
|
+
module Mixpanel
|
18
|
+
class Client
|
19
|
+
BASE_URI = 'http://mixpanel.com/api'
|
20
|
+
API_VERSION = '2.0'
|
21
|
+
|
22
|
+
# The mixpanel client can be used to easily consume data through the mixpanel API
|
23
|
+
OPTIONS = [:resource, :event, :funnel, :name, :type, :unit, :interval, :limit, :format, :bucket, :values]
|
24
|
+
attr_reader :uri
|
25
|
+
attr_accessor :api_key, :api_secret
|
26
|
+
|
27
|
+
OPTIONS.each do |option|
|
28
|
+
class_eval "
|
29
|
+
def #{option}(arg=nil)
|
30
|
+
arg ? @#{option} = arg : @#{option}
|
31
|
+
end
|
32
|
+
"
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(config)
|
36
|
+
@api_key = config['api_key']
|
37
|
+
@api_secret = config['api_secret']
|
38
|
+
end
|
39
|
+
|
40
|
+
def params
|
41
|
+
OPTIONS.inject({}) do |params, param|
|
42
|
+
option = send(param)
|
43
|
+
params.merge!(param => option) if param != :resource && !option.nil?
|
44
|
+
params
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def request(&options)
|
49
|
+
reset_options
|
50
|
+
instance_eval(&options)
|
51
|
+
@uri = URI.mixpanel(resource, normalize_params(params))
|
52
|
+
response = URI.get(@uri)
|
53
|
+
to_hash(response)
|
54
|
+
end
|
55
|
+
|
56
|
+
def normalize_params(params)
|
57
|
+
params.merge!(
|
58
|
+
:api_key => @api_key,
|
59
|
+
:expire => Time.now.to_i + 600 # Grant this request 10 minutes
|
60
|
+
).merge!(:sig => generate_signature(params))
|
61
|
+
end
|
62
|
+
|
63
|
+
def generate_signature(args)
|
64
|
+
Digest::MD5.hexdigest(args.map{|key,val| "#{key}=#{val}"}.sort.join + api_secret)
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_hash(data)
|
68
|
+
if @format == 'csv'
|
69
|
+
data
|
70
|
+
else
|
71
|
+
JSON.parse(data)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def reset_options
|
76
|
+
(OPTIONS - [:resource]).each do |option|
|
77
|
+
eval "remove_instance_variable(:@#{option}) if defined?(@#{option})"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -9,12 +9,9 @@
|
|
9
9
|
# http://mixpanel.com/api/docs/guides/api/
|
10
10
|
|
11
11
|
# URI related helpers
|
12
|
-
class
|
13
|
-
# Create an http error class for us to use
|
14
|
-
class HTTPError < StandardError; end
|
15
|
-
|
12
|
+
class Mixpanel::URI
|
16
13
|
def self.mixpanel(resource, params)
|
17
|
-
File.join([
|
14
|
+
File.join([Mixpanel::Client::BASE_URI, Mixpanel::Client::API_VERSION, resource.to_s]) + "?#{self.encode(params)}"
|
18
15
|
end
|
19
16
|
|
20
17
|
def self.encode(params)
|
data/lib/mixpanel_client.rb
CHANGED
@@ -1,2 +1,3 @@
|
|
1
|
-
require "#{File.dirname(__FILE__)}/
|
2
|
-
require "#{File.dirname(__FILE__)}/
|
1
|
+
require "#{File.dirname(__FILE__)}/mixpanel/client"
|
2
|
+
require "#{File.dirname(__FILE__)}/mixpanel/uri"
|
3
|
+
require "#{File.dirname(__FILE__)}/mixpanel/exceptions"
|
data/mixpanel_client.gemspec
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
$:.push File.expand_path('../lib', __FILE__)
|
3
|
-
require '
|
3
|
+
require 'mixpanel/version'
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = 'mixpanel_client'
|
7
|
-
s.version =
|
7
|
+
s.version = Mixpanel::Client::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.authors = ['Keolo Keagy']
|
10
10
|
s.email = ['keolo@dreampointmedia.com']
|
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
20
|
s.require_paths = ['lib']
|
21
21
|
|
22
|
-
s.add_development_dependency('rspec',
|
23
|
-
s.add_development_dependency('webmock',
|
22
|
+
s.add_development_dependency('rspec', '>=2.5.0')
|
23
|
+
s.add_development_dependency('webmock', '>=1.6.2')
|
24
24
|
s.add_development_dependency('metric_fu', '>=2.1.1')
|
25
25
|
end
|
@@ -6,7 +6,7 @@ describe 'External calls to mixpanel' do
|
|
6
6
|
before :all do
|
7
7
|
config = YAML.load_file(File.dirname(__FILE__) + '/../../config/mixpanel.yml')
|
8
8
|
config.should_not be_nil
|
9
|
-
@client =
|
9
|
+
@client = Mixpanel::Client.new(config)
|
10
10
|
end
|
11
11
|
|
12
12
|
context 'when requesting events' do
|
@@ -16,7 +16,7 @@ describe 'External calls to mixpanel' do
|
|
16
16
|
resource 'events'
|
17
17
|
end
|
18
18
|
}
|
19
|
-
data.should raise_error(
|
19
|
+
data.should raise_error(Mixpanel::URI::HTTPError)
|
20
20
|
end
|
21
21
|
|
22
22
|
it 'should return events' do
|
@@ -1,10 +1,10 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe Mixpanel::Client do
|
4
4
|
before :all do
|
5
5
|
config = {'api_key' => 'test', 'api_secret' => 'test'}
|
6
|
-
@client =
|
7
|
-
@uri = Regexp.escape(
|
6
|
+
@client = Mixpanel::Client.new(config)
|
7
|
+
@uri = Regexp.escape(Mixpanel::Client::BASE_URI)
|
8
8
|
end
|
9
9
|
|
10
10
|
context 'when making an invalid request' do
|
@@ -86,15 +86,16 @@ describe MixpanelClient do
|
|
86
86
|
event '["test-event"]'
|
87
87
|
funnel 'down-the-rabbit-hole'
|
88
88
|
name 'ricky-bobby'
|
89
|
-
type '
|
89
|
+
type 'A'
|
90
90
|
unit 'hour'
|
91
91
|
interval 24
|
92
92
|
limit 5
|
93
93
|
format 'csv'
|
94
94
|
bucket 'list'
|
95
|
+
values '["tiger", "blood"]'
|
95
96
|
end
|
96
97
|
|
97
|
-
|
98
|
+
Mixpanel::Client::OPTIONS.each do |option|
|
98
99
|
@client.send(option).should_not be_nil
|
99
100
|
end
|
100
101
|
|
@@ -102,7 +103,7 @@ describe MixpanelClient do
|
|
102
103
|
resource 'events/properties/top'
|
103
104
|
end
|
104
105
|
|
105
|
-
(
|
106
|
+
(Mixpanel::Client::OPTIONS - [:resource]).each do |option|
|
106
107
|
@client.send(option).should be_nil
|
107
108
|
end
|
108
109
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
WebMock.allow_net_connect!
|
4
|
+
|
5
|
+
describe 'External calls to mixpanel' do
|
6
|
+
before :all do
|
7
|
+
config = YAML.load_file(File.dirname(__FILE__) + '/../../config/mixpanel.yml')
|
8
|
+
config.should_not be_nil
|
9
|
+
@client = Mixpanel::Client.new(config)
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'when requesting event properties' do
|
13
|
+
it 'should raise an error for bad requests' do
|
14
|
+
data = lambda {
|
15
|
+
@client.request do
|
16
|
+
resource 'properties'
|
17
|
+
end
|
18
|
+
}
|
19
|
+
data.should raise_error(Mixpanel::URI::HTTPError)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should return events' do
|
23
|
+
data = @client.request do
|
24
|
+
resource 'events/properties'
|
25
|
+
event '["test-event"]'
|
26
|
+
name 'hello'
|
27
|
+
values '["uno", "dos"]'
|
28
|
+
type 'general'
|
29
|
+
unit 'hour'
|
30
|
+
interval 24
|
31
|
+
limit 5
|
32
|
+
bucket 'kicked'
|
33
|
+
end
|
34
|
+
data.should_not be_a Exception
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,29 +1,29 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
3
3
|
|
4
|
-
describe
|
4
|
+
describe Mixpanel::URI do
|
5
5
|
describe '.mixpanel' do
|
6
6
|
it 'should return a properly formatted mixpanel uri as a string (without an endpoint)' do
|
7
7
|
resource, params = ['events', {:c => 'see', :a => 'ey'}]
|
8
|
-
|
8
|
+
Mixpanel::URI.mixpanel(resource, params).should == 'http://mixpanel.com/api/2.0/events?a=ey&c=see'
|
9
9
|
end
|
10
10
|
it 'should return a properly formatted mixpanel uri as a string (with an endpoint)' do
|
11
11
|
resource, params = ['events/top', {:c => 'see', :a => 'ey'}]
|
12
|
-
|
12
|
+
Mixpanel::URI.mixpanel(resource, params).should == 'http://mixpanel.com/api/2.0/events/top?a=ey&c=see'
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
describe '.encode' do
|
17
17
|
it 'should return a string with url encoded values.' do
|
18
18
|
params = {:hey => '!@#$%^&*()\/"Ü', :soo => "hëllö?"}
|
19
|
-
|
19
|
+
Mixpanel::URI.encode(params).should == 'hey=%21%40%23%24%25%5E%26%2A%28%29%5C%2F%22%C3%9C&soo=h%C3%ABll%C3%B6%3F'
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
describe '.get' do
|
24
24
|
it 'should return a string response' do
|
25
25
|
stub_request(:get, 'http://example.com').to_return(:body => 'something')
|
26
|
-
|
26
|
+
Mixpanel::URI.get('http://example.com').should == 'something'
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
metadata
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mixpanel_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 62196449
|
5
|
+
prerelease: 6
|
6
6
|
segments:
|
7
|
-
-
|
7
|
+
- 2
|
8
|
+
- 0
|
8
9
|
- 0
|
10
|
+
- beta
|
9
11
|
- 1
|
10
|
-
version:
|
12
|
+
version: 2.0.0.beta1
|
11
13
|
platform: ruby
|
12
14
|
authors:
|
13
15
|
- Keolo Keagy
|
@@ -15,7 +17,7 @@ autorequire:
|
|
15
17
|
bindir: bin
|
16
18
|
cert_chain: []
|
17
19
|
|
18
|
-
date: 2011-
|
20
|
+
date: 2011-05-18 00:00:00 -07:00
|
19
21
|
default_executable:
|
20
22
|
dependencies:
|
21
23
|
- !ruby/object:Gem::Dependency
|
@@ -84,13 +86,15 @@ files:
|
|
84
86
|
- README.md
|
85
87
|
- Rakefile
|
86
88
|
- config/mixpanel.template.yml
|
89
|
+
- lib/mixpanel/client.rb
|
90
|
+
- lib/mixpanel/exceptions.rb
|
91
|
+
- lib/mixpanel/uri.rb
|
92
|
+
- lib/mixpanel/version.rb
|
87
93
|
- lib/mixpanel_client.rb
|
88
|
-
- lib/mixpanel_client/mixpanel_client.rb
|
89
|
-
- lib/mixpanel_client/uri.rb
|
90
|
-
- lib/mixpanel_client/version.rb
|
91
94
|
- mixpanel_client.gemspec
|
92
95
|
- spec/mixpanel_client/events_externalspec.rb
|
93
96
|
- spec/mixpanel_client/mixpanel_client_spec.rb
|
97
|
+
- spec/mixpanel_client/properties_externalspec.rb
|
94
98
|
- spec/mixpanel_client/uri_spec.rb
|
95
99
|
- spec/spec_helper.rb
|
96
100
|
has_rdoc: true
|
@@ -114,12 +118,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
114
118
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
119
|
none: false
|
116
120
|
requirements:
|
117
|
-
- - "
|
121
|
+
- - ">"
|
118
122
|
- !ruby/object:Gem::Version
|
119
|
-
hash:
|
123
|
+
hash: 25
|
120
124
|
segments:
|
121
|
-
-
|
122
|
-
|
125
|
+
- 1
|
126
|
+
- 3
|
127
|
+
- 1
|
128
|
+
version: 1.3.1
|
123
129
|
requirements: []
|
124
130
|
|
125
131
|
rubyforge_project: mixpanel_client
|
@@ -130,5 +136,6 @@ summary: Ruby Mixpanel API Client Library
|
|
130
136
|
test_files:
|
131
137
|
- spec/mixpanel_client/events_externalspec.rb
|
132
138
|
- spec/mixpanel_client/mixpanel_client_spec.rb
|
139
|
+
- spec/mixpanel_client/properties_externalspec.rb
|
133
140
|
- spec/mixpanel_client/uri_spec.rb
|
134
141
|
- spec/spec_helper.rb
|
@@ -1,79 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby -Ku
|
2
|
-
|
3
|
-
# Mixpanel API Ruby Client Library
|
4
|
-
#
|
5
|
-
# Copyright (c) 2009+ Keolo Keagy
|
6
|
-
# See LICENSE for details.
|
7
|
-
#
|
8
|
-
# Inspired by the official mixpanel php and python libraries.
|
9
|
-
# http://mixpanel.com/api/docs/guides/api/
|
10
|
-
|
11
|
-
require 'cgi'
|
12
|
-
require 'digest/md5'
|
13
|
-
require 'open-uri'
|
14
|
-
require 'json' unless defined?(JSON)
|
15
|
-
|
16
|
-
# Ruby library for the mixpanel.com web service
|
17
|
-
class MixpanelClient
|
18
|
-
BASE_URI = 'http://mixpanel.com/api'
|
19
|
-
API_VERSION = '2.0'
|
20
|
-
|
21
|
-
# The mixpanel client can be used to easily consume data through the mixpanel API
|
22
|
-
OPTIONS = [:resource, :event, :funnel, :name, :type, :unit, :interval, :limit, :format, :bucket]
|
23
|
-
attr_reader :uri
|
24
|
-
attr_accessor :api_key, :api_secret
|
25
|
-
|
26
|
-
OPTIONS.each do |option|
|
27
|
-
class_eval "
|
28
|
-
def #{option}(arg=nil)
|
29
|
-
arg ? @#{option} = arg : @#{option}
|
30
|
-
end
|
31
|
-
"
|
32
|
-
end
|
33
|
-
|
34
|
-
def initialize(config)
|
35
|
-
@api_key = config['api_key']
|
36
|
-
@api_secret = config['api_secret']
|
37
|
-
end
|
38
|
-
|
39
|
-
def params
|
40
|
-
OPTIONS.inject({}) do |params, param|
|
41
|
-
option = send(param)
|
42
|
-
params.merge!(param => option) if param != :resource && !option.nil?
|
43
|
-
params
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def request(&options)
|
48
|
-
reset_options
|
49
|
-
instance_eval(&options)
|
50
|
-
@uri = URI.mixpanel(resource, normalize_params(params))
|
51
|
-
response = URI.get(@uri)
|
52
|
-
to_hash(response)
|
53
|
-
end
|
54
|
-
|
55
|
-
def normalize_params(params)
|
56
|
-
params.merge!(
|
57
|
-
:api_key => @api_key,
|
58
|
-
:expire => Time.now.to_i + 600 # Grant this request 10 minutes
|
59
|
-
).merge!(:sig => generate_signature(params))
|
60
|
-
end
|
61
|
-
|
62
|
-
def generate_signature(args)
|
63
|
-
Digest::MD5.hexdigest(args.map{|key,val| "#{key}=#{val}"}.sort.join + api_secret)
|
64
|
-
end
|
65
|
-
|
66
|
-
def to_hash(data)
|
67
|
-
if @format == 'csv'
|
68
|
-
data
|
69
|
-
else
|
70
|
-
JSON.parse(data)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def reset_options
|
75
|
-
(OPTIONS - [:resource]).each do |option|
|
76
|
-
eval "remove_instance_variable(:@#{option}) if defined?(@#{option})"
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|