spree_analytics_trackers 1.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.bundle/config +2 -0
  3. data/.gitignore +21 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +2 -0
  6. data/.travis.yml +44 -0
  7. data/Appraisals +11 -0
  8. data/Gemfile +7 -0
  9. data/LICENSE +26 -0
  10. data/README.md +77 -0
  11. data/Rakefile +21 -0
  12. data/app/assets/javascripts/spree/frontend/add_to_cart_analytics.js +50 -0
  13. data/app/assets/javascripts/spree/frontend/remove_from_cart_analytics.js +34 -0
  14. data/app/controllers/spree/admin/trackers_controller.rb +14 -0
  15. data/app/controllers/spree_analytics_trackers/store_controller_decorator.rb +14 -0
  16. data/app/helpers/spree/trackers_helper.rb +86 -0
  17. data/app/models/spree/tracker.rb +31 -0
  18. data/app/models/spree_analytics_trackers/user_decorator.rb +9 -0
  19. data/app/overrides/add_analytics_to_admin_sidebar.rb +8 -0
  20. data/app/overrides/add_cart_viewed_to_orders_edit.rb +10 -0
  21. data/app/overrides/add_checkout_step_viewed_to_checkout_edit.rb +18 -0
  22. data/app/overrides/add_google_analytics_page_view_to_spree_application.rb +10 -0
  23. data/app/overrides/add_google_analytics_to_spree_application.rb +9 -0
  24. data/app/overrides/add_google_purchase_to_orders_show.rb +9 -0
  25. data/app/overrides/add_order_complete_to_orders_show.rb +10 -0
  26. data/app/overrides/add_product_list_filtered_to_taxons_show.rb +10 -0
  27. data/app/overrides/add_product_list_viewed_to_products_list.rb +10 -0
  28. data/app/overrides/add_product_viewed_to_products_show.rb +10 -0
  29. data/app/overrides/add_products_searched_to_products_list.rb +10 -0
  30. data/app/overrides/add_segment_initializer_to_layout.rb +10 -0
  31. data/app/overrides/add_segment_page_viewed_to_layout.rb +10 -0
  32. data/app/presenters/spree_analytics_trackers/base_presenter.rb +14 -0
  33. data/app/presenters/spree_analytics_trackers/segment/order_presenter.rb +32 -0
  34. data/app/presenters/spree_analytics_trackers/segment/product_presenter.rb +40 -0
  35. data/app/views/spree/admin/shared/_analytics_sidebar_menu.html.erb +3 -0
  36. data/app/views/spree/admin/trackers/_form.html.erb +42 -0
  37. data/app/views/spree/admin/trackers/edit.html.erb +13 -0
  38. data/app/views/spree/admin/trackers/index.html.erb +42 -0
  39. data/app/views/spree/admin/trackers/new.html.erb +13 -0
  40. data/app/views/spree/shared/trackers/google_analytics/_checkout_step_viewed.js.erb +19 -0
  41. data/app/views/spree/shared/trackers/google_analytics/_initializer.js.erb +25 -0
  42. data/app/views/spree/shared/trackers/google_analytics/_page_viewed.js.erb +13 -0
  43. data/app/views/spree/shared/trackers/google_analytics/_purchase.js.erb +22 -0
  44. data/app/views/spree/shared/trackers/segment/_cart_viewed.js.erb +27 -0
  45. data/app/views/spree/shared/trackers/segment/_checkout_step_viewed.js.erb +21 -0
  46. data/app/views/spree/shared/trackers/segment/_initializer.js.erb +7 -0
  47. data/app/views/spree/shared/trackers/segment/_order_complete.js.erb +11 -0
  48. data/app/views/spree/shared/trackers/segment/_page_viewed.js.erb +7 -0
  49. data/app/views/spree/shared/trackers/segment/_product_list_filtered.js.erb +23 -0
  50. data/app/views/spree/shared/trackers/segment/_product_list_viewed.js.erb +19 -0
  51. data/app/views/spree/shared/trackers/segment/_product_viewed.js.erb +12 -0
  52. data/app/views/spree/shared/trackers/segment/_products_searched.js.erb +10 -0
  53. data/bin/rails +7 -0
  54. data/config/locales/en.yml +18 -0
  55. data/config/locales/zh-TW.yml +19 -0
  56. data/config/routes.rb +5 -0
  57. data/db/migrate/20171013160337_create_spree_trackers.rb +20 -0
  58. data/db/migrate/20200721163729_add_store_id_to_spree_trackers.rb +14 -0
  59. data/gemfiles/.bundle/config +2 -0
  60. data/gemfiles/spree_4_1.gemfile +9 -0
  61. data/gemfiles/spree_master.gemfile +9 -0
  62. data/lib/generators/spree_analytics_trackers/install/install_generator.rb +28 -0
  63. data/lib/spree_analytics_trackers.rb +5 -0
  64. data/lib/spree_analytics_trackers/engine.rb +20 -0
  65. data/lib/spree_analytics_trackers/factories.rb +10 -0
  66. data/lib/spree_analytics_trackers/version.rb +17 -0
  67. data/spec/features/admin/configuration/analytics_tracker_spec.rb +61 -0
  68. data/spec/features/products_spec.rb +315 -0
  69. data/spec/fixtures/thinking-cat.jpg +0 -0
  70. data/spec/models/spree/tracker_spec.rb +32 -0
  71. data/spec/presenters/spree_analytics_trackers/segment/order_presenter_spec.rb +34 -0
  72. data/spec/spec_helper.rb +18 -0
  73. data/spec/support/custom_products.rb +26 -0
  74. data/spree_analytics_trackers.gemspec +31 -0
  75. metadata +98 -200
