mixpannenkoek 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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