keen 0.7.2 → 0.7.3

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/Gemfile CHANGED
@@ -8,6 +8,8 @@ group :development, :test do
8
8
  gem 'em-http-request'
9
9
  gem 'em-synchrony', :require => false
10
10
  gem 'webmock'
11
+ # ensure a reasonable json so multi_json doesn't complain
12
+ gem 'json', '~> 1.7.7'
11
13
  end
12
14
 
13
15
  group :development do
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- keen (0.7.1)
4
+ keen (0.7.3)
5
5
  multi_json (~> 1.0)
6
6
 
7
7
  GEM
@@ -42,6 +42,7 @@ GEM
42
42
  guard (>= 1.8)
43
43
  rspec (~> 2.13)
44
44
  http_parser.rb (0.5.3)
45
+ json (1.7.7)
45
46
  linecache (0.46)
46
47
  rbx-require-relative (> 0.0.4)
47
48
  listen (1.0.3)
@@ -50,7 +51,7 @@ GEM
50
51
  rb-kqueue (>= 0.2)
51
52
  lumberjack (1.0.3)
52
53
  method_source (0.8.1)
53
- multi_json (1.7.2)
54
+ multi_json (1.7.3)
54
55
  pry (0.9.12.1)
55
56
  coderay (~> 1.0.5)
56
57
  method_source (~> 0.8)
@@ -94,6 +95,7 @@ DEPENDENCIES
94
95
  em-synchrony
95
96
  guard
96
97
  guard-rspec
98
+ json (~> 1.7.7)
97
99
  keen!
98
100
  rake
99
101
  rb-fchange
data/README.md CHANGED
@@ -133,6 +133,26 @@ Detailed information on available parameters for each API resource can be found
133
133
 
134
134
  ### Other code examples
135
135
 
136
+ #### Batch publishing
137
+
138
+ The keen-gem supports publishing events in batches via the `publish_batch` method. Here's an example usage:
139
+
140
+ ```ruby
141
+ Keen.publish_batch(
142
+ :signups => [
143
+ { :name => "Bob" },
144
+ { :name => "Mary" }
145
+ ],
146
+ :purchases => [
147
+ { :price => 10 },
148
+ { :price => 20 }
149
+ ]
150
+ )
151
+ ```
152
+
153
+ This call would publish 2 `signups` events and 2 `purchases` events - all in just one API call.
154
+ Batch publishing is ideal for loading historical events into Keen IO.
155
+
136
156
  #### Authentication
137
157
 
138
158
  To configure keen-gem in code, do as follows:
@@ -177,6 +197,10 @@ To track email opens, simply add an image to your email template that points to
177
197
 
178
198
  ### Changelog
179
199
 
