Use ActiveRecordTokenStore by default, and transition to safely
Andrew Kane
committed Mar 23, 2016
commit 10622c8f51a03939af9a80cd2f4f961529e6b055
Showing 12
changed files with
61 additions
and 78 deletions
CHANGELOG.md
+7
-1
| @@ | @@ -1,6 +1,12 @@ |
| - | ## 1.3.1 |
| + | ## 1.4.0 [unreleased] |
| + | - Use `ActiveRecordTokenStore` by default (integer instead of uuid for id) |
| + | - Detect database for `rails g ahoy:stores:active_record` for easier installation |
| + | - Use `safely` as default exception handler |
| - Fixed issue with log silencer | |
| + | |
| + | ## 1.3.1 |
| + | |
| - Raise errors in test environment | |
| ## 1.3.0 | |
README.md
+20
-53
| @@ | @@ -25,9 +25,7 @@ And add the javascript file in `app/assets/javascripts/application.js` after jQu |
| Ahoy supports a number of data stores out of the box. You can start with one of them and customize as needed, or create your own store from scratch. | |
| - | - [PostgreSQL](#postgresql) |
| - | - [MySQL](#mysql-or-sqlite) |
| - | - [SQLite](#mysql-or-sqlite) |
| + | - [PostgreSQL, MySQL, or SQLite](#postgresql-mysql-or-sqlite) |
| - [MongoDB](#mongodb) | |
| - [Fluentd](#fluentd) | |
| - [RabbitMQ](#rabbitmq) | |
| @@ | @@ -35,46 +33,15 @@ Ahoy supports a number of data stores out of the box. You can start with one of |
| - [Logs](#logs) | |
| - [Custom](#custom) | |
| - | ### PostgreSQL |
| + | ### PostgreSQL, MySQL, or SQLite |
| - | For Rails 4.2 and PostgreSQL 9.4 or greater, use: |
| - | |
| - | ```sh |
| - | rails generate ahoy:stores:active_record -d postgresql-jsonb |
| - | rake db:migrate |
| - | ``` |
| - | |
| - | For Rails 4 and PostgreSQL 9.2 or greater, use: |
| - | |
| - | ```sh |
| - | rails generate ahoy:stores:active_record -d postgresql |
| - | rake db:migrate |
| - | ``` |
| - | |
| - | Otherwise, follow the instructions for MySQL. |
| - | |
| - | ### MySQL or SQLite |
| - | |
| - | Add [activeuuid](https://github.com/jashmenn/activeuuid) to your Gemfile. |
| - | |
| - | ```ruby |
| - | gem 'activeuuid', '>= 0.5.0' |
| - | ``` |
| - | |
| - | And run: |
| + | Run: |
| ```sh | |
| rails generate ahoy:stores:active_record | |
| rake db:migrate | |
| ``` | |
| - | If you just want visits, run: |
| - | |
| - | ```sh |
| - | rails generate ahoy:stores:active_record_visits |
| - | rake db:migrate |
| - | ``` |
| - | |
| ### MongoDB | |
| ```sh | |
| @@ | @@ -217,7 +184,7 @@ ahoy.authenticate(user) |
| Stores are built to be highly customizable. | |
| ```ruby | |
| - | class Ahoy::Store < Ahoy::Stores::ActiveRecordStore |
| + | class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore |
| # add methods here | |
| end | |
| ``` | |
| @@ | @@ -227,7 +194,7 @@ end |
| Exclude visits and events from being tracked with: | |
| ```ruby | |
| - | class Ahoy::Store < Ahoy::Stores::ActiveRecordStore |
| + | class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore |
| def exclude? | |
| bot? || request.ip == "192.168.1.1" | |
| end | |
| @@ | @@ -239,7 +206,7 @@ Bots are excluded by default. |
| ### Track Additional Values | |
| ```ruby | |
| - | class Ahoy::Store < Ahoy::Stores::ActiveRecordStore |
| + | class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore |
| def track_visit(options) | |
| super do |visit| | |
| visit.gclid = visit_properties.landing_params["gclid"] | |
| @@ | @@ -259,7 +226,7 @@ end |
| If you use a method other than `current_user`, set it here: | |
| ```ruby | |
| - | class Ahoy::Store < Ahoy::Stores::ActiveRecordStore |
| + | class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore |
| def user | |
| controller.true_user | |
| end | |
| @@ | @@ -268,18 +235,12 @@ end |
| ### Report Exceptions | |
| - | Exceptions are rescued so analytics do not break your app. |
| - | |
| - | Ahoy uses [Errbase](https://github.com/ankane/errbase) to try to report them to a service by default. |
| + | Exceptions are rescued so analytics do not break your app. Ahoy uses [Safely](https://github.com/ankane/safely) to try to report them to a service by default. |
| To customize this, use: | |
| ```ruby | |
| - | class Ahoy::Store < Ahoy::Stores::ActiveRecordStore |
| - | def report_exception(e) |
| - | Rollbar.report_exception(e) |
| - | end |
| - | end |
| + | Safely.report_exception_method = proc { |e| Rollbar.error(e) } |
| ``` | |
| ### Use Different Models | |
| @@ | @@ -287,7 +248,7 @@ end |
| For ActiveRecord and Mongoid stores | |
| ```ruby | |
| - | class Ahoy::Store < Ahoy::Stores::ActiveRecordStore |
| + | class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore |
| def visit_model | |
| CustomVisit | |
| end | |
| @@ | @@ -355,11 +316,13 @@ First, generate a migration and add a `visit_id` column. |
| ```ruby | |
| class AddVisitIdToOrders < ActiveRecord::Migration | |
| def change | |
| - | add_column :orders, :visit_id, :uuid |
| + | add_column :orders, :visit_id, :integer |
| end | |
| end | |
| ``` | |
| + | **Note**: Use the `uuid` column type if the `id` column on `visits` is a `uuid`. |
| + | |
| Then, add `visitable` to the model. | |
| ```ruby | |
| @@ | @@ -505,7 +468,7 @@ added_item_ids = Ahoy::Event.where(user_id: viewed_store_ids, name: "Added item |
| viewed_checkout_ids = Ahoy::Event.where(user_id: added_item_ids, name: "Viewed checkout").uniq.pluck(:user_id) | |
| ``` | |
| - | The same approach also works with visitor ids. |
| + | The same approach also works with visitor tokens. |
| ## Native Apps | |
| @@ | @@ -513,7 +476,7 @@ The same approach also works with visitor ids. |
| When a user launches the app, create a visit. | |
| - | Generate a `visit_id` and `visitor_id` as [UUIDs](http://en.wikipedia.org/wiki/Universally_unique_identifier). |
| + | Generate a `visit_token` and `visitor_token` as [UUIDs](http://en.wikipedia.org/wiki/Universally_unique_identifier). |
| Send these values in the `Ahoy-Visit` and `Ahoy-Visitor` headers with all requests. | |
| @@ | @@ -540,7 +503,11 @@ Use an array to pass multiple events at once. |
| ## Upgrading | |
| - | ### PostgreSQL 9.4 + JSONB |
| + | ### 1.4.0 |
| + | |
| + | There’s nothing to do, but it’s worth noting the default store was changed from `ActiveRecordStore` to `ActiveRecordTokenStore` for new installations. |
| + | |
| + | ### json -> jsonb |
| Create a migration to add a new `jsonb` column. | |
ahoy_matey.gemspec
+1
-1
| @@ | @@ -26,7 +26,7 @@ Gem::Specification.new do |spec| |
| spec.add_dependency "user_agent_parser" | |
| spec.add_dependency "request_store" | |
| spec.add_dependency "uuidtools" | |
| - | spec.add_dependency "errbase" |
| + | spec.add_dependency "safely_block" |
| spec.add_dependency "rack-attack" | |
| spec.add_development_dependency "bundler", "~> 1.5" | |
ahoy.rb b/lib/ahoy.rb
+1
-1
| @@ | @@ -6,7 +6,7 @@ require "referer-parser" |
| require "user_agent_parser" | |
| require "request_store" | |
| require "uuidtools" | |
| - | require "errbase" |
| + | require "safely_block" |
| require "ahoy/version" | |
| require "ahoy/tracker" | |
ahoy/stores/active_record_token_store.rb b/lib/ahoy/stores/active_record_token_store.rb
+2
-1
| @@ | @@ -7,7 +7,8 @@ module Ahoy |
| v.visit_token = ahoy.visit_token | |
| v.visitor_token = ahoy.visitor_token | |
| v.user = user if v.respond_to?(:user=) | |
| - | v.created_at = options[:started_at] |
| + | v.started_at = options[:started_at] if v.respond_to?(:started_at) |
| + | v.created_at = options[:started_at] if v.respond_to?(:created_at) |
| end | |
| set_visit_properties(visit) | |
ahoy/stores/base_store.rb b/lib/ahoy/stores/base_store.rb
+1
-1
| @@ | @@ -27,7 +27,7 @@ module Ahoy |
| end | |
| def report_exception(e) | |
| - | Errbase.report(e) |
| + | raise e |
| end | |
| def user | |
ahoy/tracker.rb b/lib/ahoy/tracker.rb
+6
-7
| @@ | @@ -124,15 +124,14 @@ module Ahoy |
| @store.exclude? | |
| end | |
| + | # odd pattern for backwards compatibility |
| + | # TODO remove this method in next major release |
| def report_exception(e) | |
| - | begin |
| + | safely do |
| @store.report_exception(e) | |
| - | rescue |
| - | # fail-safe |
| - | $stderr.puts "Error reporting exception" |
| - | end |
| - | if Rails.env.development? || Rails.env.test? |
| - | raise e |
| + | if Rails.env.development? || Rails.env.test? |
| + | raise e |
| + | end |
| end | |
| end | |
generators/ahoy/stores/active_record_events_generator.rb b/lib/generators/ahoy/stores/active_record_events_generator.rb
+11
-1
| @@ | @@ -24,7 +24,8 @@ module Ahoy |
| end | |
| def copy_migration | |
| - | unless options["database"].in?([nil, "postgresql", "postgresql-jsonb"]) |
| + | @database = options["database"] || detect_database |
| + | unless @database.in?([nil, "postgresql", "postgresql-jsonb", "mysql", "sqlite"]) |
| raise Thor::Error, "Unknown database option" | |
| end | |
| migration_template "active_record_events_migration.rb", "db/migrate/create_ahoy_events.rb" | |
| @@ | @@ -37,6 +38,15 @@ module Ahoy |
| def create_initializer | |
| template "active_record_initializer.rb", "config/initializers/ahoy.rb" | |
| end | |
| + | |
| + | def detect_database |
| + | postgresql_version = ActiveRecord::Base.connection.send(:postgresql_version) rescue 0 |
| + | if postgresql_version >= 90400 |
| + | "postgresql-jsonb" |
| + | elsif postgresql_version >= 90200 |
| + | "postgresql" |
| + | end |
| + | end |
| end | |
| end | |
| end | |
generators/ahoy/stores/templates/active_record_event_model.rb b/lib/generators/ahoy/stores/templates/active_record_event_model.rb
+1
-1
| @@ | @@ -3,7 +3,7 @@ module Ahoy |
| self.table_name = "ahoy_events" | |
| belongs_to :visit | |
| - | belongs_to :user<% if options["database"].blank? %> |
| + | belongs_to :user<% unless %w(postgresql postgresql-jsonb).include?(@database) %> |
| serialize :properties, JSON<% end %> | |
| end | |
generators/ahoy/stores/templates/active_record_events_migration.rb b/lib/generators/ahoy/stores/templates/active_record_events_migration.rb
+6
-7
| @@ | @@ -1,20 +1,19 @@ |
| class <%= migration_class_name %> < ActiveRecord::Migration | |
| def change | |
| - | create_table :ahoy_events, id: false do |t| |
| - | t.uuid :id, default: nil, primary_key: true |
| - | t.uuid :visit_id, default: nil |
| + | create_table :ahoy_events do |t| |
| + | t.integer :visit_id |
| # user | |
| t.integer :user_id | |
| # add t.string :user_type if polymorphic | |
| t.string :name | |
| - | t.<% case options["database"] when "postgresql" %>json<% when "postgresql-jsonb" %>jsonb<% else %>text<% end %> :properties |
| + | t.<% case @database when "postgresql" %>json<% when "postgresql-jsonb" %>jsonb<% else %>text<% end %> :properties |
| t.timestamp :time | |
| end | |
| - | add_index :ahoy_events, [:visit_id] |
| - | add_index :ahoy_events, [:user_id] |
| - | add_index :ahoy_events, [:time] |
| + | add_index :ahoy_events, [:visit_id, :name] |
| + | add_index :ahoy_events, [:user_id, :name] |
| + | add_index :ahoy_events, [:name, :time] |
| end | |
| end | |
generators/ahoy/stores/templates/active_record_initializer.rb b/lib/generators/ahoy/stores/templates/active_record_initializer.rb
+1
-1
| @@ | @@ -1,3 +1,3 @@ |
| - | class Ahoy::Store < Ahoy::Stores::ActiveRecordStore |
| + | class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore |
| # customize here | |
| end | |
generators/ahoy/stores/templates/active_record_visits_migration.rb b/lib/generators/ahoy/stores/templates/active_record_visits_migration.rb
+4
-3
| @@ | @@ -1,8 +1,8 @@ |
| class <%= migration_class_name %> < ActiveRecord::Migration | |
| def change | |
| - | create_table :visits, id: false do |t| |
| - | t.uuid :id, default: nil, primary_key: true |
| - | t.uuid :visitor_id, default: nil |
| + | create_table :visits do |t| |
| + | t.string :visit_token |
| + | t.string :visitor_token |
| # the rest are recommended but optional | |
| # simply remove the columns you don't want | |
| @@ | @@ -51,6 +51,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration |
| t.timestamp :started_at | |
| end | |
| + | add_index :visits, [:visit_token], unique: true |
| add_index :visits, [:user_id] | |
| end | |
| end | |