keen 0.6.0 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +28 -7
- data/lib/keen/client/publishing_methods.rb +9 -6
- data/lib/keen/client/querying_methods.rb +22 -1
- data/lib/keen/client.rb +10 -6
- data/lib/keen/version.rb +1 -1
- data/lib/keen.rb +4 -0
- data/spec/integration/api_spec.rb +1 -1
- data/spec/keen/client/publishing_methods_spec.rb +18 -3
- data/spec/keen/client/querying_methods_spec.rb +1 -1
- data/spec/keen/client_spec.rb +14 -5
- data/spec/synchrony/synchrony_spec.rb +1 -1
- metadata +2 -2
data/README.md
CHANGED
@@ -29,13 +29,15 @@ Before making any API calls, you must supply keen-gem with a Project ID.
|
|
29
29
|
The recommended way to do this is to set `KEEN_PROJECT_ID` in your
|
30
30
|
environment. If you're using [foreman](http://ddollar.github.com/foreman/), add this to your `.env` file:
|
31
31
|
|
32
|
-
KEEN_PROJECT_ID=
|
32
|
+
KEEN_PROJECT_ID=xxxxxxxxxxxxxxxx
|
33
33
|
|
34
|
-
|
34
|
+
If not, make to to export the variable into your shell or put it before the command you use to start your server.
|
35
|
+
|
36
|
+
When you deploy, make sure your production environment variables are set. For example,
|
35
37
|
set [config vars](https://devcenter.heroku.com/articles/config-vars) on Heroku. (We recommend this
|
36
38
|
environment-based approach because it keeps sensitive information out of the codebase. If you can't do this, see the alternatives below.)
|
37
39
|
|
38
|
-
If your environment is set up property, `Keen` is ready go immediately. Publish an event like this:
|
40
|
+
If your environment is set up property, `Keen` is ready to go immediately. Publish an event like this:
|
39
41
|
|
40
42
|
```ruby
|
41
43
|
Keen.publish("sign_ups", { :username => "lloyd", :referred_by => "harry" })
|
@@ -60,7 +62,11 @@ method to send events.
|
|
60
62
|
To compare asychronous vs. synchronous performance, check out the [keen-gem-example](http://keen-gem-example.herokuapp.com/) app.
|
61
63
|
|
62
64
|
To publish asynchronously, first add
|
63
|
-
[em-http-request](https://github.com/igrigorik/em-http-request) to your Gemfile.
|
65
|
+
[em-http-request](https://github.com/igrigorik/em-http-request) to your Gemfile. Make sure it's version 1.0 or above.
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
gem "em-http-request", "~> 1.0"
|
69
|
+
```
|
64
70
|
|
65
71
|
Next, run an instance of EventMachine. If you're using an EventMachine-based web server like
|
66
72
|
thin or goliath you're already doing this. Otherwise, you'll need to start an EventMachine loop manually as follows:
|
@@ -93,7 +99,7 @@ Unlike event publishing, queries require that an API Key is provided. Just like
|
|
93
99
|
|
94
100
|
KEEN_API_KEY=your-api-key
|
95
101
|
|
96
|
-
Here's are some examples of querying with
|
102
|
+
Here's are some examples of querying with keen-gem. Let's assume you've added some events to the "purchases" collection.
|
97
103
|
|
98
104
|
```ruby
|
99
105
|
Keen.count("purchases") # => 100
|
@@ -110,9 +116,14 @@ Keen.select_unique("purchases", :target_property => "username") # => ["bob", "l
|
|
110
116
|
Keen.extraction("purchases") # => [{ "price" => 20, ... }, { ... }]
|
111
117
|
|
112
118
|
Keen.funnel(:steps => [
|
113
|
-
{ :actor_property => "username",
|
114
|
-
{ :actor_property => "username",
|
119
|
+
{ :actor_property => "username", :event_collection => "purchases" },
|
120
|
+
{ :actor_property => "username", :event_collection => "referrals" },
|
115
121
|
{ ... }]) # => [20, 15 ...]
|
122
|
+
|
123
|
+
Keen.multi_analysis("purchases", analyses: {
|
124
|
+
:gross => { :analysis_type => "sum", :target_property => "price" },
|
125
|
+
:customers => { :analysis_type => "count_unique", :target_property => "username" } },
|
126
|
+
:timeframe => 'today', :group_by => "item.id") # => [{"item.id"=>2, "gross"=>314.49, "customers"=> 8}, { ... }]
|
116
127
|
```
|
117
128
|
|
118
129
|
Many of there queries can be performed with group by, filters, series and intervals. The API response for these is converted directly into Ruby Hash or Array.
|
@@ -159,6 +170,9 @@ To track email opens, simply add an image to your email template that points to
|
|
159
170
|
|
160
171
|
### Changelog
|
161
172
|
|
173
|
+
##### 0.6.1
|
174
|
+
+ Improved logging and exception handling.
|
175
|
+
|
162
176
|
##### 0.6.0
|
163
177
|
+ Added querying capabilities. A big thanks to [ifeelgoods](http://www.ifeelgoods.com/) for contributing!
|
164
178
|
|
@@ -185,3 +199,10 @@ at [users.keen.io](http://users.keen.io). We'd love to hear your feedback and id
|
|
185
199
|
### Contributing
|
186
200
|
keen-gem is an open source project and we welcome your contributions.
|
187
201
|
Fire away with issues and pull requests!
|
202
|
+
|
203
|
+
### Community Contributors
|
204
|
+
+ [alexkwolfe](https://github.com/alexkwolfe)
|
205
|
+
+ [peteygao](https://github.com/peteygao)
|
206
|
+
+ [obieq](https://github.com/obieq)
|
207
|
+
|
208
|
+
Thanks everyone, you rock!
|
@@ -32,7 +32,7 @@ module Keen
|
|
32
32
|
:headers => api_headers("sync"),
|
33
33
|
:body => MultiJson.encode(properties))
|
34
34
|
rescue Exception => http_error
|
35
|
-
raise HttpError.new("
|
35
|
+
raise HttpError.new("HTTP publish failure: #{http_error.message}", http_error)
|
36
36
|
end
|
37
37
|
process_response(response.code, response.body.chomp)
|
38
38
|
end
|
@@ -60,8 +60,9 @@ module Keen
|
|
60
60
|
|
61
61
|
if defined?(EM::Synchrony)
|
62
62
|
if http.error
|
63
|
-
|
64
|
-
|
63
|
+
error = HttpError.new("HTTP em-synchrony publish_async error: #{http.error}")
|
64
|
+
Keen.logger.error(error)
|
65
|
+
raise error
|
65
66
|
else
|
66
67
|
process_response(http.response_header.status, http.response.chomp)
|
67
68
|
end
|
@@ -69,14 +70,16 @@ module Keen
|
|
69
70
|
http.callback {
|
70
71
|
begin
|
71
72
|
response = process_response(http.response_header.status, http.response.chomp)
|
72
|
-
deferrable.succeed(response)
|
73
73
|
rescue Exception => e
|
74
|
+
Keen.logger.error(e)
|
74
75
|
deferrable.fail(e)
|
75
76
|
end
|
77
|
+
deferrable.succeed(response) if response
|
76
78
|
}
|
77
79
|
http.errback {
|
78
|
-
|
79
|
-
|
80
|
+
error = Error.new("HTTP publish_async failure: #{http.error}")
|
81
|
+
Keen.logger.error(error)
|
82
|
+
deferrable.fail(error)
|
80
83
|
}
|
81
84
|
deferrable
|
82
85
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
|
1
2
|
module Keen
|
2
3
|
class Client
|
3
4
|
module QueryingMethods
|
@@ -141,6 +142,23 @@ module Keen
|
|
141
142
|
query(__method__, nil, params)
|
142
143
|
end
|
143
144
|
|
145
|
+
# Runs a multi-analysis query
|
146
|
+
# See detailed documentation here:
|
147
|
+
# https://keen.io/docs/data-analysis/multi-analysis/
|
148
|
+
#
|
149
|
+
# NOTE: why isn't multi-analysis listed in the
|
150
|
+
# API Technical Reference?
|
151
|
+
#
|
152
|
+
# @param event_collection
|
153
|
+
# @param params [Hash]
|
154
|
+
# analyses [Hash] (required)
|
155
|
+
# label (required)
|
156
|
+
# analysis_type (required)
|
157
|
+
# target_property (optional)
|
158
|
+
def multi_analysis(event_collection, params)
|
159
|
+
query(__method__, event_collection, params)
|
160
|
+
end
|
161
|
+
|
144
162
|
private
|
145
163
|
|
146
164
|
def query(query_name, event_collection, params)
|
@@ -177,6 +195,10 @@ module Keen
|
|
177
195
|
params[:steps] = MultiJson.encode(params[:steps])
|
178
196
|
end
|
179
197
|
|
198
|
+
if params.key?(:analyses)
|
199
|
+
params[:analyses] = MultiJson.encode(params[:analyses])
|
200
|
+
end
|
201
|
+
|
180
202
|
if params.key?(:timeframe) && params[:timeframe].is_a?(Hash)
|
181
203
|
params[:timeframe] = MultiJson.encode(params[:timeframe])
|
182
204
|
end
|
@@ -196,4 +218,3 @@ module Keen
|
|
196
218
|
end
|
197
219
|
end
|
198
220
|
end
|
199
|
-
|
data/lib/keen/client.rb
CHANGED
@@ -53,18 +53,22 @@ module Keen
|
|
53
53
|
private
|
54
54
|
|
55
55
|
def process_response(status_code, response_body)
|
56
|
-
body = MultiJson.decode(response_body)
|
57
56
|
case status_code.to_i
|
58
57
|
when 200..201
|
59
|
-
|
58
|
+
begin
|
59
|
+
return MultiJson.decode(response_body)
|
60
|
+
rescue
|
61
|
+
Keen.logger.warn("Invalid JSON for response code #{status_code}: #{response_body}")
|
62
|
+
return {}
|
63
|
+
end
|
60
64
|
when 400
|
61
|
-
raise BadRequestError.new(
|
65
|
+
raise BadRequestError.new(response_body)
|
62
66
|
when 401
|
63
|
-
raise AuthenticationError.new(
|
67
|
+
raise AuthenticationError.new(response_body)
|
64
68
|
when 404
|
65
|
-
raise NotFoundError.new(
|
69
|
+
raise NotFoundError.new(response_body)
|
66
70
|
else
|
67
|
-
raise HttpError.new(
|
71
|
+
raise HttpError.new(response_body)
|
68
72
|
end
|
69
73
|
end
|
70
74
|
|
data/lib/keen/version.rb
CHANGED
data/lib/keen.rb
CHANGED
@@ -50,7 +50,7 @@ describe Keen::Client::PublishingMethods do
|
|
50
50
|
|
51
51
|
e.class.should == Keen::HttpError
|
52
52
|
e.original_error.class.should == Timeout::Error
|
53
|
-
e.message.should == "
|
53
|
+
e.message.should == "Keen IO Exception: HTTP publish failure: execution expired"
|
54
54
|
end
|
55
55
|
|
56
56
|
it "should raise an exception if client has no project_id" do
|
@@ -87,7 +87,7 @@ describe Keen::Client::PublishingMethods do
|
|
87
87
|
EM.run {
|
88
88
|
client.publish_async("foo bar", event_properties).callback {
|
89
89
|
begin
|
90
|
-
|
90
|
+
expect_keen_post(api_event_resource_url("foo%20bar"), event_properties, "async")
|
91
91
|
ensure
|
92
92
|
EM.stop
|
93
93
|
end
|
@@ -127,13 +127,28 @@ describe Keen::Client::PublishingMethods do
|
|
127
127
|
client.publish_async(collection, event_properties).errback { |error|
|
128
128
|
begin
|
129
129
|
error.should_not be_nil
|
130
|
-
error.message.should == "
|
130
|
+
error.message.should == "Keen IO Exception: HTTP publish_async failure: WebMock timeout error"
|
131
131
|
ensure
|
132
132
|
EM.stop
|
133
133
|
end
|
134
134
|
}
|
135
135
|
}
|
136
136
|
end
|
137
|
+
|
138
|
+
it "should not trap exceptions in the client callback" do
|
139
|
+
stub_keen_post(api_event_resource_url("foo%20bar"), 201, api_success)
|
140
|
+
expect {
|
141
|
+
EM.run {
|
142
|
+
client.publish_async("foo bar", event_properties).callback {
|
143
|
+
begin
|
144
|
+
blowup
|
145
|
+
ensure
|
146
|
+
EM.stop
|
147
|
+
end
|
148
|
+
}
|
149
|
+
}
|
150
|
+
}.to raise_error
|
151
|
+
end
|
137
152
|
end
|
138
153
|
end
|
139
154
|
end
|
@@ -15,7 +15,7 @@ describe Keen::Client do
|
|
15
15
|
describe "querying names" do
|
16
16
|
let(:params) { { :event_collection => "signups" } }
|
17
17
|
|
18
|
-
["minimum", "maximum", "sum", "average", "count", "count_unique", "select_unique", "extraction"].each do |query_name|
|
18
|
+
["minimum", "maximum", "sum", "average", "count", "count_unique", "select_unique", "extraction", "multi_analysis"].each do |query_name|
|
19
19
|
it "should call keen query passing the query name" do
|
20
20
|
client.should_receive(:query).with(query_name.to_sym, event_collection, params)
|
21
21
|
client.send(query_name, event_collection, params)
|
data/spec/keen/client_spec.rb
CHANGED
@@ -25,34 +25,43 @@ describe Keen::Client do
|
|
25
25
|
|
26
26
|
describe "process_response" do
|
27
27
|
let (:body) { "{ \"wazzup\": 1 }" }
|
28
|
+
let (:exception_body) { "Keen IO Exception: { \"wazzup\": 1 }" }
|
28
29
|
let (:process_response) { client.method(:process_response) }
|
29
30
|
|
30
|
-
it "should
|
31
|
+
it "should return encoded json for a 200" do
|
31
32
|
process_response.call(200, body).should == { "wazzup" => 1 }
|
32
33
|
end
|
33
34
|
|
35
|
+
it "should return encoded json for a 201" do
|
36
|
+
process_response.call(201, body).should == { "wazzup" => 1 }
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should return empty for bad json on a 200/201" do
|
40
|
+
process_response.call(200, "invalid json").should == {}
|
41
|
+
end
|
42
|
+
|
34
43
|
it "should raise a bad request error for a 400" do
|
35
44
|
expect {
|
36
45
|
process_response.call(400, body)
|
37
|
-
}.to raise_error(Keen::BadRequestError)
|
46
|
+
}.to raise_error(Keen::BadRequestError, exception_body)
|
38
47
|
end
|
39
48
|
|
40
49
|
it "should raise a authentication error for a 401" do
|
41
50
|
expect {
|
42
51
|
process_response.call(401, body)
|
43
|
-
}.to raise_error(Keen::AuthenticationError)
|
52
|
+
}.to raise_error(Keen::AuthenticationError, exception_body)
|
44
53
|
end
|
45
54
|
|
46
55
|
it "should raise a not found error for a 404" do
|
47
56
|
expect {
|
48
57
|
process_response.call(404, body)
|
49
|
-
}.to raise_error(Keen::NotFoundError)
|
58
|
+
}.to raise_error(Keen::NotFoundError, exception_body)
|
50
59
|
end
|
51
60
|
|
52
61
|
it "should raise an http error otherwise" do
|
53
62
|
expect {
|
54
63
|
process_response.call(420, body)
|
55
|
-
}.to raise_error(Keen::HttpError)
|
64
|
+
}.to raise_error(Keen::HttpError, exception_body)
|
56
65
|
end
|
57
66
|
end
|
58
67
|
end
|
@@ -41,7 +41,7 @@ describe Keen::HTTP::Async do
|
|
41
41
|
e = exception
|
42
42
|
end
|
43
43
|
e.class.should == Keen::HttpError
|
44
|
-
e.message.should == "
|
44
|
+
e.message.should == "Keen IO Exception: HTTP em-synchrony publish_async error: WebMock timeout error"
|
45
45
|
EM.stop
|
46
46
|
}
|
47
47
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: keen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-04-
|
13
|
+
date: 2013-04-24 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: multi_json
|