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 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=your-project-id
32
+ KEEN_PROJECT_ID=xxxxxxxxxxxxxxxx
33
33
 
34
- When you deploy, make sure your production environment variables are also set. For example,
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 the Ruby gem. Let's assume you've added some events to the "purchases" collection.
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", "event_collection" => "purchases" },
114
- { :actor_property => "username", "event_collection" => "referrals" },
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("Couldn't connect to Keen IO: #{http_error.message}", http_error)
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
- Keen.logger.warn("Couldn't connect to Keen IO: #{http.error}")
64
- raise HttpError.new("Couldn't connect to Keen IO: #{http.error}")
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
- Keen.logger.warn("Couldn't connect to Keen IO: #{http.error}")
79
- deferrable.fail(Error.new("Couldn't connect to Keen IO: #{http.error}"))
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
- return body
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(body)
65
+ raise BadRequestError.new(response_body)
62
66
  when 401
63
- raise AuthenticationError.new(body)
67
+ raise AuthenticationError.new(response_body)
64
68
  when 404
65
- raise NotFoundError.new(body)
69
+ raise NotFoundError.new(response_body)
66
70
  else
67
- raise HttpError.new(body)
71
+ raise HttpError.new(response_body)
68
72
  end
69
73
  end
70
74
 
data/lib/keen/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Keen
2
- VERSION = "0.6.0"
2
+ VERSION = "0.6.1"
3
3
  end
data/lib/keen.rb CHANGED
@@ -10,6 +10,10 @@ module Keen
10
10
  self.original_error = _original_error
11
11
  super(message)
12
12
  end
13
+
14
+ def to_s
15
+ "Keen IO Exception: #{super}"
16
+ end
13
17
  end
14
18
 
15
19
  class ConfigurationError < Error; end
@@ -70,7 +70,7 @@ describe "Keen IO API" do
70
70
  :username => "bob",
71
71
  :price => 30
72
72
  })
73
- sleep(1)
73
+ sleep(5)
74
74
  end
75
75
 
76
76
  it "should return a valid count" do
@@ -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 == "Couldn't connect to Keen IO: execution expired"
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
- expect_post(api_event_resource_url("foo%20bar"), event_properties, "async")
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 == "Couldn't connect to Keen IO: WebMock timeout error"
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)
@@ -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 raise a bad request error for a 400" do
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 == "Couldn't connect to Keen IO: WebMock timeout error"
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.0
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-05 00:00:00.000000000 Z
13
+ date: 2013-04-24 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: multi_json