keen 0.4.3 → 0.4.4

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.
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: []