keen 0.8.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +21 -1
- data/keen.gemspec +1 -3
- data/lib/keen.rb +1 -1
- data/lib/keen/client/publishing_methods.rb +20 -0
- data/lib/keen/version.rb +1 -1
- data/spec/integration/api_spec.rb +48 -12
- data/spec/keen/client/publishing_methods_spec.rb +80 -1
- data/spec/keen/keen_spec.rb +1 -1
- data/spec/synchrony/synchrony_spec.rb +50 -1
- metadata +5 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bfa6893d4994089e26e547221cd8471568335ec6
|
4
|
+
data.tar.gz: c353a3635a5656b6b9e0a4840d1bd88de8beac21
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 75adf0c282766b84c1d7be3fc5dab6567951b36f0d63456c12406a7845ccc401f1fb258b93de6e6e150c4fea2c5349bf50eee4719e8f7eeab40979a7b9cd86db
|
7
|
+
data.tar.gz: d9ed4ec4656056f296bdacab25b8ab980164330f62477c0333a1a3d4c7a0874665cde88e0c9e172cae11c0e1d4b0df323b0a9a5626f657e92681a7577320af70
|
data/README.md
CHANGED
@@ -154,7 +154,7 @@ Keen.delete(:signups) # => true
|
|
154
154
|
|
155
155
|
# Or just delete an event corresponding to a particular user
|
156
156
|
Keen.delete(:signups, filters: [{
|
157
|
-
:property_name => 'username',
|
157
|
+
:property_name => 'username', :operator => 'eq', :property_value => "Bob"
|
158
158
|
}]) # => true
|
159
159
|
```
|
160
160
|
|
@@ -180,6 +180,23 @@ Keen.publish_batch(
|
|
180
180
|
This call would publish 2 `signups` events and 2 `purchases` events - all in just one API call.
|
181
181
|
Batch publishing is ideal for loading historical events into Keen IO.
|
182
182
|
|
183
|
+
#### Asynchronous batch publishing
|
184
|
+
|
185
|
+
Ensuring the above guidance is followed for asynchronous publishing, batch publishing logic can used asynchronously with `publish_batch_async`:
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
Keen.publish_batch_async(
|
189
|
+
:signups => [
|
190
|
+
{ :name => "Bob" },
|
191
|
+
{ :name => "Mary" }
|
192
|
+
],
|
193
|
+
:purchases => [
|
194
|
+
{ :price => 10 },
|
195
|
+
{ :price => 20 }
|
196
|
+
]
|
197
|
+
)
|
198
|
+
```
|
199
|
+
|
183
200
|
#### Configurable and per-client authentication
|
184
201
|
|
185
202
|
To configure keen-gem in code, do as follows:
|
@@ -266,6 +283,9 @@ EventMachine itself won't do this because it runs in a different thread. Here's
|
|
266
283
|
|
267
284
|
### Changelog
|
268
285
|
|
286
|
+
##### 0.8.1
|
287
|
+
+ Add support for asynchronous batch publishing
|
288
|
+
|
269
289
|
##### 0.8.0
|
270
290
|
+ **UPGRADE WARNING** Do you use spaces in collection names? Or other special characters? Read [this post](https://groups.google.com/forum/?fromgroups#!topic/keen-io-devs/VtCgPuNKrgY) from the mailing list to make sure your collection names don't change.
|
271
291
|
+ Add support for generating [scoped keys](https://keen.io/docs/security/#scoped-key).
|
data/keen.gemspec
CHANGED
@@ -12,9 +12,7 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.description = "Send events and build analytics features into your Ruby applications."
|
13
13
|
s.license = "MIT"
|
14
14
|
|
15
|
-
s.
|
16
|
-
|
17
|
-
s.add_dependency "multi_json", "~> 1.0"
|
15
|
+
s.add_dependency "multi_json", "~> 1.3"
|
18
16
|
s.add_dependency "addressable", "~> 2.3.5"
|
19
17
|
s.add_dependency "jruby-openssl" if defined?(JRUBY_VERSION)
|
20
18
|
|
data/lib/keen.rb
CHANGED
@@ -39,7 +39,7 @@ module Keen
|
|
39
39
|
|
40
40
|
def_delegators :default_client,
|
41
41
|
:publish, :publish_async, :publish_batch,
|
42
|
-
:beacon_url, :redirect_url
|
42
|
+
:publish_batch_async, :beacon_url, :redirect_url
|
43
43
|
|
44
44
|
def_delegators :default_client,
|
45
45
|
:count, :count_unique, :minimum, :maximum,
|
@@ -77,6 +77,26 @@ module Keen
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
+
def publish_batch_async(events)
|
81
|
+
ensure_project_id!
|
82
|
+
ensure_write_key!
|
83
|
+
|
84
|
+
http_client = Keen::HTTP::Async.new(
|
85
|
+
self.api_url,
|
86
|
+
{:proxy_url => self.proxy_url, :proxy_type => self.proxy_type})
|
87
|
+
|
88
|
+
http = http_client.post(
|
89
|
+
:path => api_events_resource_path,
|
90
|
+
:headers => api_headers(self.write_key, "async"),
|
91
|
+
:body => MultiJson.encode(events)
|
92
|
+
)
|
93
|
+
if defined?(EM::Synchrony)
|
94
|
+
process_with_synchrony(http)
|
95
|
+
else
|
96
|
+
process_with_callbacks(http)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
80
100
|
# Returns an encoded URL that will record an event. Useful in email situations.
|
81
101
|
# See detailed documentation here
|
82
102
|
# https://keen.io/docs/api/reference/#event-collection-resource
|
data/lib/keen/version.rb
CHANGED
@@ -4,6 +4,15 @@ describe "Keen IO API" do
|
|
4
4
|
let(:project_id) { ENV['KEEN_PROJECT_ID'] }
|
5
5
|
let(:write_key) { ENV['KEEN_WRITE_KEY'] }
|
6
6
|
|
7
|
+
def wait_for_count(event_collection, count)
|
8
|
+
attempts = 0
|
9
|
+
while attempts < 30
|
10
|
+
break if Keen.count(event_collection) == count
|
11
|
+
attempts += 1
|
12
|
+
sleep(1)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
7
16
|
describe "publishing" do
|
8
17
|
let(:collection) { "User posts.new" }
|
9
18
|
let(:event_properties) { { "name" => "Bob" } }
|
@@ -85,6 +94,35 @@ describe "Keen IO API" do
|
|
85
94
|
end
|
86
95
|
end
|
87
96
|
|
97
|
+
describe "batch_async" do
|
98
|
+
# no TLS support in EventMachine on jRuby
|
99
|
+
unless defined?(JRUBY_VERSION)
|
100
|
+
let(:api_success) { {"batch_purchases"=>[{"success"=>true}, {"success"=>true}], "batch_signups"=>[{"success"=>true}, {"success"=>true}]} }
|
101
|
+
it "should publish the event and trigger callbacks" do
|
102
|
+
EM.run {
|
103
|
+
Keen.publish_batch_async(
|
104
|
+
:batch_signups => [
|
105
|
+
{ :name => "bob" },
|
106
|
+
{ :name => "ted" }
|
107
|
+
],
|
108
|
+
:batch_purchases => [
|
109
|
+
{ :price => 30 },
|
110
|
+
{ :price => 40 }
|
111
|
+
]).callback { |response|
|
112
|
+
begin
|
113
|
+
response.should == api_success
|
114
|
+
ensure
|
115
|
+
EM.stop
|
116
|
+
end
|
117
|
+
}.errback { |error|
|
118
|
+
EM.stop
|
119
|
+
fail error
|
120
|
+
}
|
121
|
+
}
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
88
126
|
describe "queries" do
|
89
127
|
let(:read_key) { ENV['KEEN_READ_KEY'] }
|
90
128
|
let(:event_collection) { @event_collection }
|
@@ -106,26 +144,25 @@ describe "Keen IO API" do
|
|
106
144
|
:username => "bob",
|
107
145
|
:price => 30
|
108
146
|
})
|
109
|
-
sleep(5)
|
110
|
-
end
|
111
147
|
|
112
|
-
|
113
|
-
|
148
|
+
# poll the count to know when to continue
|
149
|
+
wait_for_count(@event_collection, 2)
|
150
|
+
wait_for_count(@returns_event_collection, 1)
|
114
151
|
end
|
115
152
|
|
116
153
|
it "should return a valid count_unique" do
|
117
154
|
Keen.count_unique(event_collection, :target_property => "price").should == 2
|
118
155
|
end
|
119
|
-
|
120
|
-
it "should return a valid count with group_by" do
|
156
|
+
|
157
|
+
it "should return a valid count with group_by" do
|
121
158
|
response = Keen.average(event_collection, :group_by => "username", :target_property => "price")
|
122
159
|
bobs_response = response.select { |result| result["username"] == "bob" }.first
|
123
160
|
bobs_response["result"].should == 10
|
124
161
|
teds_response = response.select { |result| result["username"] == "ted" }.first
|
125
|
-
teds_response["result"].should == 20
|
162
|
+
teds_response["result"].should == 20
|
126
163
|
end
|
127
|
-
|
128
|
-
it "should return a valid count with multi-group_by" do
|
164
|
+
|
165
|
+
it "should return a valid count with multi-group_by" do
|
129
166
|
response = Keen.average(event_collection, :group_by => ["username", "price"], :target_property => "price")
|
130
167
|
bobs_response = response.select { |result| result["username"] == "bob" }.first
|
131
168
|
bobs_response["result"].should == 10
|
@@ -189,15 +226,14 @@ describe "Keen IO API" do
|
|
189
226
|
before do
|
190
227
|
Keen.publish(event_collection, :delete => "me")
|
191
228
|
Keen.publish(event_collection, :delete => "you")
|
192
|
-
|
229
|
+
wait_for_count(event_collection, 2)
|
193
230
|
end
|
194
231
|
|
195
232
|
it "should delete the event" do
|
196
|
-
Keen.count(event_collection).should == 2
|
197
233
|
Keen.delete(event_collection, :filters => [
|
198
234
|
{ :property_name => "delete", :operator => "eq", :property_value => "me" }
|
199
235
|
])
|
200
|
-
|
236
|
+
wait_for_count(event_collection, 1)
|
201
237
|
results = Keen.extraction(event_collection)
|
202
238
|
results.length.should == 1
|
203
239
|
results.first["delete"].should == "you"
|
@@ -130,7 +130,7 @@ describe Keen::Client::PublishingMethods do
|
|
130
130
|
ensure
|
131
131
|
EM.stop
|
132
132
|
end
|
133
|
-
}.errback {
|
133
|
+
}.errback {
|
134
134
|
EM.stop
|
135
135
|
fail
|
136
136
|
}
|
@@ -211,6 +211,84 @@ describe Keen::Client::PublishingMethods do
|
|
211
211
|
end
|
212
212
|
end
|
213
213
|
|
214
|
+
describe "publish_batch_async" do
|
215
|
+
unless defined?(JRUBY_VERSION)
|
216
|
+
let(:multi) { EventMachine::MultiRequest.new }
|
217
|
+
let(:events) {
|
218
|
+
{
|
219
|
+
:purchases => [
|
220
|
+
{ :price => 10 },
|
221
|
+
{ :price => 11 }
|
222
|
+
],
|
223
|
+
:signups => [
|
224
|
+
{ :name => "bob" },
|
225
|
+
{ :name => "bill" }
|
226
|
+
]
|
227
|
+
}
|
228
|
+
}
|
229
|
+
|
230
|
+
it "should raise an exception if client has no project_id" do
|
231
|
+
expect {
|
232
|
+
Keen::Client.new(
|
233
|
+
:write_key => "abcde"
|
234
|
+
).publish_batch_async(events)
|
235
|
+
}.to raise_error(Keen::ConfigurationError, "Keen IO Exception: Project ID must be set")
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should raise an exception if client has no write_key" do
|
239
|
+
expect {
|
240
|
+
Keen::Client.new(
|
241
|
+
:project_id => "12345"
|
242
|
+
).publish_batch_async(events)
|
243
|
+
}.to raise_error(Keen::ConfigurationError, "Keen IO Exception: Write Key must be set for sending events")
|
244
|
+
end
|
245
|
+
|
246
|
+
describe "deferrable callbacks" do
|
247
|
+
it "should trigger callbacks" do
|
248
|
+
stub_keen_post(api_event_resource_url(api_url), 201, api_success)
|
249
|
+
EM.run {
|
250
|
+
client.publish_batch_async(events).callback { |response|
|
251
|
+
begin
|
252
|
+
response.should == api_success
|
253
|
+
ensure
|
254
|
+
EM.stop
|
255
|
+
end
|
256
|
+
}
|
257
|
+
}
|
258
|
+
end
|
259
|
+
|
260
|
+
it "should trigger errbacks" do
|
261
|
+
stub_request(:post, api_event_resource_url(api_url)).to_timeout
|
262
|
+
EM.run {
|
263
|
+
client.publish_batch_async(events).errback { |error|
|
264
|
+
begin
|
265
|
+
error.should_not be_nil
|
266
|
+
error.message.should == "Keen IO Exception: HTTP publish_async failure: WebMock timeout error"
|
267
|
+
ensure
|
268
|
+
EM.stop
|
269
|
+
end
|
270
|
+
}
|
271
|
+
}
|
272
|
+
end
|
273
|
+
|
274
|
+
it "should not trap exceptions in the client callback" do
|
275
|
+
stub_keen_post(api_event_resource_url(api_url), 201, api_success)
|
276
|
+
expect {
|
277
|
+
EM.run {
|
278
|
+
client.publish_batch_async(events).callback {
|
279
|
+
begin
|
280
|
+
blowup
|
281
|
+
ensure
|
282
|
+
EM.stop
|
283
|
+
end
|
284
|
+
}
|
285
|
+
}
|
286
|
+
}.to raise_error
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
214
292
|
it "should raise an exception if client has no project_id" do
|
215
293
|
expect {
|
216
294
|
Keen::Client.new.publish_async(collection, event_properties)
|
@@ -237,4 +315,5 @@ describe Keen::Client::PublishingMethods do
|
|
237
315
|
"#{api_url}/3.0/projects/12345/events/sign_ups?api_key=#{write_key}&data=eyJuYW1lIjoiQm9iIn0=&redirect=http://keen.io/?foo=bar&bar=baz"
|
238
316
|
end
|
239
317
|
end
|
318
|
+
|
240
319
|
end
|
data/spec/keen/keen_spec.rb
CHANGED
@@ -84,7 +84,7 @@ describe Keen do
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
-
[:publish, :publish_async, :publish_batch].each do |_method|
|
87
|
+
[:publish, :publish_async, :publish_batch, :publish_batch_async].each do |_method|
|
88
88
|
it "should forward the #{_method} method" do
|
89
89
|
@default_client.should_receive(_method).with("users", {})
|
90
90
|
Keen.send(_method, "users", {})
|
@@ -7,6 +7,19 @@ describe Keen::HTTP::Async do
|
|
7
7
|
let(:api_url) { "https://fake.keen.io" }
|
8
8
|
let(:event_properties) { { "name" => "Bob" } }
|
9
9
|
let(:api_success) { { "created" => true } }
|
10
|
+
let(:batch_api_success) { { "created" => true } }
|
11
|
+
let(:events) {
|
12
|
+
{
|
13
|
+
:purchases => [
|
14
|
+
{ :price => 10 },
|
15
|
+
{ :price => 11 }
|
16
|
+
],
|
17
|
+
:signups => [
|
18
|
+
{ :name => "bob" },
|
19
|
+
{ :name => "bill" }
|
20
|
+
]
|
21
|
+
}
|
22
|
+
}
|
10
23
|
|
11
24
|
describe "synchrony" do
|
12
25
|
before do
|
@@ -25,7 +38,7 @@ describe Keen::HTTP::Async do
|
|
25
38
|
}
|
26
39
|
end
|
27
40
|
|
28
|
-
it "should
|
41
|
+
it "should receive the right response 'synchronously'" do
|
29
42
|
stub_keen_post(api_event_collection_resource_url(api_url, collection), 201, api_success)
|
30
43
|
EM.synchrony {
|
31
44
|
@client.publish_async(collection, event_properties).should == api_success
|
@@ -34,6 +47,25 @@ describe Keen::HTTP::Async do
|
|
34
47
|
end
|
35
48
|
end
|
36
49
|
|
50
|
+
describe "batch success" do
|
51
|
+
it "should post the event data" do
|
52
|
+
stub_keen_post(api_event_resource_url(api_url), 201, api_success)
|
53
|
+
EM.synchrony {
|
54
|
+
@client.publish_batch_async(events)
|
55
|
+
expect_keen_post(api_event_resource_url(api_url), events, "async", write_key)
|
56
|
+
EM.stop
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should receive the right response 'synchronously'" do
|
61
|
+
stub_keen_post(api_event_resource_url(api_url), 201, api_success)
|
62
|
+
EM.synchrony {
|
63
|
+
@client.publish_batch_async(events).should == api_success
|
64
|
+
EM.stop
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
37
69
|
describe "failure" do
|
38
70
|
it "should raise an exception" do
|
39
71
|
stub_request(:post, api_event_collection_resource_url(api_url, collection)).to_timeout
|
@@ -50,5 +82,22 @@ describe Keen::HTTP::Async do
|
|
50
82
|
}
|
51
83
|
end
|
52
84
|
end
|
85
|
+
|
86
|
+
describe "batch failure" do
|
87
|
+
it "should raise an exception" do
|
88
|
+
stub_request(:post, api_event_resource_url(api_url)).to_timeout
|
89
|
+
e = nil
|
90
|
+
EM.synchrony {
|
91
|
+
begin
|
92
|
+
@client.publish_batch_async(events).should == api_success
|
93
|
+
rescue Exception => exception
|
94
|
+
e = exception
|
95
|
+
end
|
96
|
+
e.class.should == Keen::HttpError
|
97
|
+
e.message.should == "Keen IO Exception: HTTP em-synchrony publish_async error: WebMock timeout error"
|
98
|
+
EM.stop
|
99
|
+
}
|
100
|
+
end
|
101
|
+
end
|
53
102
|
end
|
54
103
|
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.8.
|
4
|
+
version: 0.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kyle Wild
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2014-
|
13
|
+
date: 2014-04-04 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: multi_json
|
@@ -18,14 +18,14 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: '1.
|
21
|
+
version: '1.3'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
26
|
- - ~>
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
version: '1.
|
28
|
+
version: '1.3'
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: addressable
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
@@ -179,9 +179,7 @@ homepage: https://github.com/keenlabs/keen-gem
|
|
179
179
|
licenses:
|
180
180
|
- MIT
|
181
181
|
metadata: {}
|
182
|
-
post_install_message:
|
183
|
-
Or other special characters? Read https://groups.google.com/forum/?fromgroups#!topic/keen-io-devs/VtCgPuNKrgY
|
184
|
-
from the mailing list to make sure your collection names don''t change!'
|
182
|
+
post_install_message:
|
185
183
|
rdoc_options: []
|
186
184
|
require_paths:
|
187
185
|
- lib
|