@@ -0,0 +1,13 @@
1
+ <% if ga_enabled? %>
2
+ <script>
3
+ if (typeof gtag !== 'undefined') {
4
+ <% if user_id = try_spree_current_user.try(:id) %>
5
+ gtag('config', '<%= ga_tracker.analytics_id %>', {
6
+ 'user_id': '<%= user_id %>'
7
+ });
8
+ <% else %>
9
+ gtag('config', '<%= ga_tracker.analytics_id %>');
10
+ <% end %>
11
+ }
12
+ </script>
13
+ <% end %>
@@ -0,0 +1,22 @@
1
+ <% if ga_enabled? && @order.present? && order_just_completed?(@order) %>
2
+ <script>
3
+ window.addEventListener('turbolinks:load', function() {
4
+ if (typeof gtag !== 'undefined') {
5
+ clearGAplugins();
6
+ gtag('event', 'purchase', {
7
+ transaction_id: '<%= @order.number %>',
8
+ value: <%= @order.total&.to_f %>,
9
+ currency: '<%= @order.currency %>',
10
+ tax: <%= @order.tax_total&.to_f %>,
11
+ shipping: <%= @order.shipment_total&.to_f %>,
12
+ coupon: '<%= @order.promo_code %>',
13
+ items: [
14
+ <% @order.line_items.each do |line_item| %>
15
+ <%= ga_line_item(line_item) %>,
16
+ <% end %>
17
+ ]
18
+ });
19
+ }
20
+ });
21
+ </script>
22
+ <% end %>
@@ -0,0 +1,27 @@
1
+ <% if segment_enabled? && @order.present? && !@order.complete? %>
2
+ <script>
3
+ window.addEventListener('turbolinks:load', function() {
4
+ if (typeof analytics !== 'undefined') {
5
+ var segmentCartJson = {
6
+ cart_id: '<%= @order.number %>',
7
+ products: [
8
+ <% @order.line_items.each_with_index do |line_item, index| %>
9
+ <%=
10
+ product_for_segment(
11
+ line_item.product,
12
+ position: index+1,
13
+ quantity: line_item.quantity,
14
+ image: default_image_for_product_or_variant(line_item.variant)
15
+ )
16
+ %>,
17
+ <% end %>
18
+ ]
19
+ }
20
+
21
+ analytics.track('Cart Viewed', segmentCartJson);
22
+ analytics.page('Cart Viewed', segmentCartJson);
23
+ }
24
+ });
25
+ </script>
26
+ <% end %>
27
+
@@ -0,0 +1,21 @@
1
+ <% if segment_enabled? && @order.present? && !@order.completed? %>
2
+ <script>
3
+ window.addEventListener('turbolinks:load', function() {
4
+ if (typeof analytics !== 'undefined') {
5
+ analytics.track('Checkout Step Viewed',
6
+ {
7
+ checkout_id: '<%= @order.number %>',
8
+ step: '<%= (@order.checkout_steps.index(@order.state) + 1) %>'
9
+ }
10
+ );
11
+
12
+ analytics.page('Checkout Step Viewed',
13
+ {
14
+ checkout_id: '<%= @order.number %>',
15
+ step: '<%= (@order.checkout_steps.index(@order.state) + 1) %>'
16
+ }
17
+ );
18
+ }
19
+ });
20
+ </script>
21
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <% if segment_enabled? %>
2
+ <script>
3
+ !function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on"];analytics.factory=function(t){return function(){var e=Array.prototype.slice.call(arguments);e.unshift(t);analytics.push(e);return analytics}};for(var t=0;t<analytics.methods.length;t++){var e=analytics.methods[t];analytics[e]=analytics.factory(e)}analytics.load=function(t,e){var n=document.createElement("script");n.type="text/javascript";n.async=!0;n.src="https://cdn.segment.com/analytics.js/v1/"+t+"/analytics.min.js";var a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(n,a);analytics._loadOptions=e};analytics.SNIPPET_VERSION="4.1.0";
4
+ analytics.load('<%=segment_tracker.analytics_id%>'.replace(/^\s+|\s+$/g, ''));
5
+ }}();
6
+ </script>
7
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <% if segment_enabled? && @order.present? && order_just_completed?(@order) %>
2
+ <% order_json = SpreeAnalyticsTrackers::Segment::OrderPresenter.new(@order).to_json %>
3
+ <script>
4
+ window.addEventListener('turbolinks:load', function() {
5
+ if (typeof analytics !== 'undefined') {
6
+ analytics.page('Order Completed', <%= order_json.html_safe %>);
7
+ analytics.track('Order Completed', <%= order_json.html_safe %>);
8
+ }
9
+ });
10
+ </script>
11
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <% if segment_enabled? %>
2
+ <script>
3
+ if (typeof analytics !== 'undefined') {
4
+ analytics.page();
5
+ }
6
+ </script>
7
+ <% end %>
@@ -0,0 +1,23 @@
1
+ <% if segment_enabled? && any_filtering_params? %>
2
+ <script>
3
+ window.addEventListener('turbolinks:load', function() {
4
+ if (typeof analytics !== 'undefined') {
5
+ var segmentProductListFilteredJson = {
6
+ filters: [
7
+ <% filtering_params_with_values.each do |type, value| %>
8
+ <%= { type: type, value: value } .to_json.html_safe %>,
9
+ <% end %>
10
+ ],
11
+ products: [
12
+ <% @products.each_with_index do |product, index| %>
13
+ <%= product_for_segment(product, position: index + 1) %>,
14
+ <% end %>
15
+ ]
16
+ }
17
+
18
+ analytics.track('Product List Filtered', segmentProductListFilteredJson);
19
+ analytics.page('Product List Filtered', segmentProductListFilteredJson);
20
+ }
21
+ });
22
+ </script>
23
+ <% end %>
@@ -0,0 +1,19 @@
1
+ <% if segment_enabled? && defined?(products) && products.present? %>
2
+ <script>
3
+ window.addEventListener('turbolinks:load', function() {
4
+ if (typeof analytics !== 'undefined') {
5
+ var segmentProductListViewedJson = {
6
+ category: '<%= @taxon&.name %>',
7
+ products: [
8
+ <% products.each_with_index do |product, index| %>
9
+ <%= product_for_segment(product, position: index+1, image: default_image_for_product_or_variant(product)) %>,
10
+ <% end %>
11
+ ]
12
+ }
13
+
14
+ analytics.track('Product List Viewed', segmentProductListViewedJson);
15
+ analytics.page('Product List Viewed', segmentProductListViewedJson);
16
+ }
17
+ });
18
+ </script>
19
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <% if segment_enabled? && @product.present? %>
2
+ <script>
3
+ window.addEventListener('turbolinks:load', function() {
4
+ if (typeof analytics !== 'undefined') {
5
+ var segmentProductViewed = <%= product_for_segment(@product, image: default_image_for_product_or_variant(@product)) %>;
6
+
7
+ analytics.page('Product Viewed', segmentProductViewed);
8
+ analytics.track('Product Viewed', segmentProductViewed);
9
+ }
10
+ });
11
+ </script>
12
+ <% end %>
@@ -0,0 +1,10 @@
1
+ <% if segment_enabled? && params.key?(:keywords) && params[:keywords].present? %>
2
+ <script>
3
+ window.addEventListener('turbolinks:load', function() {
4
+ if (typeof analytics !== 'undefined') {
5
+ analytics.track('Products Searched', { query: '<%= params[:keywords] %>' });
6
+ analytics.page('Products Searched', { query: '<%= params[:keywords] %>' });
7
+ }
8
+ });
9
+ </script>
10
+ <% end %>
@@ -0,0 +1,7 @@
1
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
2
+
3
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
4
+ ENGINE_PATH = File.expand_path('../../lib/spree_analytics_trackers/engine', __FILE__)
5
+
6
+ require 'rails/all'
7
+ require 'rails/engine/commands'
@@ -0,0 +1,18 @@
1
+ en:
2
+ activerecord:
3
+ attributes:
4
+ spree/tracker:
5
+ one: Tracker
6
+ other: Trackers
7
+ spree:
8
+ analytics_engine: Analytics Engine
9
+ analytics_trackers: Analytics Trackers
10
+ new_tracker: New Tracker
11
+ google_analytics: Google Analytics
12
+ google_analytics_id: Analytics ID
13
+ analytics_desc_header_1: Spree Analytics
14
+ analytics_desc_header_2: Live analytics integrated into your Spree dashboard
15
+ analytics_desc_list_1: Get live sales information as it happens
16
+ analytics_desc_list_2: Requires only a free Spree account to activate
17
+ analytics_desc_list_3: Absolutely no code to install
18
+ analytics_desc_list_4: It's completely free!
@@ -0,0 +1,19 @@
1
+ zh-TW:
2
+ activerecord:
3
+ attributes:
4
+ spree/tracker:
5
+ one: 追蹤器
6
+ other: 追蹤器
7
+ spree:
8
+ analytics_engine: 分析引擎
9
+ analytics_trackers: 分析追蹤器
10
+ new_tracker: 新增追蹤器
11
+ google_analytics: Google 分析
12
+ google_analytics_id: Google 追蹤器代碼
13
+ analytics_desc_header_1: Spree 分析
14
+ analytics_desc_header_2: 新增即時分析到 Spree 的後台
15
+ analytics_desc_list_1: 獲得即時銷售數據
16
+ analytics_desc_list_2: 免費安裝
17
+ analytics_desc_list_3: 不需要寫任何代碼安裝
18
+ analytics_desc_list_4: 完全免費!
19
+
@@ -0,0 +1,5 @@
1
+ Spree::Core::Engine.add_routes do
2
+ namespace :admin, path: Spree.admin_path do
3
+ resources :trackers
4
+ end
5
+ end
@@ -0,0 +1,20 @@
1
+ class CreateSpreeTrackers < SpreeExtension::Migration[4.2]
2
+ def change
3
+ if table_exists?(:spree_trackers)
4
+ add_index :spree_trackers, :active unless index_exists?(:spree_trackers, :active)
5
+ remove_column :spree_trackers, :environment if column_exists?(:spree_trackers, :environment)
6
+ unless column_exists?(:spree_trackers, :engine)
7
+ add_column :spree_trackers, :kind, :integer, default: 0, null: false, index: true unless column_exists?(:spree_trackers, :kind)
8
+ rename_column :spree_trackers, :kind, :engine if column_exists?(:spree_trackers, :kind)
9
+ end
10
+ else
11
+ create_table :spree_trackers do |t|
12
+ t.string 'analytics_id'
13
+ t.boolean 'active', default: true, index: true
14
+ t.datetime 'created_at', null: false
15
+ t.datetime 'updated_at', null: false
16
+ t.integer 'engine', default: 0, null: false, index: true
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ class AddStoreIdToSpreeTrackers < ActiveRecord::Migration[6.0]
2
+ def change
3
+ add_column :spree_trackers, :store_id, :integer
4
+ add_index :spree_trackers, :store_id
5
+
6
+ Spree::Tracker.reset_column_information
7
+
8
+ default_store = Spree::Store.default
9
+ Spree::Tracker.all.each do |tracker|
10
+ tracker.store = default_store
11
+ tracker.save
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_RETRY: "1"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "spree", "~> 4.1.0"
6
+ gem "spree_auth_devise", "~> 4.1.0"
7
+ gem "rails-controller-testing"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "spree", github: "spree/spree", branch: "master"
6
+ gem "spree_auth_devise", github: "spree/spree_auth_devise", branch: "master"
7
+ gem "rails-controller-testing"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,28 @@
1
+ module SpreeAnalyticsTrackers
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ class_option :auto_run_migrations, type: :boolean, default: false
5
+
6
+ def add_javascripts
7
+ javascripts_path = 'vendor/assets/javascripts/spree/frontend/all.js'
8
+ return unless File.file?(javascripts_path)
9
+
10
+ append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/add_to_cart_analytics\n"
11
+ append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/remove_from_cart_analytics\n"
12
+ end
13
+
14
+ def add_migrations
15
+ run 'bundle exec rake railties:install:migrations FROM=spree_analytics_trackers'
16
+ end
17
+
18
+ def run_migrations
19
+ run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask 'Would you like to run the migrations now? [Y/n]')
20
+ if run_migrations
21
+ run 'bundle exec rake db:migrate'
22
+ else
23
+ puts 'Skipping rake db:migrate, don\'t forget to run it!'
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ require 'spree_core'
2
+ require 'spree_extension'
3
+ require 'spree_analytics_trackers/engine'
4
+ require 'spree_analytics_trackers/version'
5
+ require 'deface'
@@ -0,0 +1,20 @@
1
+ module SpreeAnalyticsTrackers
2
+ class Engine < Rails::Engine
3
+ require 'spree/core'
4
+ isolate_namespace Spree
5
+ engine_name 'spree_analytics_trackers'
6
+
7
+ # use rspec for tests
8
+ config.generators do |g|
9
+ g.test_framework :rspec
10
+ end
11
+
12
+ def self.activate
13
+ Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
14
+ Rails.configuration.cache_classes ? require(c) : load(c)
15
+ end
16
+ end
17
+
18
+ config.to_prepare &method(:activate).to_proc
19
+ end
20
+ end
@@ -0,0 +1,10 @@
1
+ unless FactoryBot.factories.registered?(:tracker)
2
+ FactoryBot.define do
3
+ factory :tracker, class: Spree::Tracker do
4
+ analytics_id { 'A100' }
5
+ active { true }
6
+ engine { :google_analytics }
7
+ store { Spree::Store.default || create(:store) }
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ module SpreeAnalyticsTrackers
2
+ module_function
3
+
4
+ # Returns the version of the currently loaded SpreeAnalyticsTrackers as a
5
+ # <tt>Gem::Version</tt>.
6
+ def version
7
+ Gem::Version.new VERSION::STRING
8
+ end
9
+
10
+ module VERSION
11
+ MAJOR = 2
12
+ MINOR = 1
13
+ TINY = 0
14
+
15
+ STRING = [MAJOR, MINOR, TINY].compact.join('.')
16
+ end
17
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Analytics Tracker', type: :feature do
4
+ stub_authorization!
5
+
6
+ let!(:default_store) { Spree::Store.default }
7
+ let!(:another_store) { create(:store, name: 'Eurozone') }
8
+
9
+ context 'index' do
10
+ before(:each) do
11
+ create(:tracker, store: default_store)
12
+ create(:tracker, analytics_id: 'A200', store: another_store)
13
+ visit spree.admin_trackers_path
14
+ end
15
+
16
+ it 'should have the right content' do
17
+ expect(page).to have_content('Analytics Trackers')
18
+ end
19
+
20
+ it 'should have the right tabular values displayed' do
21
+ within_row(1) do
22
+ expect(column_text(1)).to eq('A100')
23
+ expect(column_text(2)).to eq('Google analytics')
24
+ expect(column_text(3)).to eq(default_store.name)
25
+ expect(column_text(4)).to eq('Yes')
26
+ end
27
+
28
+ within_row(2) do
29
+ expect(column_text(1)).to eq('A200')
30
+ expect(column_text(2)).to eq('Google analytics')
31
+ expect(column_text(3)).to eq('Eurozone')
32
+ expect(column_text(4)).to eq('Yes')
33
+ end
34
+ end
35
+ end
36
+
37
+ context 'create' do
38
+ before(:each) do
39
+ visit spree.admin_trackers_path
40
+ end
41
+
42
+ it 'should be able to create a new analytics tracker', js: true do
43
+ within('.content-header') do
44
+ click_link 'admin_new_tracker_link'
45
+ end
46
+ fill_in 'tracker_analytics_id', with: 'A100'
47
+
48
+ select2 default_store.name, from: 'Store'
49
+
50
+ click_button 'Create'
51
+
52
+ expect(page).to have_content('successfully created!')
53
+ within_row(1) do
54
+ expect(column_text(1)).to eq('A100')
55
+ expect(column_text(2)).to eq('Google analytics')
56
+ expect(column_text(3)).to eq(default_store.name)
57
+ expect(column_text(4)).to eq('Yes')
58
+ end
59
+ end
60
+ end
61
+ end