mixpannenkoek 0.0.2 → 0.0.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a7399793b312fda362a710cab02c13c0061f3181
4
- data.tar.gz: 3d4990a74dc58c7d011c83d7d9f0794ceb0eba76
3
+ metadata.gz: bc559cc6c5f4a55a318e78cfea0dd26fc5abcb8d
4
+ data.tar.gz: ba9b0af6737d9c8c4c7fbfdf7bd495ce53d0383d
5
5
  SHA512:
6
- metadata.gz: ebed70b8633f90ef5976f67defa8d8351e47e5ac9690fafd1ec192f0d55151c25f212aa0235d604892a3e419d7b090dcc39f3026d4f1d5a890fe20ecca329634
7
- data.tar.gz: a93ba52c09f8f521558305c7432f5115bc253f43d9f25933eeb6422c0df350984a5ced58048153356829fa565bf937fb77c291e75ddea36733c7c6e5451095f1
6
+ metadata.gz: a3bcaaef791d0848be8bf5bfd31e0434ecd51d3318037655bc2892ab2e56177707a1077c1fb75750cda43bf6065c2b7360d402200e4e21c1a60e3965fd840cd7
7
+ data.tar.gz: 90a9757a0c9db3d992688481ae9d0acd76d5e2e2176809851715d367668948b9cdc24ee7ef606d0a5b3c6d4a56d8f208a4202648ff7824299cb16e7bb3441a10
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Mixpannenkoek
2
2
 
