keen 0.4.3 → 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +14 -6
- data/keen.gemspec +1 -1
- data/lib/keen/client.rb +40 -18
- data/lib/keen/version.rb +1 -1
- data/spec/integration/api_spec.rb +10 -10
- data/spec/keen/client_spec.rb +60 -8
- data/spec/spec_helper.rb +8 -2
- data/spec/synchrony/synchrony_spec.rb +1 -1
- metadata +3 -3
data/README.md
CHANGED
@@ -89,8 +89,7 @@ To configure keen-gem credentials in code, do as follows:
|
|
89
89
|
|
90
90
|
You can also configure individual client instances as follows:
|
91
91
|
|
92
|
-
keen = Keen::Client.new(:project_id => 'your-project-id',
|
93
|
-
:api_key => 'your-api-key')
|
92
|
+
keen = Keen::Client.new(:project_id => 'your-project-id', :api_key => 'your-api-key')
|
94
93
|
|
95
94
|
#### em-synchrony
|
96
95
|
keen-gem can be used with [em-synchrony](https://github.com/igrigorik/em-synchrony).
|
@@ -106,15 +105,24 @@ This is useful for situations like tracking email opens using [image beacons](ht
|
|
106
105
|
In this situation, the JSON event data is passed by encoding it base-64 and adding it as a request parameter called `data`.
|
107
106
|
The `beacon_url` method found on the `Keen::Client` does this for you. Here's an example:
|
108
107
|
|
109
|
-
keen = Keen::Client.new(:project_id => '12345',
|
110
|
-
:api_key => 'abcde')
|
108
|
+
keen = Keen::Client.new(:project_id => '12345', :api_key => 'abcde')
|
111
109
|
|
112
110
|
keen.beacon_url("sign_ups", :recipient => "foo@foo.com")
|
113
|
-
# => "https://api.keen.io/3.0/projects/
|
114
|
-
email_opens?api_key=f806128f31c349fda124b62d1f4cf4b2&data=eyJyZWNpcGllbnQiOiJmb29AZm9vLmNvbSJ9"
|
111
|
+
# => "https://api.keen.io/3.0/projects/12345/events/email_opens?api_key=abcde&data=eyJyZWNpcGllbnQiOiJmb29AZm9vLmNvbSJ9"
|
115
112
|
|
116
113
|
To track email opens, simply add an image to your email template that points to this URL.
|
117
114
|
|
115
|
+
### Changelog
|
116
|
+
|
117
|
+
##### 0.4.4
|
118
|
+
+ Event collections are URI escaped to account for spaces.
|
119
|
+
+ User agent of API calls made more granular to aid in support cases.
|
120
|
+
+ Throw arguments error for nil event_collection and properties arguments.
|
121
|
+
|
122
|
+
##### 0.4.3
|
123
|
+
+ Added beacon_url support
|
124
|
+
+ Add support for using em-synchrony with asynchronous calls
|
125
|
+
|
118
126
|
### Questions & Support
|
119
127
|
|
120
128
|
If you have any questions, bugs, or suggestions, please
|
data/keen.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.email = "josh@keen.io"
|
11
11
|
s.homepage = "https://github.com/keenlabs/keen-gem"
|
12
12
|
s.summary = "Keen IO API Client"
|
13
|
-
s.description = "
|
13
|
+
s.description = "Send events and build analytics features into your Ruby applications."
|
14
14
|
|
15
15
|
s.add_dependency "multi_json", "~> 1.0"
|
16
16
|
s.add_dependency "jruby-openssl" if defined?(JRUBY_VERSION)
|
data/lib/keen/client.rb
CHANGED
@@ -3,6 +3,7 @@ require 'keen/version'
|
|
3
3
|
require 'openssl'
|
4
4
|
require 'multi_json'
|
5
5
|
require 'base64'
|
6
|
+
require 'uri'
|
6
7
|
|
7
8
|
module Keen
|
8
9
|
class Client
|
@@ -18,16 +19,21 @@ module Keen
|
|
18
19
|
:verify_depth => 5,
|
19
20
|
:ca_file => File.expand_path("../../../config/cacert.pem", __FILE__) },
|
20
21
|
:api_async_http_options => {},
|
21
|
-
:api_headers => {
|
22
|
-
"
|
23
|
-
|
22
|
+
:api_headers => lambda { |sync_or_async|
|
23
|
+
user_agent = "keen-gem, v#{Keen::VERSION}, #{sync_or_async}"
|
24
|
+
user_agent += ", #{RUBY_VERSION}, #{RUBY_PLATFORM}, #{RUBY_PATCHLEVEL}"
|
25
|
+
if defined?(RUBY_ENGINE)
|
26
|
+
user_agent += ", #{RUBY_ENGINE}"
|
27
|
+
end
|
28
|
+
{ "Content-Type" => "application/json",
|
29
|
+
"User-Agent" => user_agent }
|
24
30
|
}
|
25
31
|
}
|
26
32
|
|
27
|
-
def beacon_url(
|
33
|
+
def beacon_url(event_collection, properties)
|
28
34
|
json = MultiJson.encode(properties)
|
29
35
|
data = [json].pack("m0").tr("+/", "-_").gsub("\n", "")
|
30
|
-
"https://#{api_host}
|
36
|
+
"https://#{api_host}#{api_path(event_collection)}?api_key=#{@api_key}&data=#{data}"
|
31
37
|
end
|
32
38
|
|
33
39
|
def initialize(*args)
|
@@ -44,13 +50,15 @@ module Keen
|
|
44
50
|
:project_id, :api_key)
|
45
51
|
end
|
46
52
|
|
47
|
-
def publish(
|
53
|
+
def publish(event_collection, properties)
|
48
54
|
check_configuration!
|
55
|
+
check_event_data!(event_collection, properties)
|
56
|
+
|
49
57
|
begin
|
50
58
|
response = Keen::HTTP::Sync.new(
|
51
59
|
api_host, api_port, api_sync_http_options).post(
|
52
|
-
:path => api_path(
|
53
|
-
:headers => api_headers_with_auth,
|
60
|
+
:path => api_path(event_collection),
|
61
|
+
:headers => api_headers_with_auth("sync"),
|
54
62
|
:body => MultiJson.encode(properties))
|
55
63
|
rescue Exception => http_error
|
56
64
|
raise HttpError.new("Couldn't connect to Keen IO: #{http_error.message}", http_error)
|
@@ -58,15 +66,16 @@ module Keen
|
|
58
66
|
process_response(response.code, response.body.chomp)
|
59
67
|
end
|
60
68
|
|
61
|
-
def publish_async(
|
69
|
+
def publish_async(event_collection, properties)
|
62
70
|
check_configuration!
|
71
|
+
check_event_data!(event_collection, properties)
|
63
72
|
|
64
73
|
deferrable = EventMachine::DefaultDeferrable.new
|
65
74
|
|
66
75
|
http_client = Keen::HTTP::Async.new(api_host, api_port, api_async_http_options)
|
67
76
|
http = http_client.post({
|
68
|
-
:path => api_path(
|
69
|
-
:headers => api_headers_with_auth,
|
77
|
+
:path => api_path(event_collection),
|
78
|
+
:headers => api_headers_with_auth("async"),
|
70
79
|
:body => MultiJson.encode(properties)
|
71
80
|
})
|
72
81
|
|
@@ -95,8 +104,8 @@ module Keen
|
|
95
104
|
end
|
96
105
|
|
97
106
|
# deprecated
|
98
|
-
def add_event(
|
99
|
-
self.publish(
|
107
|
+
def add_event(event_collection, properties, options={})
|
108
|
+
self.publish(event_collection, properties, options)
|
100
109
|
end
|
101
110
|
|
102
111
|
private
|
@@ -117,12 +126,12 @@ module Keen
|
|
117
126
|
end
|
118
127
|
end
|
119
128
|
|
120
|
-
def api_path(
|
121
|
-
"/#{api_version}/projects/#{project_id}/events/#{
|
129
|
+
def api_path(event_collection)
|
130
|
+
"/#{api_version}/projects/#{project_id}/events/#{URI.escape(event_collection)}"
|
122
131
|
end
|
123
132
|
|
124
|
-
def api_headers_with_auth
|
125
|
-
api_headers.merge("Authorization" => api_key)
|
133
|
+
def api_headers_with_auth(sync_or_async)
|
134
|
+
api_headers(sync_or_async).merge("Authorization" => api_key)
|
126
135
|
end
|
127
136
|
|
128
137
|
def check_configuration!
|
@@ -130,8 +139,21 @@ module Keen
|
|
130
139
|
raise ConfigurationError, "API Key must be set" unless api_key
|
131
140
|
end
|
132
141
|
|
142
|
+
def check_event_data!(event_collection, properties)
|
143
|
+
raise ArgumentError, "Event collection can not be nil" unless event_collection
|
144
|
+
raise ArgumentError, "Event properties can not be nil" unless properties
|
145
|
+
end
|
146
|
+
|
133
147
|
def method_missing(_method, *args, &block)
|
134
|
-
CONFIG[_method.to_sym]
|
148
|
+
if config = CONFIG[_method.to_sym]
|
149
|
+
if config.is_a?(Proc)
|
150
|
+
config.call(*args)
|
151
|
+
else
|
152
|
+
config
|
153
|
+
end
|
154
|
+
else
|
155
|
+
super
|
156
|
+
end
|
135
157
|
end
|
136
158
|
end
|
137
159
|
end
|
data/lib/keen/version.rb
CHANGED
@@ -32,16 +32,8 @@ describe "Keen IO API" do
|
|
32
32
|
}.to raise_error(Keen::AuthenticationError)
|
33
33
|
end
|
34
34
|
|
35
|
-
it "should
|
36
|
-
|
37
|
-
Keen.publish(collection, nil)
|
38
|
-
}.to raise_error(Keen::BadRequestError)
|
39
|
-
end
|
40
|
-
|
41
|
-
it "should return not found for an invalid collection name" do
|
42
|
-
expect {
|
43
|
-
Keen.publish(nil, event_properties)
|
44
|
-
}.to raise_error(Keen::NotFoundError)
|
35
|
+
it "should success if a non-url-safe event collection is specified" do
|
36
|
+
Keen.publish("infinite possibilities", event_properties).should == api_success
|
45
37
|
end
|
46
38
|
end
|
47
39
|
|
@@ -58,6 +50,14 @@ describe "Keen IO API" do
|
|
58
50
|
}
|
59
51
|
end
|
60
52
|
|
53
|
+
it "should publish to non-url-safe collections" do
|
54
|
+
EM.run {
|
55
|
+
Keen.publish_async("foo bar", event_properties).callback { |response|
|
56
|
+
response.should == api_success
|
57
|
+
EM.stop
|
58
|
+
}
|
59
|
+
}
|
60
|
+
end
|
61
61
|
end
|
62
62
|
end
|
63
63
|
end
|
data/spec/keen/client_spec.rb
CHANGED
@@ -56,7 +56,7 @@ describe Keen::Client do
|
|
56
56
|
it "should post using the collection and properties" do
|
57
57
|
stub_api(api_url(collection), 201, "")
|
58
58
|
@client.publish(collection, event_properties)
|
59
|
-
expect_post(api_url(collection), event_properties, api_key)
|
59
|
+
expect_post(api_url(collection), event_properties, api_key, "sync")
|
60
60
|
end
|
61
61
|
|
62
62
|
it "should return the proper response" do
|
@@ -65,6 +65,24 @@ describe Keen::Client do
|
|
65
65
|
@client.publish(collection, event_properties).should == api_response
|
66
66
|
end
|
67
67
|
|
68
|
+
it "should raise an argument error if no event collection is specified" do
|
69
|
+
expect {
|
70
|
+
@client.publish(nil, {})
|
71
|
+
}.to raise_error(ArgumentError)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should raise an argument error if no properties are specified" do
|
75
|
+
expect {
|
76
|
+
@client.publish(collection, nil)
|
77
|
+
}.to raise_error(ArgumentError)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should url encode the event collection" do
|
81
|
+
stub_api(api_url("foo%20bar"), 201, "")
|
82
|
+
@client.publish("foo bar", event_properties)
|
83
|
+
expect_post(api_url("foo%20bar"), event_properties, api_key, "sync")
|
84
|
+
end
|
85
|
+
|
68
86
|
it "should wrap exceptions" do
|
69
87
|
stub_request(:post, api_url(collection)).to_timeout
|
70
88
|
e = nil
|
@@ -94,19 +112,50 @@ describe Keen::Client do
|
|
94
112
|
stub_api(api_url(collection), 201, api_success)
|
95
113
|
EM.run {
|
96
114
|
@client.publish_async(collection, event_properties).callback {
|
97
|
-
|
98
|
-
|
115
|
+
begin
|
116
|
+
expect_post(api_url(collection), event_properties, api_key, "async")
|
117
|
+
ensure
|
118
|
+
EM.stop
|
119
|
+
end
|
99
120
|
}
|
100
121
|
}
|
101
122
|
end
|
102
123
|
|
124
|
+
it "should uri encode the event collection" do
|
125
|
+
stub_api(api_url("foo%20bar"), 201, api_success)
|
126
|
+
EM.run {
|
127
|
+
@client.publish_async("foo bar", event_properties).callback {
|
128
|
+
begin
|
129
|
+
expect_post(api_url("foo%20bar"), event_properties, api_key, "async")
|
130
|
+
ensure
|
131
|
+
EM.stop
|
132
|
+
end
|
133
|
+
}
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should raise an argument error if no event collection is specified" do
|
138
|
+
expect {
|
139
|
+
@client.publish_async(nil, {})
|
140
|
+
}.to raise_error(ArgumentError)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should raise an argument error if no properties are specified" do
|
144
|
+
expect {
|
145
|
+
@client.publish_async(collection, nil)
|
146
|
+
}.to raise_error(ArgumentError)
|
147
|
+
end
|
148
|
+
|
103
149
|
describe "deferrable callbacks" do
|
104
150
|
it "should trigger callbacks" do
|
105
151
|
stub_api(api_url(collection), 201, api_success)
|
106
152
|
EM.run {
|
107
153
|
@client.publish_async(collection, event_properties).callback { |response|
|
108
|
-
|
109
|
-
|
154
|
+
begin
|
155
|
+
response.should == api_success
|
156
|
+
ensure
|
157
|
+
EM.stop
|
158
|
+
end
|
110
159
|
}
|
111
160
|
}
|
112
161
|
end
|
@@ -115,9 +164,12 @@ describe Keen::Client do
|
|
115
164
|
stub_request(:post, api_url(collection)).to_timeout
|
116
165
|
EM.run {
|
117
166
|
@client.publish_async(collection, event_properties).errback { |error|
|
118
|
-
|
119
|
-
|
120
|
-
|
167
|
+
begin
|
168
|
+
error.should_not be_nil
|
169
|
+
error.message.should == "Couldn't connect to Keen IO: WebMock timeout error"
|
170
|
+
ensure
|
171
|
+
EM.stop
|
172
|
+
end
|
121
173
|
}
|
122
174
|
}
|
123
175
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -17,11 +17,17 @@ module Keen::SpecHelpers
|
|
17
17
|
:body => MultiJson.encode(json_body))
|
18
18
|
end
|
19
19
|
|
20
|
-
def expect_post(url, event_properties, api_key)
|
20
|
+
def expect_post(url, event_properties, api_key, sync_or_async_ua)
|
21
|
+
user_agent = "keen-gem, v#{Keen::VERSION}, #{sync_or_async_ua}"
|
22
|
+
user_agent += ", #{RUBY_VERSION}, #{RUBY_PLATFORM}, #{RUBY_PATCHLEVEL}"
|
23
|
+
if defined?(RUBY_ENGINE)
|
24
|
+
user_agent += ", #{RUBY_ENGINE}"
|
25
|
+
end
|
26
|
+
|
21
27
|
WebMock.should have_requested(:post, url).with(
|
22
28
|
:body => MultiJson.encode(event_properties),
|
23
29
|
:headers => { "Content-Type" => "application/json",
|
24
|
-
"User-Agent" =>
|
30
|
+
"User-Agent" => user_agent,
|
25
31
|
"Authorization" => api_key })
|
26
32
|
end
|
27
33
|
|
@@ -19,7 +19,7 @@ describe Keen::HTTP::Async do
|
|
19
19
|
stub_api(api_url(collection), 201, api_success)
|
20
20
|
EM.synchrony {
|
21
21
|
@client.publish_async(collection, event_properties)
|
22
|
-
expect_post(api_url(collection), event_properties, api_key)
|
22
|
+
expect_post(api_url(collection), event_properties, api_key, "async")
|
23
23
|
EM.stop
|
24
24
|
}
|
25
25
|
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.4.
|
4
|
+
version: 0.4.4
|
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-01-
|
13
|
+
date: 2013-01-25 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: multi_json
|
@@ -28,7 +28,7 @@ dependencies:
|
|
28
28
|
- - ~>
|
29
29
|
- !ruby/object:Gem::Version
|
30
30
|
version: '1.0'
|
31
|
-
description:
|
31
|
+
description: Send events and build analytics features into your Ruby applications.
|
32
32
|
email: josh@keen.io
|
33
33
|
executables: []
|
34
34
|
extensions: []
|