200
+ ##### 0.7.3
201
+ + Add batch publishing support
202
+ + Allow event collection names for querying methods to be symbols. Thanks to [cbartlett](https://github.com/cbartlett).
203
+
180
204
  ##### 0.7.2
181
205
  + Fix support for non-https API URL testing
182
206
 
@@ -223,5 +247,6 @@ Fire away with issues and pull requests!
223
247
  + [alexkwolfe](https://github.com/alexkwolfe)
224
248
  + [peteygao](https://github.com/peteygao)
225
249
  + [obieq](https://github.com/obieq)
226
-
250
+ + [cbartlett](https://github.com/cbartlett)
251
+
227
252
  Thanks everyone, you rock!
@@ -25,17 +25,27 @@ module Keen
25
25
  ensure_project_id!
26
26
  ensure_write_key!
27
27
  check_event_data!(event_collection, properties)
28
+ publish_body(
29
+ api_event_collection_resource_path(event_collection),
30
+ MultiJson.encode(properties),
31
+ "publish")
32
+ end
28
33
 
29
- begin
30
- response = Keen::HTTP::Sync.new(
31
- self.api_url).post(
32
- :path => api_event_resource_path(event_collection),
33
- :headers => api_headers(self.write_key, "sync"),
34
- :body => MultiJson.encode(properties))
35
- rescue Exception => http_error
36
- raise HttpError.new("HTTP publish failure: #{http_error.message}", http_error)
37
- end
38
- process_response(response.code, response.body.chomp)
34
+ # Publishes a batch of events
35
+ # See detailed documentation here
36
+ # https://keen.io/docs/api/reference/#post-request-body-example-of-batch-event-posting
37
+ #
38
+ # @param events - a hash where the keys are event collection names
39
+ # and the values are arrays of hashes (event properties)
40
+ #
41
+ # @return the JSON response from the API
42
+ def publish_batch(events)
43
+ ensure_project_id!
44
+ ensure_write_key!
45
+ publish_body(
46
+ api_events_resource_path,
47
+ MultiJson.encode(events),
48
+ "publish")
39
49
  end
40
50
 
41
51
  # Publishes an asynchronous event
@@ -55,7 +65,7 @@ module Keen
55
65
 
56
66
  http_client = Keen::HTTP::Async.new(self.api_url)
57
67
  http = http_client.post(
58
- :path => api_event_resource_path(event_collection),
68
+ :path => api_event_collection_resource_path(event_collection),
59
69
  :headers => api_headers(self.write_key, "async"),
60
70
  :body => MultiJson.encode(properties)
61
71
  )
@@ -98,15 +108,32 @@ module Keen
98
108
  def beacon_url(event_collection, properties)
99
109
  json = MultiJson.encode(properties)
100
110
  data = [json].pack("m0").tr("+/", "-_").gsub("\n", "")
101
- "#{self.api_url}#{api_event_resource_path(event_collection)}?api_key=#{self.write_key}&data=#{data}"
111
+ "#{self.api_url}#{api_event_collection_resource_path(event_collection)}?api_key=#{self.write_key}&data=#{data}"
102
112
  end
103
113
 
104
114
  private
105
115
 
106
- def api_event_resource_path(event_collection)
116
+ def publish_body(path, body, error_method)
117
+ begin
118
+ response = Keen::HTTP::Sync.new(
119
+ self.api_url).post(
120
+ :path => path,
121
+ :headers => api_headers(self.write_key, "sync"),
122
+ :body => body)
123
+ rescue Exception => http_error
124
+ raise HttpError.new("HTTP #{error_method} failure: #{http_error.message}", http_error)
125
+ end
126
+ process_response(response.code, response.body.chomp)
127
+ end
128
+
129
+ def api_event_collection_resource_path(event_collection)
107
130
  "/#{api_version}/projects/#{project_id}/events/#{URI.escape(event_collection)}"
108
131
  end
109
132
 
133
+ def api_events_resource_path
134
+ "/#{api_version}/projects/#{project_id}/events"
135
+ end
136
+
110
137
  def check_event_data!(event_collection, properties)
111
138
  raise ArgumentError, "Event collection can not be nil" unless event_collection
112
139
  raise ArgumentError, "Event properties can not be nil" unless properties
@@ -165,7 +165,7 @@ module Keen
165
165
  ensure_read_key!
166
166
 
167
167
  if event_collection
168
- params[:event_collection] = event_collection
168
+ params[:event_collection] = event_collection.to_s
169
169
  end
170
170
 
171
171
  query_params = preprocess_params(params)
data/lib/keen/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Keen
2
- VERSION = "0.7.2"
2
+ VERSION = "0.7.3"
3
3
  end
data/lib/keen.rb CHANGED
@@ -25,19 +25,20 @@ module Keen
25
25
  class << self
26
26
  extend Forwardable
27
27
 
28
- def_delegators :default_client,
29
- :project_id, :project_id=,
30
- :write_key, :write_key=,
31
- :read_key, :read_key=,
28
+ def_delegators :default_client,
29
+ :project_id, :project_id=,
30
+ :write_key, :write_key=,
31
+ :read_key, :read_key=,
32
32
  :api_url, :api_url=
33
33
 
34
34
  def_delegators :default_client,
35
- :publish, :publish_async,
35
+ :publish, :publish_async, :publish_batch,
36
36
  :beacon_url
37
37
 
38
38
  def_delegators :default_client,
39
39
  :count, :count_unique, :minimum, :maximum,
40
- :sum, :average, :select_unique, :funnel, :extraction
40
+ :sum, :average, :select_unique, :funnel, :extraction,
41
+ :multi_analysis
41
42
 
42
43
  attr_writer :logger
43
44
 
@@ -60,6 +60,29 @@ describe "Keen IO API" do
60
60
  end
61
61
  end
62
62
  end
63
+
64
+ describe "batch" do
65
+ it "should publish a batch of events" do
66
+ Keen.publish_batch(
67
+ :batch_signups => [
68
+ { :name => "bob" },
69
+ { :name => "ted" }
70
+ ],
71
+ :batch_purchases => [
72
+ { :price => 30 },
73
+ { :price => 40 }
74
+ ]
75
+ ).should == {
76
+ "batch_purchases" => [
77
+ { "success" => true },
78
+ { "success" => true }
79
+ ],
80
+ "batch_signups" => [
81
+ { "success" => true },
82
+ { "success"=>true }
83
+ ]}
84
+ end
85
+ end
63
86
  end
64
87
 
65
88
  describe "queries" do
@@ -13,14 +13,14 @@ describe Keen::Client::PublishingMethods do
13
13
 
14
14
  describe "publish" do
15
15
  it "should post using the collection and properties" do
16
- stub_keen_post(api_event_resource_url(api_url, collection), 201, "")
16
+ stub_keen_post(api_event_collection_resource_url(api_url, collection), 201, "")
17
17
  client.publish(collection, event_properties)
18
- expect_keen_post(api_event_resource_url(api_url, collection), event_properties, "sync", write_key)
18
+ expect_keen_post(api_event_collection_resource_url(api_url, collection), event_properties, "sync", write_key)
19
19
  end
20
20
 
21
21
  it "should return the proper response" do
22
22
  api_response = { "created" => true }
23
- stub_keen_post(api_event_resource_url(api_url, collection), 201, api_response)
23
+ stub_keen_post(api_event_collection_resource_url(api_url, collection), 201, api_response)
24
24
  client.publish(collection, event_properties).should == api_response
25
25
  end
26
26
 
@@ -37,13 +37,13 @@ describe Keen::Client::PublishingMethods do
37
37
  end
38
38
 
39
39
  it "should url encode the event collection" do
40
- stub_keen_post(api_event_resource_url(api_url, "foo%20bar"), 201, "")
40
+ stub_keen_post(api_event_collection_resource_url(api_url, "foo%20bar"), 201, "")
41
41
  client.publish("foo bar", event_properties)
42
- expect_keen_post(api_event_resource_url(api_url, "foo%20bar"), event_properties, "sync", write_key)
42
+ expect_keen_post(api_event_collection_resource_url(api_url, "foo%20bar"), event_properties, "sync", write_key)
43
43
  end
44
44
 
45
45
  it "should wrap exceptions" do
46
- stub_request(:post, api_event_resource_url(api_url, collection)).to_timeout
46
+ stub_request(:post, api_event_collection_resource_url(api_url, collection)).to_timeout
47
47
  e = nil
48
48
  begin
49
49
  client.publish(collection, event_properties)
@@ -73,6 +73,45 @@ describe Keen::Client::PublishingMethods do
73
73
  end
74
74
  end
75
75
 
76
+ describe "publish_batch" do
77
+ let(:events) {
78
+ {
79
+ :purchases => [
80
+ { :price => 10 },
81
+ { :price => 11 }
82
+ ],
83
+ :signups => [
84
+ { :name => "bob" },
85
+ { :name => "bill" }
86
+ ]
87
+ }
88
+ }
89
+
90
+ it "should raise an exception if client has no project_id" do
91
+ expect {
92
+ Keen::Client.new(
93
+ :write_key => "abcde"
94
+ ).publish_batch(events)
95
+ }.to raise_error(Keen::ConfigurationError, "Keen IO Exception: Project ID must be set")
96
+ end
97
+
98
+ it "should raise an exception if client has no write_key" do
99
+ expect {
100
+ Keen::Client.new(
101
+ :project_id => "12345"
102
+ ).publish_batch(events)
103
+ }.to raise_error(Keen::ConfigurationError, "Keen IO Exception: Write Key must be set for sending events")
104
+ end
105
+
106
+ it "should publish a batch of events" do
107
+ stub_keen_post(api_event_resource_url(api_url), 201, "")
108
+ client.publish_batch(events)
109
+ expect_keen_post(
110
+ api_event_resource_url(api_url),
111
+ events, "sync", write_key)
112
+ end
113
+ end
114
+
76
115
  describe "publish_async" do
77
116
  # no TLS support in EventMachine on jRuby
78
117
  unless defined?(JRUBY_VERSION)
@@ -83,11 +122,11 @@ describe Keen::Client::PublishingMethods do
83
122
  end
84
123
 
85
124
  it "should post the event data" do
86
- stub_keen_post(api_event_resource_url(api_url, collection), 201, api_success)
125
+ stub_keen_post(api_event_collection_resource_url(api_url, collection), 201, api_success)
87
126
  EM.run {
88
127
  client.publish_async(collection, event_properties).callback {
89
128
  begin
90
- expect_keen_post(api_event_resource_url(api_url, collection), event_properties, "async", write_key)
129
+ expect_keen_post(api_event_collection_resource_url(api_url, collection), event_properties, "async", write_key)
91
130
  ensure
92
131
  EM.stop
93
132
  end
@@ -99,11 +138,11 @@ describe Keen::Client::PublishingMethods do
99
138
  end
100
139
 
101
140
  it "should uri encode the event collection" do
102
- stub_keen_post(api_event_resource_url(api_url, "foo%20bar"), 201, api_success)
141
+ stub_keen_post(api_event_collection_resource_url(api_url, "foo%20bar"), 201, api_success)
103
142
  EM.run {
104
143
  client.publish_async("foo bar", event_properties).callback {
105
144
  begin
106
- expect_keen_post(api_event_resource_url(api_url, "foo%20bar"), event_properties, "async", write_key)
145
+ expect_keen_post(api_event_collection_resource_url(api_url, "foo%20bar"), event_properties, "async", write_key)
107
146
  ensure
108
147
  EM.stop
109
148
  end
@@ -128,7 +167,7 @@ describe Keen::Client::PublishingMethods do
128
167
 
129
168
  describe "deferrable callbacks" do
130
169
  it "should trigger callbacks" do
131
- stub_keen_post(api_event_resource_url(api_url, collection), 201, api_success)
170
+ stub_keen_post(api_event_collection_resource_url(api_url, collection), 201, api_success)
132
171
  EM.run {
133
172
  client.publish_async(collection, event_properties).callback { |response|
134
173
  begin
@@ -141,7 +180,7 @@ describe Keen::Client::PublishingMethods do
141
180
  end
142
181
 
143
182
  it "should trigger errbacks" do
144
- stub_request(:post, api_event_resource_url(api_url, collection)).to_timeout
183
+ stub_request(:post, api_event_collection_resource_url(api_url, collection)).to_timeout
145
184
  EM.run {
146
185
  client.publish_async(collection, event_properties).errback { |error|
147
186
  begin
@@ -155,7 +194,7 @@ describe Keen::Client::PublishingMethods do
155
194
  end
156
195
 
157
196
  it "should not trap exceptions in the client callback" do
158
- stub_keen_post(api_event_resource_url(api_url, "foo%20bar"), 201, api_success)
197
+ stub_keen_post(api_event_collection_resource_url(api_url, "foo%20bar"), 201, api_success)
159
198
  expect {
160
199
  EM.run {
161
200
  client.publish_async("foo bar", event_properties).callback {
@@ -113,13 +113,23 @@ describe Keen::Client do
113
113
  end
114
114
 
115
115
  describe "#count" do
116
- it "should not require params" do
117
- query_params = "?event_collection=#{event_collection}"
118
- url = query_url("count", query_params)
116
+ let(:query_params) { "?event_collection=#{event_collection}" }
117
+ let(:url) { query_url("count", query_params) }
118
+ before do
119
119
  stub_keen_get(url, 200, :result => 10)
120
+ end
121
+
122
+ it "should not require params" do
120
123
  client.count(event_collection).should == 10
121
124
  expect_keen_get(url, "sync", read_key)
122
125
  end
126
+
127
+ context "with event collection as symbol" do
128
+ let(:event_collection) { :users }
129
+ it "should not require a string" do
130
+ client.count(event_collection).should == 10
131
+ end
132
+ end
123
133
  end
124
134
 
125
135
  describe "#extraction" do
@@ -62,12 +62,21 @@ describe Keen do
62
62
  end
63
63
  end
64
64
 
65
- [:publish, :publish_async].each do |_method|
65
+ [:publish, :publish_async, :publish_batch].each do |_method|
66
66
  it "should forward the #{_method} method" do
67
67
  @default_client.should_receive(_method).with("users", {})
68
68
  Keen.send(_method, "users", {})
69
69
  end
70
70
  end
71
+
72
+ # pull the query methods list at runtime in order to ensure
73
+ # any new methods have a corresponding delegator
74
+ Keen::Client::QueryingMethods.instance_methods.each do |_method|
75
+ it "should forward the #{_method} query method" do
76
+ @default_client.should_receive(_method).with("users", {})
77
+ Keen.send(_method, "users", {})
78
+ end
79
+ end
71
80
  end
72
81
 
73
82
  describe "logger" do
data/spec/spec_helper.rb CHANGED
@@ -48,9 +48,13 @@ module Keen::SpecHelpers
48
48
  expect_keen_request(:post, url, MultiJson.encode(event_properties), sync_or_async_ua, write_key)
49
49
  end
50
50
 
51
- def api_event_resource_url(base_url, collection)
51
+ def api_event_collection_resource_url(base_url, collection)
52
52
  "#{base_url}/3.0/projects/#{project_id}/events/#{collection}"
53
53
  end
54
+
55
+ def api_event_resource_url(base_url)
56
+ "#{base_url}/3.0/projects/#{project_id}/events"
57
+ end
54
58
  end
55
59
 
56
60
  RSpec.configure do |config|
@@ -17,16 +17,16 @@ describe Keen::HTTP::Async do
17
17
 
18
18
  describe "success" do
19
19
  it "should post the event data" do
20
- stub_keen_post(api_event_resource_url(api_url, collection), 201, api_success)
20
+ stub_keen_post(api_event_collection_resource_url(api_url, collection), 201, api_success)
21
21
  EM.synchrony {
22
22
  @client.publish_async(collection, event_properties)
23
- expect_keen_post(api_event_resource_url(api_url, collection), event_properties, "async", write_key)
23
+ expect_keen_post(api_event_collection_resource_url(api_url, collection), event_properties, "async", write_key)
24
24
  EM.stop
25
25
  }
26
26
  end
27
27
 
28
28
  it "should recieve the right response 'synchronously'" do
29
- stub_keen_post(api_event_resource_url(api_url, collection), 201, api_success)
29
+ stub_keen_post(api_event_collection_resource_url(api_url, collection), 201, api_success)
30
30
  EM.synchrony {
31
31
  @client.publish_async(collection, event_properties).should == api_success
32
32
  EM.stop
@@ -36,7 +36,7 @@ describe Keen::HTTP::Async do
36
36
 
37
37
  describe "failure" do
38
38
  it "should raise an exception" do
39
- stub_request(:post, api_event_resource_url(api_url, collection)).to_timeout
39
+ stub_request(:post, api_event_collection_resource_url(api_url, collection)).to_timeout
40
40
  e = nil
41
41
  EM.synchrony {
42
42
  begin
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.7.2
4
+ version: 0.7.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-05-02 00:00:00.000000000 Z
14
+ date: 2013-05-17 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: multi_json