ahoy_matey 0.1.8 → 0.2.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e26a906ddc293ac0416f2f254d784cfc6db73003
4
- data.tar.gz: 541082bbd91a7633e0dbec197f8107e5d4eda36b
3
+ metadata.gz: c1ddf7a214c83d60f35b1d7a39653ef65d5ab3ee
4
+ data.tar.gz: e3c9af288c825faffda2cb7bf97cd8aa248c41a6
5
5
  SHA512:
6
- metadata.gz: 003f1ce5a22f8a075dc29e2f1fec577621977152821cfd4bff6f8f6b930237909f9b63ca148efc03d9ddc12f8224a1f60f6683f1753ba801569ca745dab03898
7
- data.tar.gz: bdc8fe84047e09ffec04fa4464c30698ef7be5771749a2fce969e70b704676e33395ff2cb508e9541c3930111228fa5d3b17e46b01b661fb791ea686b18a320a
6
+ metadata.gz: e3c068287e17bc73b51ce57af242a91013720b64dceefb00330a85ac2c586ba9b11b3fdd6395b9d8adf3d952b9dee1a73557d16a6a8792a49fa62d9c1893ba3c
7
+ data.tar.gz: 137594a001bb62a37274b94fe7776074b0880e74ddfdd0d48cc305d67847d13ca056de3f6fe38161d180d13cf22f79d6d363888a949d2d3abbdea8eed056d7cd
@@ -1,3 +1,8 @@
1
+ ## 0.2.0
2
+
3
+ - Added event tracking (merged ahoy_events)
4
+ - Added ahoy.js
5
+
1
6
  ## 0.1.8
2
7
 
3
8
  - Fixed bug with `user_type` set to `false` instead of `nil`
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Ahoy
2
2
 
3
- :fire: Simple, powerful visit tracking for Rails
3
+ :fire: Simple, powerful analytics for Rails
4
4
 
5
5
  Visits are stored in **your database** so you can easily combine them with other data.
6
6
 
@@ -11,16 +11,18 @@ You get:
11
11
  - **technology** - browser, OS, and device type
12
12
  - **utm parameters** - source, medium, term, content, campaign
13
13
 
14
- See which campaigns generate the most revenue effortlessly.
14
+ Track events in:
15
15
 
16
- ```ruby
17
- Order.joins(:visit).group("utm_campaign").sum(:revenue)
18
- ```
16
+ - JavaScript
17
+ - Ruby
18
+ - Native apps
19
19
 
