keen 0.6.0 → 0.6.1
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/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
|