3
- This gem implements a fluent query interface for mixpanel_client.
3
+ This gem implements a fluent query interface for the [mixpanel_client](https://github.com/keolo/mixpanel_client) gem.
4
+
5
+ [![Code Climate](https://codeclimate.com/github/Springest/mixpannenkoek.png)](https://codeclimate.com/github/Springest/mixpannenkoek)
4
6
 
5
7
  ## Installation
6
8
 
@@ -18,46 +20,124 @@ Or install it yourself as:
18
20
 
19
21
  ## Usage
20
22
 
21
- Begin by configuring a model.
23
+ Begin by creating and configuring a model. In Rails you might place this in `app/models/conversion_funnel.rb`. You *must* set the api_key, api_secret, and endpoint. These properties are heritable, so in the following example, `OtherFunnel` will have the api_key, api_secret and endpoint already set.
22
24
 
23
25
  ```ruby
24
- class ConversionFunnel < Mixpannenkoek::Base
26
+ class Funnel::Base < Mixpannenkoek::Base
25
27
  set_api_key 'MY_API_KEY'
26
28
  set_api_secret 'MY_API_SECRET'
27
- set_endpoint 'funnels'
28
-
29
+ set_endpoint 'funnels' # or any other endpoint
30
+ end
31
+
32
+ class Funnel::Conversions < Funnel::Base
29
33
  default_scope { set(funnel_id: 123456) }
30
34
  end
31
35
  ```
32
36
 
33
- Build up a query with `where`, `group`, and `set`.
37
+ With mixpanel_client, you might run a query like this:
34
38
 
35
39
  ```ruby
36
- ConversionFunnel.where(date: 31.days.ago..1.day.ago)
37
-
38
- ConversionFunnel.where(date: 31.days.ago..1.day.ago).where(user_id: 123).set(interval: 50).group('traffic_source')
40
+ client = Mixpanel::Client.new(
41
+ api_key: 'MY_API_KEY',
42
+ api_secret: 'MY_API_SECRET'
43
+ )
44
+
45
+ data = client.request(
46
+ 'funnels',
47
+ funnel_id: 123456,
48
+ from_date: '2014-01-01',
49
+ to_date: '2014-01-31',
50
+ interval: 31,
51
+ on: 'properties["traffic_source"]',
52
+ where: 'properties["user_type"] = "visitor" AND properties["landing_page"] = "homepage"',
53
+ )
39
54
  ```
40
55
 
41
- Operate on the query results fluently
56
+ With mixpannenkoek, you would write it like this (making use of the models defined above):
42
57
 
43
58
  ```ruby
44
- ConversionFunnel.where(date: 31.days.ago..1.day.ago).map { |date,data| data['steps'].last['count'] }
45
- #=> [1, 4, 2]
59
+ Funnel::Conversions.where(date: Date.parse('2014-01-01')..Date.parse('2014-01-31')).set(interval: 31).group('traffic_source').where(user_type: 'visitor').where(landing_page: 'homepage')
46
60
  ```
47
61
 
48
- Organize your query models with default scopes. Default scopes are heritable, so they will be automatically be applied to subclasses.
62
+ `where` allows you to easily build the `where` parameter of the request.
63
+
64
+ `group` corresponds to the `on` parameter.
65
+
66
+ `set` sets any other parameters in the request (in this case, `funnel_id` is mandatory).
67
+
68
+ This gem also supports `default_scope`, which is also heritable. Some of the parameters above might instead be set in the model, to save time.
69
+
49
70
  ```ruby
50
- class ConversionFunnel < Mixpannenkoek::Base
51
- default_scope { set(interval: 50) }
71
+ class Funnel::Conversions < Funnel::Base
72
+ default_scope { set(funnel_id: 123456) }
73
+ default_scope { set(interval: 31) }
52
74
  default_scope { where(user_type: 'visitor') }
53
75
  end
76
+ ```
54
77
 
55
- # default scopes are heritable
56
- # (GroupedConversionFunnel will get the default scopes
57
- # of ConversionFunnel, in addition to its own)
58
- class GroupedConversionFunnel < ConversionFunnel
59
- default_scope { group('traffic_source') }
60
- end
78
+ Building up the query then becomes a little bit easier:
79
+
80
+ ```ruby
81
+ Funnel::Conversions.where(date: Date.parse('2014-01-01')..Date.parse('2014-01-31')).group('traffic_source').where(landing_page: 'homepage')
82
+ ```
83
+
84
+ Note: you are not required to set the `funnel_id` in the model itself. The following queries are possible:
85
+
86
+ ```ruby
87
+ Funnel::Base.set(funnel_id: 987654).where(date: range)
88
+ ```
89
+
90
+ Operating on the response data is also fluent. Just call a method (including `[]`). This will trigger a request to the mixpanel API and make the response data available:
91
+
92
+ ```ruby
93
+ Funnel::Conversions.where(date: 31.days.ago..1.day.ago).response_data
94
+ #=> {"2010-05-24"=>
95
+ {"analysis"=>
96
+ {"completion"=>0.0646793595800525,
97
+ "starting_amount"=>762,
98
+ "steps"=>3,
99
+ "worst"=>2},
100
+ "steps"=>
101
+ [{"count"=>762,
102
+ "goal"=>"pages",
103
+ "overall_conv_ratio"=>1.0,
104
+ "step_conv_ratio"=>1.0},
105
+ {"count"=>69,
106
+ "goal"=>"View signup",
107
+ "overall_conv_ratio"=>0.09055118110236221,
108
+ "step_conv_ratio"=>0.09055118110236221},
109
+ {"count"=>10,
110
+ "goal"=>"View docs",
111
+ "overall_conv_ratio"=>0.0646793595800525,
112
+ "step_conv_ratio"=>0.7142857142857143}]},
113
+ "2010-05-31"=>
114
+ {"analysis"=>
115
+ {"completion"=>0.12362030905077263,
116
+ "starting_amount"=>906,
117
+ "steps"=>2,
118
+ "worst"=>2},
119
+ "steps"=>
120
+ [{"count"=>906,
121
+ "goal"=>"homepage",
122
+ "overall_conv_ratio"=>1.0,
123
+ "step_conv_ratio"=>1.0},
124
+ {"count"=>112,
125
+ "goal"=>"View signup",
126
+ "overall_conv_ratio"=>0.12362030905077263,
127
+ "step_conv_ratio"=>0.12362030905077263}]}}
128
+
129
+ Funnel::Conversions.where(date: 31.days.ago..1.day.ago)['2010-05-24']['steps'][0]['count']
130
+ #=> 762
131
+
132
+ Funnel::Conversions.where(date: 31.days.ago..1.day.ago).map { |date,data| data['steps'].last['count'] }
133
+ #=> [10, 112]
134
+ ```
135
+
136
+ Query objects are also immutable. So it's possible to organize your code in the following manner:
137
+
138
+ ```ruby
139
+ query_1 = Funnel::Conversions.where(date: 31.days.ago..1.day.ago)
140
+ query_2 = query_1.where(traffic_source: 'google') # this leaves query_1 unchanged
61
141
  ```
62
142
 
63
143
  ## Contributing
@@ -5,33 +5,28 @@ module Mixpannenkoek
5
5
  extend ::Mixpannenkoek::ClassInheritableAttribute
6
6
  class_inheritable_attribute :_api_key, :_api_secret, :_endpoint, :_default_scope
7
7
 
8
- # Public: the mixpanel api key
9
8
  def self.set_api_key(api_key = nil, &block)
10
- raise ArgumentError if !api_key.nil? && !block.nil?
11
- self._api_key = api_key || block
9
+ self._api_key = value_or_block(api_key, &block)
12
10
  end
13
11
 
14
- def self.api_key
15
- self._api_key.respond_to?(:call) ? self._api_key.call : self._api_key
12
+ def self.set_api_secret(api_secret = nil, &block)
13
+ self._api_secret = value_or_block(api_secret, &block)
16
14
  end
17
15
 
18
- # Public: the mixpanel api secret
19
- def self.set_api_secret(api_secret = nil, &block)
20
- raise ArgumentError if !api_secret.nil? && !block.nil?
21
- self._api_secret = api_secret || block
16
+ def self.set_endpoint(endpoint = nil, &block)
17
+ self._endpoint = value_or_block(endpoint, &block)
22
18
  end
23
19
 
24
- def self.api_secret
25
- self._api_secret.respond_to?(:call) ? self._api_secret.call : self._api_secret
20
+ def self.api_key
21
+ value_from_block(self._api_key)
26
22
  end
27
23
 
28
- def self.set_endpoint(endpoint = nil, &block)
29
- raise ArgumentError if !endpoint.nil? && !block.nil?
30
- self._endpoint = endpoint || block
24
+ def self.api_secret
25
+ value_from_block(self._api_secret)
31
26
  end
32
27
 
33
28
  def self.endpoint
34
- self._endpoint.respond_to?(:call) ? self._endpoint.call : self._endpoint
29
+ value_from_block(self._endpoint)
35
30
  end
36
31
 
37
32
  ### Class methods (for convenience)
@@ -63,5 +58,15 @@ module Mixpannenkoek
63
58
  self._default_scope ||= []
64
59
  self._default_scope.map{ |p| p.call }
65
60
  end
61
+
62
+ private
63
+ def self.value_or_block(value, &block)
64
+ raise ArgumentError unless !!value ^ !!block
65
+ value || block
66
+ end
67
+
68
+ def self.value_from_block(value_or_proc)
69
+ value_or_proc.respond_to?(:call) ? value_or_proc.call : value_or_proc
70
+ end
66
71
  end
67
72
  end
@@ -2,20 +2,28 @@ module Mixpannenkoek
2
2
  module ClassInheritableAttribute
3
3
  def class_inheritable_attribute(*attributes)
4
4
  attributes.map(&:to_sym).each do |attribute|
5
- define_singleton_method("#{attribute}=") do |value|
6
- @@class_inheritable_attributes ||= {}
7
- @@class_inheritable_attributes[attribute] ||= {}
5
+ create_setter(attribute)
6
+ create_getter(attribute)
7
+ end
8
+ end
8
9
 
9
- @@class_inheritable_attributes[attribute][self.name] = value
10
- end
11
- define_singleton_method(attribute) do
12
- if @@class_inheritable_attributes[attribute] && @@class_inheritable_attributes[attribute].has_key?(self.name)
13
- @@class_inheritable_attributes[attribute][self.name]
14
- elsif superclass.respond_to?(attribute)
15
- superclass.send(attribute)
16
- else
17
- nil
18
- end
10
+ def create_setter(attribute)
11
+ define_singleton_method("#{attribute}=") do |value|
12
+ @@class_inheritable_attributes ||= {}
13
+ @@class_inheritable_attributes[attribute] ||= {}
14
+
15
+ @@class_inheritable_attributes[attribute][self.name] = value
16
+ end
17
+ end
18
+
19
+ def create_getter(attribute)
20
+ define_singleton_method(attribute) do
21
+ if @@class_inheritable_attributes[attribute] && @@class_inheritable_attributes[attribute].has_key?(self.name)
22
+ @@class_inheritable_attributes[attribute][self.name]
23
+ elsif superclass.respond_to?(attribute)
24
+ superclass.send(attribute)
25
+ else
26
+ nil
19
27
  end
20
28
  end
21
29
  end
@@ -57,10 +57,7 @@ module Mixpannenkoek
57
57
  query = @vars
58
58
 
59
59
  if @where && @where != {}
60
- if @where[:date]
61
- query[:from_date] = @where[:date].first.strftime('%Y-%m-%d')
62
- query[:to_date] = @where[:date].last.strftime('%Y-%m-%d')
63
- end
60
+ extract_dates(query, @where)
64
61
 
65
62
  query[:where] = @where.map do |key,value|
66
63
  next if key == :date
@@ -118,5 +115,15 @@ module Mixpannenkoek
118
115
  time = ::Benchmark.ms(&block)
119
116
  Rails.logger.info " Mixpanel (#{time.round(1)}ms) #{request_parameters.inspect}" if defined? Rails
120
117
  end
118
+
119
+ def extract_dates(query, where)
120
+ return unless where[:date]
121
+
122
+ query[:from_date] = where[:date].first
123
+ query[:to_date] = where[:date].last
124
+
125
+ query[:from_date] = query[:from_date].strftime('%Y-%m-%d') if query[:from_date].respond_to? :strftime
126
+ query[:to_date] = query[:to_date].strftime('%Y-%m-%d') if query[:to_date].respond_to? :strftime
127
+ end
121
128
  end
122
129
  end
@@ -1,3 +1,3 @@
1
1
  module Mixpannenkoek
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -43,6 +43,17 @@ describe Mixpannenkoek::Base do
43
43
  it 'sets :to_date' do
44
44
  expect(subject).to include({ to_date: '2014-01-05' })
45
45
  end
46
+
47
+ context 'date strings' do
48
+ let(:date_range) { '2014-01-01'..'2014-01-31' }
49
+ it 'sets :from_date' do
50
+ expect(subject).to include({ from_date: '2014-01-01' })
51
+ end
52
+
53
+ it 'sets :to_date' do
54
+ expect(subject).to include({ to_date: '2014-01-31' })
55
+ end
56
+ end
46
57
  end
47
58
  end
48
59
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mixpannenkoek
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derek Kraan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-27 00:00:00.000000000 Z
11
+ date: 2014-05-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mixpanel_client