keen 0.7.2 → 0.7.3

Sign up to get free protection for your applications and to get access to all the features.
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