keen 0.8.0 → 0.8.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.
- 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
|