20
- :seedling: To track events like page views, check out [Ahoy Events](https://github.com/ankane/ahoy_events).
20
+ And store them wherever you’d like - your database, logs, external services, or all of them.
21
21
 
22
22
  :postbox: To track emails, check out [Ahoy Email](https://github.com/ankane/ahoy_email).
23
23
 
24
+ No Ruby? Check out [Ahoy.js](https://github.com/ankane/ahoy.js).
25
+
24
26
  ## Installation
25
27
 
26
28
  Add this line to your application’s Gemfile:
@@ -93,8 +95,6 @@ Order.joins(:visit).group("device_type").count
93
95
 
94
96
  Ahoy automatically attaches the `current_user` to the `current_visit`.
95
97
 
96
- If you define your own `current_user` method, be sure to add it to `ActionController::Base`, not `ApplicationController`.
97
-
98
98
  With [Devise](https://github.com/plataformatec/devise), it will attach the user even if he / she signs in after the visit starts.
99
99
 
100
100
  With other authentication frameworks, add this to the end of your sign in method:
@@ -135,19 +135,104 @@ or
135
135
  http://datakick.org/?utm_medium=twitter&utm_campaign=social&utm_source=tweet123
136
136
  ```
137
137
 
138
- ### Location
138
+ ### Native Apps
139
139
 
140
- Ahoy uses [Geocoder](https://github.com/alexreisner/geocoder) for IP-based geocoding.
140
+ When a user launches the app, create a visit. Send a `POST` request to `/ahoy/visits` with:
141
141
 
142
- ### Multiple Subdomains
142
+ - platform - `iOS`, `Android`, etc.
143
+ - app_version - `1.0.0`
144
+ - os_version - `7.0.6`
145
+ - visitor_token - if you have one
143
146
 
144
- To track visits across multiple subdomains, add this **before** the javascript files.
147
+ The endpoint will return a JSON response like:
148
+
149
+ ```json
150
+ {
151
+ "visit_token": "8tx2ziymkwa1WlppnkqxyaBaRlXrEQ3K",
152
+ "visitor_token": "hYBIV0rBfrIUAiArWweiECt4N9pyiygN"
153
+ }
154
+ ```
155
+
156
+ Send the visit token in the `Ahoy-Visit` header for all requests.
157
+
158
+ After 4 hours, create another visit and use the updated visit token.
159
+
160
+ ## Events
161
+
162
+ Each event has a `name` and `properties`.
163
+
164
+ There are three ways to track events.
165
+
166
+ #### JavaScript
145
167
 
146
168
  ```javascript
147
- var ahoy = {"domain": "yourdomain.com"};
169
+ ahoy.track("Viewed book", {title: "The World is Flat"});
170
+ ```
171
+
172
+ or track all views and clicks with:
173
+
174
+ ```javascript
175
+ ahoy.trackAll();
176
+ ```
177
+
178
+ #### Ruby
179
+
180
+ ```ruby
181
+ ahoy.track "Viewed book", title: "Hot, Flat, and Crowded"
148
182
  ```
149
183
 
150
- ### Development
184
+ #### Native Apps
185
+
186
+ Send a `POST` request to `/ahoy/events` with:
187
+
188
+ - name
189
+ - properties
190
+ - user token (depends on your authentication framework)
191
+ - `Ahoy-Visit` header
192
+
193
+ Requests should have `Content-Type: application/json`.
194
+
195
+ ### Storing Events
196
+
197
+ You choose how to store events.
198
+
199
+ #### ActiveRecord
200
+
201
+ Create an `Ahoy::Event` model to store events.
202
+
203
+ ```sh
204
+ rails generate ahoy:events:active_record
205
+ rake db:migrate
206
+ ```
207
+
208
+ #### Custom
209
+
210
+ Create your own subscribers in `config/initializers/ahoy.rb`.
211
+
212
+ ```ruby
213
+ class LogSubscriber
214
+
215
+ def track(name, properties, options = {})
216
+ data = {
217
+ name: name,
218
+ properties: properties,
219
+ time: options[:time].to_i,
220
+ visit_id: options[:visit].try(:id),
221
+ user_id: options[:user].try(:id),
222
+ ip: options[:controller].try(:request).try(:remote_ip)
223
+ }
224
+ Rails.logger.info data.to_json
225
+ end
226
+
227
+ end
228
+
229
+ # and add it
230
+ Ahoy.subscribers << LogSubscriber.new
231
+ ```
232
+
233
+ Add as many subscribers as you’d like.
234
+
235
+ ## Development
151
236
 
152
237
  Ahoy is built with developers in mind. You can run the following code in your browser’s console.
153
238
 
@@ -169,27 +254,12 @@ Turn off logging
169
254
  ahoy.debug(false);
170
255
  ```
171
256
 
172
- ### Native Apps
173
-
174
- When a user launches the app, create a visit. Send a `POST` request to `/ahoy/visits` with:
175
-
176
- - platform - `iOS`, `Android`, etc.
177
- - app_version - `1.0.0`
178
- - os_version - `7.0.6`
179
- - visitor_token - if you have one
180
-
181
- The endpoint will return a JSON response like:
182
-
183
- ```json
184
- {
185
- "visit_token": "8tx2ziymkwa1WlppnkqxyaBaRlXrEQ3K",
186
- "visitor_token": "hYBIV0rBfrIUAiArWweiECt4N9pyiygN"
187
- }
188
- ```
189
-
190
- Send the visit token in the `Ahoy-Visit` header for all requests.
257
+ ### More
191
258
 
192
- After 4 hours, create another visit and use the updated visit token.
259
+ - Excludes bots
260
+ - Degrades gracefully when cookies are disabled
261
+ - Don’t need a field? Just remove it from the migration
262
+ - Visits are 4 hours by default
193
263
 
194
264
  ### Doorkeeper
195
265
 
@@ -207,24 +277,12 @@ class ApplicationController < ActionController::Base
207
277
  end
208
278
  ```
209
279
 
210
- ### More
211
-
212
- - Excludes bots
213
- - Degrades gracefully when cookies are disabled
214
- - Don’t need a field? Just remove it from the migration
215
- - Visits are 4 hours by default
216
-
217
280
  ## Reference
218
281
 
219
- Use a different model
220
-
221
- ```ruby
222
- Ahoy.visit_model = UserVisit
282
+ To track visits across multiple subdomains, add this **before** the javascript files.
223
283
 
224
- # fix for Rails reloader in development
225
- ActionDispatch::Reloader.to_prepare do
226
- Ahoy.visit_model = UserVisit
227
- end
284
+ ```javascript
285
+ var ahoy = {"domain": "yourdomain.com"};
228
286
  ```
229
287
 
230
288
  Change the platform on the web
@@ -266,6 +324,49 @@ Customize visitable
266
324
  visitable :sign_up_visit, class_name: "Visit"
267
325
  ```
268
326
 
327
+ Track view
328
+
329
+ ```javascript
330
+ ahoy.trackView();
331
+ ```
332
+
333
+ Track clicks
334
+
335
+ ```javascript
336
+ ahoy.trackClicks();
337
+ ```
338
+
339
+ Track all Rails actions
340
+
341
+ ```ruby
342
+ class ApplicationController < ActionController::Base
343
+ after_filter :track_action
344
+
345
+ protected
346
+
347
+ def track_action
348
+ ahoy.track "Hit action", request.filtered_parameters
349
+ end
350
+ end
351
+ ```
352
+
353
+ Use a different model for visits
354
+
355
+ ```ruby
356
+ Ahoy.visit_model = UserVisit
357
+
358
+ # fix for Rails reloader in development
359
+ ActionDispatch::Reloader.to_prepare do
360
+ Ahoy.visit_model = UserVisit
361
+ end
362
+ ```
363
+
364
+ Use a different model for events
365
+
366
+ ```ruby
367
+ Ahoy.subscribers << Ahoy::Subscribers::ActiveRecord.new(model: Event)
368
+ ```
369
+
269
370
  ## Upgrading
270
371
 
271
372
  In `0.1.6`, a big improvement was made to `browser` and `os`. Update existing visits with:
@@ -279,6 +380,7 @@ end
279
380
 
280
381
  ## TODO
281
382
 
383
+ - better readme
282
384
  - simple dashboard
283
385
  - turn off modules
284
386
 
@@ -0,0 +1,14 @@
1
+ module Ahoy
2
+ class EventsController < Ahoy::BaseController
3
+
4
+ def create
5
+ options = {}
6
+ if params[:time] and (time = Time.at(params[:time].to_f) rescue nil) and (1.minute.ago..Time.now).cover?(time)
7
+ options[:time] = time
8
+ end
9
+ ahoy.track params[:name], params[:properties], options
10
+ render json: {}
11
+ end
12
+
13
+ end
14
+ end
@@ -2,7 +2,7 @@ module Ahoy
2
2
  class VisitsController < BaseController
3
3
 
4
4
  def create
5
- visit_token = generate_token
5
+ visit_token = params[:visit_token] || generate_token
6
6
  visitor_token = params[:visitor_token] || generate_token
7
7
 
8
8
  visit =
@@ -0,0 +1,10 @@
1
+ module Ahoy
2
+ class Event < ActiveRecord::Base
3
+ self.table_name = "ahoy_events"
4
+
5
+ belongs_to :visit
6
+ belongs_to :user, polymorphic: true
7
+
8
+ serialize :properties, JSON
9
+ end
10
+ end
@@ -5,5 +5,6 @@ end
5
5
  Ahoy::Engine.routes.draw do
6
6
  scope module: "ahoy" do
7
7
  resources :visits, only: [:create]
8
+ resources :events, only: [:create]
8
9
  end
9
10
  end
@@ -3,6 +3,7 @@ module Ahoy
3
3
 
4
4
  def self.included(base)
5
5
  base.helper_method :current_visit
6
+ base.helper_method :ahoy
6
7
  base.before_filter do
7
8
  RequestStore.store[:ahoy_controller] ||= self
8
9
  end
@@ -15,5 +16,9 @@ module Ahoy
15
16
  end
16
17
  end
17
18
 
19
+ def ahoy
20
+ @ahoy ||= Ahoy::Tracker.new(controller: self)
21
+ end
22
+
18
23
  end
19
24
  end
@@ -0,0 +1,21 @@
1
+ module Ahoy
2
+ module Subscribers
3
+ class ActiveRecord
4
+
5
+ def initialize(options = {})
6
+ @model = options[:model] || Ahoy::Event
7
+ end
8
+
9
+ def track(name, properties, options = {})
10
+ @model.create! do |e|
11
+ e.visit = options[:visit]
12
+ e.user = options[:user]
13
+ e.name = name
14
+ e.properties = properties
15
+ e.time = options[:time]
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,30 @@
1
+ module Ahoy
2
+ class Tracker
3
+
4
+ def initialize(options = {})
5
+ @controller = options[:controller]
6
+ end
7
+
8
+ def track(name, properties, options = {})
9
+ # publish to each subscriber
10
+ if @controller
11
+ options[:controller] ||= @controller
12
+ options[:user] ||= Ahoy.fetch_user(@controller)
13
+ if @controller.respond_to?(:current_visit)
14
+ options[:visit] ||= @controller.current_visit
15
+ end
16
+ end
17
+ options[:time] ||= Time.zone.now
18
+
19
+ subscribers = Ahoy.subscribers
20
+ if subscribers.any?
21
+ subscribers.each do |subscriber|
22
+ subscriber.track(name, properties, options)
23
+ end
24
+ else
25
+ $stderr.puts "No subscribers"
26
+ end
27
+ end
28
+
29
+ end
30
+ end
@@ -1,3 +1,3 @@
1
1
  module Ahoy
2
- VERSION = "0.1.8"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -5,8 +5,10 @@ require "referer-parser"
5
5
  require "user_agent_parser"
6
6
  require "request_store"
7
7
  require "ahoy/version"
8
+ require "ahoy/tracker"
8
9
  require "ahoy/controller"
9
10
  require "ahoy/model"
11
+ require "ahoy/subscribers/active_record"
10
12
  require "ahoy/engine"
11
13
 
12
14
  module Ahoy
@@ -43,6 +45,8 @@ module Ahoy
43
45
  (controller.respond_to?(:current_user) && controller.current_user) || (controller.respond_to?(:current_resource_owner, true) && controller.send(:current_resource_owner)) || nil
44
46
  end
45
47
 
48
+ mattr_accessor :subscribers
49
+ self.subscribers = []
46
50
  end
47
51
 
48
52
  ActionController::Base.send :include, Ahoy::Controller
@@ -0,0 +1,36 @@
1
+ # taken from https://github.com/collectiveidea/audited/blob/master/lib/generators/audited/install_generator.rb
2
+ require "rails/generators"
3
+ require "rails/generators/migration"
4
+ require "active_record"
5
+ require "rails/generators/active_record"
6
+
7
+ module Ahoy
8
+ module Events
9
+ module Generators
10
+ class ActiveRecordGenerator < Rails::Generators::Base
11
+ include Rails::Generators::Migration
12
+
13
+ source_root File.expand_path("../templates", __FILE__)
14
+
15
+ # Implement the required interface for Rails::Generators::Migration.
16
+ def self.next_migration_number(dirname) #:nodoc:
17
+ next_migration_number = current_migration_number(dirname) + 1
18
+ if ActiveRecord::Base.timestamped_migrations
19
+ [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
20
+ else
21
+ "%.3d" % next_migration_number
22
+ end
23
+ end
24
+
25
+ def copy_migration
26
+ migration_template "create_events.rb", "db/migrate/create_ahoy_events.rb"
27
+ end
28
+
29
+ def create_initializer
30
+ template "initializer.rb", "config/initializers/ahoy.rb"
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,20 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration
2
+ def change
3
+ create_table :ahoy_events do |t|
4
+ # visit
5
+ t.references :visit
6
+
7
+ # user
8
+ t.integer :user_id
9
+ t.string :user_type
10
+
11
+ t.string :name
12
+ t.text :properties
13
+ t.timestamp :time
14
+ end
15
+
16
+ add_index :ahoy_events, [:visit_id]
17
+ add_index :ahoy_events, [:user_id, :user_type]
18
+ add_index :ahoy_events, [:time]
19
+ end
20
+ end
@@ -0,0 +1 @@
1
+ Ahoy.subscribers << Ahoy::Subscribers::ActiveRecord.new
@@ -10,6 +10,8 @@
10
10
  var visitorTtl = 2 * 365 * 24 * 60; // 2 years
11
11
  var isReady = false;
12
12
  var queue = [];
13
+ var canStringify = typeof(JSON) !== "undefined" && typeof(JSON.stringify) !== "undefined";
14
+ var eventQueue = [];
13
15
 
14
16
  // cookies
15
17
 
@@ -62,25 +64,98 @@
62
64
  isReady = true;
63
65
  }
64
66
 
67
+ function ready(callback) {
68
+ if (isReady) {
69
+ callback();
70
+ } else {
71
+ queue.push(callback);
72
+ }
73
+ }
74
+
75
+ // https://github.com/klughammer/node-randomstring
76
+ function generateId() {
77
+ var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghiklmnopqrstuvwxyz';
78
+ var length = 32;
79
+ var string = '';
80
+
81
+ for (var i = 0; i < length; i++) {
82
+ var randomNumber = Math.floor(Math.random() * chars.length);
83
+ string += chars.substring(randomNumber, randomNumber + 1);
84
+ }
85
+
86
+ return string;
87
+ }
88
+
89
+ function saveEventQueue() {
90
+ // TODO add stringify method for IE 7 and under
91
+ if (canStringify) {
92
+ setCookie("ahoy_events", JSON.stringify(eventQueue), 1);
93
+ }
94
+ }
95
+
96
+ function trackEvent(event) {
97
+ ready( function () {
98
+ // ensure JSON is defined
99
+ if (canStringify) {
100
+ $.ajax({
101
+ type: "POST",
102
+ url: "/ahoy/events",
103
+ data: JSON.stringify(event),
104
+ contentType: "application/json; charset=utf-8",
105
+ dataType: "json",
106
+ success: function() {
107
+ // remove from queue
108
+ for (var i = 0; i < eventQueue.length; i++) {
109
+ if (eventQueue[i].id == event.id) {
110
+ eventQueue.splice(i, 1);
111
+ break;
112
+ }
113
+ }
114
+ saveEventQueue();
115
+ }
116
+ });
117
+ }
118
+ });
119
+ }
120
+
121
+ function eventProperties(e) {
122
+ var $target = $(e.currentTarget);
123
+ return {
124
+ tag: $target.get(0).tagName.toLowerCase(),
125
+ id: $target.attr("id"),
126
+ class: $target.attr("class")
127
+ };
128
+ }
129
+
65
130
  // main
66
131
 
67
132
  visitToken = getCookie("ahoy_visit");
68
133
  visitorToken = getCookie("ahoy_visitor");
69
134
 
70
- if (visitToken && visitorToken && visitToken != "test") {
135
+ if (visitToken && visitorToken) {
71
136
  // TODO keep visit alive?
72
137
  log("Active visit");
73
138
  setReady();
74
139
  } else {
75
- setCookie("ahoy_visit", "test", 1);
140
+ visitToken = generateId();
141
+ setCookie("ahoy_visit", visitToken, visitTtl);
76
142
 
77
143
  // make sure cookies are enabled
78
144
  if (getCookie("ahoy_visit")) {
79
145
  log("Visit started");
80
146
 
147
+ if (!visitorToken) {
148
+ visitorToken = generateId();
149
+ setCookie("ahoy_visitor", visitorToken, visitorTtl);
150
+ }
151
+
81
152
  var data = {
153
+ visit_token: visitToken,
154
+ visitor_token: visitorToken,
82
155
  platform: ahoy.platform || "Web",
83
- landing_page: window.location.href
156
+ landing_page: window.location.href,
157
+ screen_width: window.screen.width,
158
+ screen_height: window.screen.height
84
159
  };
85
160
 
86
161
  // referrer
@@ -88,17 +163,9 @@
88
163
  data.referrer = document.referrer;
89
164
  }
90
165
 
91
- if (visitorToken) {
92
- data.visitor_token = visitorToken;
93
- }
94
-
95
166
  log(data);
96
167
 
97
- $.post("/ahoy/visits", data, function(response) {
98
- setCookie("ahoy_visit", response.visit_token, visitTtl);
99
- setCookie("ahoy_visitor", response.visitor_token, visitorTtl);
100
- setReady();
101
- }, "json");
168
+ $.post("/ahoy/visits", data, setReady, "json");
102
169
  } else {
103
170
  log("Cookies disabled");
104
171
  setReady();
@@ -120,18 +187,74 @@
120
187
  return true;
121
188
  };
122
189
 
123
- ahoy.setCookie = setCookie;
124
- ahoy.getCookie = getCookie;
125
- ahoy.destroyCookie = destroyCookie;
126
- ahoy.log = log;
190
+ ahoy.track = function (name, properties) {
191
+ // generate unique id
192
+ var event = {
193
+ id: generateId(),
194
+ name: name,
195
+ properties: properties,
196
+ time: (new Date()).getTime() / 1000.0
197
+ };
198
+ log(event);
127
199
 
128
- ahoy.ready = function (callback) {
129
- if (isReady) {
130
- callback();
131
- } else {
132
- queue.push(callback);
133
- }
200
+ eventQueue.push(event);
201
+ saveEventQueue();
202
+
203
+ // wait in case navigating to reduce duplicate events
204
+ setTimeout( function () {
205
+ trackEvent(event);
206
+ }, 1000);
207
+ };
208
+
209
+ ahoy.trackView = function () {
210
+ var properties = {
211
+ url: window.location.href,
212
+ title: document.title
213
+ };
214
+ ahoy.track("$view", properties);
215
+ };
216
+
217
+ ahoy.trackClicks = function () {
218
+ $(document).on("click", "a, button, input[type=submit]", function (e) {
219
+ var $target = $(e.currentTarget);
220
+ var properties = eventProperties(e);
221
+ properties.text = properties.tag == "input" ? $target.val() : $.trim($target.text());
222
+ properties.href = $target.attr("href");
223
+ ahoy.track("$click", properties);
224
+ });
225
+ };
226
+
227
+ ahoy.trackSubmits = function () {
228
+ $(document).on("submit", "form", function (e) {
229
+ var properties = eventProperties(e);
230
+ ahoy.track("$submit", properties);
231
+ });
134
232
  };
135
233
 
234
+ ahoy.trackChanges = function () {
235
+ $(document).on("change", "input, textarea, select", function (e) {
236
+ var properties = eventProperties(e);
237
+ ahoy.track("$change", properties);
238
+ });
239
+ };
240
+
241
+ ahoy.trackAll = function() {
242
+ ahoy.trackView();
243
+ ahoy.trackClicks();
244
+ ahoy.trackSubmits();
245
+ ahoy.trackChanges();
246
+ };
247
+
248
+ // push events from queue
249
+ try {
250
+ eventQueue = JSON.parse(getCookie("ahoy_events") || "[]");
251
+ } catch (e) {
252
+ // do nothing
253
+ }
254
+
255
+ for (var i = 0; i < eventQueue.length; i++) {
256
+ trackEvent(eventQueue[i]);
257
+ }
258
+
136
259
  window.ahoy = ahoy;
137
260
  }(window));
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ahoy_matey
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-11 00:00:00.000000000 Z
11
+ date: 2014-05-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -137,13 +137,20 @@ files:
137
137
  - Rakefile
138
138
  - ahoy_matey.gemspec
139
139
  - app/controllers/ahoy/base_controller.rb
140
+ - app/controllers/ahoy/events_controller.rb
140
141
  - app/controllers/ahoy/visits_controller.rb
142
+ - app/models/ahoy/event.rb
141
143
  - config/routes.rb
142
144
  - lib/ahoy/controller.rb
143
145
  - lib/ahoy/engine.rb
144
146
  - lib/ahoy/model.rb
147
+ - lib/ahoy/subscribers/active_record.rb
148
+ - lib/ahoy/tracker.rb
145
149
  - lib/ahoy/version.rb
146
150
  - lib/ahoy_matey.rb
151
+ - lib/generators/ahoy/events/active_record_generator.rb
152
+ - lib/generators/ahoy/events/templates/create_events.rb
153
+ - lib/generators/ahoy/events/templates/initializer.rb
147
154
  - lib/generators/ahoy/install_generator.rb
148
155
  - lib/generators/ahoy/templates/install.rb
149
156
  - vendor/assets/javascripts/ahoy.js