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 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/50e5ffa6897a2c319b000000/events/ \
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
@@ -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 = "Batch and send events to the Keen IO API. Supports asychronous requests."
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)
@@ -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
- "Content-Type" => "application/json",
23
- "User-Agent" => "keen-gem v#{Keen::VERSION}"
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(event_name, properties)
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}/#{api_version}/projects/#{@project_id}/events/#{event_name}?api_key=#{@api_key}&data=#{data}"
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(event_name, properties)
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(event_name),
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(event_name, properties)
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(event_name),
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(event_name, properties, options={})
99
- self.publish(event_name, properties, options)
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(collection)
121
- "/#{api_version}/projects/#{project_id}/events/#{collection}"
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] || super
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
@@ -1,3 +1,3 @@
1
1
  module Keen
2
- VERSION = "0.4.3"
2
+ VERSION = "0.4.4"
3
3
  end
@@ -32,16 +32,8 @@ describe "Keen IO API" do
32
32
  }.to raise_error(Keen::AuthenticationError)
33
33
  end
34
34
 
35
- it "should raise bad request if no JSON is supplied" do
36
- expect {
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
@@ -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
- expect_post(api_url(collection), event_properties, api_key)
98
- EM.stop
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
- response.should == api_success
109
- EM.stop
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
- error.should_not be_nil
119
- error.message.should == "Couldn't connect to Keen IO: WebMock timeout error"
120
- EM.stop
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
@@ -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" => "keen-gem v#{Keen::VERSION}",
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.3
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-23 00:00:00.000000000 Z
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: Batch and send events to the Keen IO API. Supports asychronous requests.
31
+ description: Send events and build analytics features into your Ruby applications.
32
32
  email: josh@keen.io
33
33
  executables: []
34
34
  extensions: []