mixpannenkoek 0.0.3 → 0.0.4

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: bc559cc6c5f4a55a318e78cfea0dd26fc5abcb8d
4
- data.tar.gz: ba9b0af6737d9c8c4c7fbfdf7bd495ce53d0383d
3
+ metadata.gz: c522161ceeb1a1cb920c8df995fbbdc742a9afa8
4
+ data.tar.gz: 9700672e659aa9477cadc2ca4bfb4d4a2dcaafca
5
5
  SHA512:
6
- metadata.gz: a3bcaaef791d0848be8bf5bfd31e0434ecd51d3318037655bc2892ab2e56177707a1077c1fb75750cda43bf6065c2b7360d402200e4e21c1a60e3965fd840cd7
7
- data.tar.gz: 90a9757a0c9db3d992688481ae9d0acd76d5e2e2176809851715d367668948b9cdc24ee7ef606d0a5b3c6d4a56d8f208a4202648ff7824299cb16e7bb3441a10
6
+ metadata.gz: 14103a289695a37071c7f700705436665f95085d172477f885a8563f16b6d9c144dc54874ea3b435275842082854abb567ac4c03d3ef6cf5c5a19215790a12b0
7
+ data.tar.gz: 8cef5cadd63a7ecd662fba07f4154e8e17c41bc0f4dc576449ef529c08b7f90596cd59b36f72ffe79d4c7eda8a16dc8f66a69f8f30d7ad5feccdd196b53129f9
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ # - 1.9.3
5
+ # - 1.8.7
6
+ # - jruby-19mode
7
+ # - rubinius
8
+
9
+ script: "bundle exec rspec"
data/CONTRIBUTORS.md ADDED
@@ -0,0 +1,3 @@
1
+ ## Contributors to mixpannenkoek
2
+ * [Derek Kraan](https://github.com/derekkraan)
3
+ * [Tim Flapper](https://github.com/timflapper)
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Mixpannenkoek
2
2
 
3
- This gem implements a fluent query interface for the [mixpanel_client](https://github.com/keolo/mixpanel_client) gem.
3
+ Mixpannenkoek implements a fluent query interface for Mixpanel. It uses the [mixpanel_client](https://github.com/keolo/mixpanel_client) gem.
4
+
5
+ Read the [introductory blogpost](http://devblog.springest.com/introducing-mixpannenkoek).
4
6
 
5
7
  [![Code Climate](https://codeclimate.com/github/Springest/mixpannenkoek.png)](https://codeclimate.com/github/Springest/mixpannenkoek)
6
8
 
@@ -20,7 +22,7 @@ Or install it yourself as:
20
22
 
21
23
  ## Usage
22
24
 
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.
25
+ Begin by creating and configuring a model. In Rails you might place this in `app/models/funnel/conversions.rb`. You *must* set the api_key, api_secret, and endpoint. These properties are heritable, so in the following example, `Funnel::Conversions` will have the api_key, api_secret and endpoint set in `Funnel::Base`.
24
26
 
25
27
  ```ruby
26
28
  class Funnel::Base < Mixpannenkoek::Base
@@ -38,7 +40,7 @@ With mixpanel_client, you might run a query like this:
38
40
 
39
41
  ```ruby
40
42
  client = Mixpanel::Client.new(
41
- api_key: 'MY_API_KEY',
43
+ api_key: 'MY_API_KEY',
42
44
  api_secret: 'MY_API_SECRET'
43
45
  )
44
46
 
@@ -59,7 +61,7 @@ With mixpannenkoek, you would write it like this (making use of the models defin
59
61
  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')
60
62
  ```
61
63
 
62
- `where` allows you to easily build the `where` parameter of the request.
64
+ `where` and `where.not` allow you to easily build the `where` parameter of the request.
63
65
 
64
66
  `group` corresponds to the `on` parameter.
65
67
 
@@ -125,7 +127,7 @@ Funnel::Conversions.where(date: 31.days.ago..1.day.ago).response_data
125
127
  "goal"=>"View signup",
126
128
  "overall_conv_ratio"=>0.12362030905077263,
127
129
  "step_conv_ratio"=>0.12362030905077263}]}}
128
-
130
+
129
131
  Funnel::Conversions.where(date: 31.days.ago..1.day.ago)['2010-05-24']['steps'][0]['count']
130
132
  #=> 762
131
133
 
@@ -8,17 +8,23 @@ module Mixpannenkoek
8
8
 
9
9
  attr_accessor :where, :group, :klass
10
10
 
11
- def initialize(klass, where = {}, vars = {}, group = nil)
11
+ def initialize(klass, where = {}, where_not = {}, vars = {}, group = nil)
12
12
  @where = where
13
+ @where_not = where_not
13
14
  @vars = vars
14
15
  @group = group
15
16
  @klass = klass
16
17
  end
17
18
 
18
- def where(condition)
19
+ def where(condition = nil)
20
+ return self if condition.nil?
19
21
  chain(where: @where.merge(condition))
20
22
  end
21
23
 
24
+ def not(condition)
25
+ chain(where_not: @where_not.merge(condition))
26
+ end
27
+
22
28
  def set(variable)
23
29
  chain(vars: @vars.merge(variable))
24
30
  end
@@ -56,26 +62,24 @@ module Mixpannenkoek
56
62
  def query
57
63
  query = @vars
58
64
 
59
- if @where && @where != {}
65
+ if (@where && @where != {}) || (@where_not && @where_not != {})
66
+ query[:where] = []
67
+
60
68
  extract_dates(query, @where)
61
69
 
62
- query[:where] = @where.map do |key,value|
63
- next if key == :date
70
+ query[:where] += @where.map do |key,value|
71
+ where_clause(key, value)
72
+ end
64
73
 
65
- case value
66
- when Array
67
- %Q((#{value.map { |val| %Q(properties["#{key}"] == "#{val}") }.join(' or ')}))
68
- else
69
- %Q(properties["#{key}"] == "#{value}")
70
- end
71
- end.compact
74
+ query[:where] += @where_not.map do |key,value|
75
+ where_not_clause(key, value)
76
+ end
72
77
 
73
- query[:where] =
74
- if query[:where]
75
- query[:where].join(' and ')
76
- else
77
- nil
78
- end
78
+ if query[:where].compact != []
79
+ query[:where] = query[:where].compact.join(' and ')
80
+ else
81
+ query.delete(:where)
82
+ end
79
83
  end
80
84
 
81
85
  query[:on] = %Q(properties["#{@group}"]) if @group
@@ -85,8 +89,8 @@ module Mixpannenkoek
85
89
 
86
90
  private
87
91
 
88
- def chain(klass: @klass, where: @where, vars: @vars, group: @group)
89
- self.class.new(klass, where, vars, group)
92
+ def chain(klass: @klass, where: @where, where_not: @where_not, vars: @vars, group: @group)
93
+ self.class.new(klass, where, where_not, vars, group)
90
94
  end
91
95
 
92
96
  def mixpanel_client
@@ -125,5 +129,20 @@ module Mixpannenkoek
125
129
  query[:from_date] = query[:from_date].strftime('%Y-%m-%d') if query[:from_date].respond_to? :strftime
126
130
  query[:to_date] = query[:to_date].strftime('%Y-%m-%d') if query[:to_date].respond_to? :strftime
127
131
  end
132
+
133
+ def where_clause(key, value, operator = '==', join = 'or')
134
+ return nil if key == :date
135
+
136
+ case value
137
+ when Array
138
+ %Q((#{value.map { |val| %Q(properties["#{key}"] #{operator} "#{val}") }.join(" #{join} ")}))
139
+ else
140
+ %Q(properties["#{key}"] #{operator} "#{value}")
141
+ end
142
+ end
143
+
144
+ def where_not_clause(key, value)
145
+ where_clause(key, value, '!=', 'and')
146
+ end
128
147
  end
129
148
  end
@@ -1,3 +1,3 @@
1
1
  module Mixpannenkoek
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -57,6 +57,27 @@ describe Mixpannenkoek::Base do
57
57
  end
58
58
  end
59
59
 
60
+ describe '#where.not' do
61
+ subject { Mixpannenkoek::TestQuery.where(date: date_range).where.not(subject_name: 'Subject ABC').request_parameters[1] }
62
+ it 'sets :where' do
63
+ expect(subject).to include({ where: 'properties["subject_name"] != "Subject ABC"' })
64
+ end
65
+
66
+ context 'called twice' do
67
+ subject { Mixpannenkoek::TestQuery.where(date: date_range).where.not(subject_name: 'Subject ABC').where.not(training_name: 'Training XYZ').request_parameters[1] }
68
+ it 'sets multiple :where conditions' do
69
+ expect(subject).to include({ where: 'properties["subject_name"] != "Subject ABC" and properties["training_name"] != "Training XYZ"' })
70
+ end
71
+ end
72
+
73
+ context 'called with value: []' do
74
+ subject { Mixpannenkoek::TestQuery.where(date: date_range).where.not(subject_name: ['Subject ABC', 'Subject XYZ']).request_parameters[1] }
75
+ it 'sets multiple :where conditions' do
76
+ expect(subject).to include({ where: '(properties["subject_name"] != "Subject ABC" and properties["subject_name"] != "Subject XYZ")' })
77
+ end
78
+ end
79
+ end
80
+
60
81
  describe '#group' do
61
82
  subject { Mixpannenkoek::TestQuery.where(date: date_range).group('subject_name').request_parameters[1] }
62
83
  it 'sets :on' do
@@ -73,7 +94,7 @@ describe Mixpannenkoek::Base do
73
94
 
74
95
  describe '#set' do
75
96
  subject { Mixpannenkoek::TestQuery.set(funnel_id: 12345).where(date: date_range).request_parameters[1] }
76
- it { should include({ funnel_id: 12345 }) }
97
+ it { expect(subject).to include({ funnel_id: 12345 }) }
77
98
 
78
99
  context 'called twice' do
79
100
  subject { Mixpannenkoek::TestQuery.set(funnel_id: 12345).set(event: 'click').where(date: date_range).request_parameters[1] }
@@ -92,7 +113,7 @@ describe Mixpannenkoek::Base do
92
113
 
93
114
  context 'missing api_key' do
94
115
  subject { Mixpannenkoek::TestQuery.where(date: date_range).to_hash }
95
- before { Mixpannenkoek::TestQuery.stub(:api_key) { nil } }
116
+ before { allow(Mixpannenkoek::TestQuery).to receive(:api_key) { nil } }
96
117
  it 'raises Mixpannenkoek::Query::MissingConfiguration' do
97
118
  expect { subject }.to raise_error Mixpannenkoek::Query::MissingConfiguration, 'The mixpanel api_key has not been configured'
98
119
  end
@@ -100,7 +121,7 @@ describe Mixpannenkoek::Base do
100
121
 
101
122
  context 'missing api_secret' do
102
123
  subject { Mixpannenkoek::TestQuery.where(date: date_range).to_hash }
103
- before { Mixpannenkoek::TestQuery.stub(:api_secret) { nil } }
124
+ before { allow(Mixpannenkoek::TestQuery).to receive(:api_secret) { nil } }
104
125
  it 'raises Mixpannenkoek::Query::MissingConfiguration' do
105
126
  expect { subject }.to raise_error Mixpannenkoek::Query::MissingConfiguration, 'The mixpanel api_secret has not been configured'
106
127
  end
@@ -111,7 +132,7 @@ describe Mixpannenkoek::Base do
111
132
  subject { Mixpannenkoek::TestQuery.where(date: date_range).results }
112
133
 
113
134
  let(:response_data) { JSON.parse(File.open("#{File.dirname(__FILE__)}/../../fixtures/funnel_response_data.json").read) }
114
- before { Mixpanel::Client.any_instance.stub(:request).and_return(response_data) }
135
+ before { allow_any_instance_of(Mixpanel::Client).to receive(:request).and_return(response_data) }
115
136
 
116
137
  it 'returns a Mixpannenkoek::Results::Base object' do
117
138
  expect(subject).to be_a Mixpannenkoek::Results::Base
@@ -119,7 +140,7 @@ describe Mixpannenkoek::Base do
119
140
  end
120
141
 
121
142
  describe '#method_missing' do
122
- before { Mixpanel::Client.any_instance.stub(:request).and_return({ "data" => { "2014-01-01" => [{ 'count' => '1' }, { 'count' => '4' }] } }) }
143
+ before { allow_any_instance_of(Mixpanel::Client).to receive(:request).and_return({ "data" => { "2014-01-01" => [{ 'count' => '1' }, { 'count' => '4' }] } }) }
123
144
  it 'delegates all calls to #results.send(*args)' do
124
145
  expect(Mixpannenkoek::TestQuery.where(date: date_range).keys).to eq ['2014-01-01']
125
146
  end
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.3
4
+ version: 0.0.4
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-28 00:00:00.000000000 Z
11
+ date: 2014-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mixpanel_client
@@ -75,6 +75,8 @@ extensions: []
75
75
  extra_rdoc_files: []
76
76
  files:
77
77
  - .gitignore
78
+ - .travis.yml
79
+ - CONTRIBUTORS.md
78
80
  - Gemfile
79
81
  - LICENSE.txt
80
82
  - README.md