mixpanel_client 1.0.1 → 2.0.0.beta1
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 +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
|