spree_piwik 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +14 -0
  3. data/.hound.yml +2 -0
  4. data/.rspec +1 -0
  5. data/.travis.yml +4 -0
  6. data/Gemfile +7 -0
  7. data/README.md +41 -0
  8. data/Rakefile +21 -0
  9. data/app/assets/javascripts/spree/backend/spree_piwik.js +2 -0
  10. data/app/assets/javascripts/spree/frontend/spree_piwik.js +2 -0
  11. data/app/assets/stylesheets/spree/backend/spree_piwik.css +4 -0
  12. data/app/assets/stylesheets/spree/frontend/spree_piwik.css +4 -0
  13. data/app/helpers/spree/base_helper_decorator.rb +5 -0
  14. data/app/models/spree/piwik/client.rb +29 -0
  15. data/app/models/spree/piwik/line_item.rb +44 -0
  16. data/app/models/spree/piwik_configuration.rb +4 -0
  17. data/app/overrides/add_piwik_tracking_tag_to_layout.rb +5 -0
  18. data/app/views/spree/_ecommerce_item.html.erb +7 -0
  19. data/app/views/spree/_ecommerce_view.html.erb +6 -0
  20. data/app/views/spree/_piwik.html.erb +19 -0
  21. data/bin/rails +7 -0
  22. data/config/.jshint.json +3 -0
  23. data/config/locales/en.yml +5 -0
  24. data/config/routes.rb +3 -0
  25. data/config/spring.rb +1 -0
  26. data/lib/generators/spree_piwik/install/install_generator.rb +32 -0
  27. data/lib/spree/piwik/engine.rb +32 -0
  28. data/lib/spree_piwik.rb +2 -0
  29. data/spec/features/interact_with_cart_spec.rb +49 -0
  30. data/spec/features/visit_home_pages_spec.rb +21 -0
  31. data/spec/features/visit_product_page_spec.rb +45 -0
  32. data/spec/fixtures/empty_cart.js +9 -0
  33. data/spec/fixtures/index_example_com.js +8 -0
  34. data/spec/fixtures/multiple_products_in_cart.js +23 -0
  35. data/spec/fixtures/one_product_in_cart.js +16 -0
  36. data/spec/fixtures/product_example_com.js +14 -0
  37. data/spec/fixtures/product_with_categories.js +14 -0
  38. data/spec/helpers/spree/base_helper_decorator_spec.rb +35 -0
  39. data/spec/models/spree/piwik/client_spec.rb +71 -0
  40. data/spec/models/spree/piwik/line_item_spec.rb +59 -0
  41. data/spec/models/spree/piwik_configuration_spec.rb +11 -0
  42. data/spec/overrides/add_piwik_tracking_tag_to_layout_spec.rb +20 -0
  43. data/spec/spec_helper.rb +96 -0
  44. data/spec/support/components/piwig_tag_component.rb +37 -0
  45. data/spec/support/helpers/fixture_helpers.rb +11 -0
  46. data/spree_piwik.gemspec +35 -0
  47. metadata +370 -0
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MmM0NWQ2NmY5ZTQ5YmUzZjU0MzViYzYxYzA4ZTZlMWY3Nzc4MWQxMA==
5
+ data.tar.gz: !binary |-
6
+ MjI0OWI2ZTJiZWNjNGFiZjE3ZGI3M2ZkNWY4NzZjOGM0ODVhNGJjOQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZDg4MmJmZjI2ZDQyNmVkNmRmMDVjMDFhMjJjNjk0MzAzNTliNTFkYjY4Yjgx
10
+ NzYyOWEzMzNkMTYzYWRkNWNiZGM5N2QwMThhNzJlYzg2OTkwYWMxNjVkZDQz
11
+ MzliZjI3Mzg4MGRmODIxNjVmMjFlZTdiN2M5ZTFiODY0MjQzYWI=
12
+ data.tar.gz: !binary |-
13
+ YmZlYTBiNGRjYzM3OTBlY2ZjOTVkMWU1MTRmYmI0NzE3MDVmNDJkNzJlZmQx
14
+ M2EwOWVlMjljNThhZmNkMWNkODc4ZWI1YTU2MWI4OGZjNTMzZGQ3OGQwYTk1
15
+ ZDJhOTRkMDQwOGU4OTUxODBmNGMwM2MwNjE0ZWU3ZDc3NTA1ZDM=
@@ -0,0 +1,14 @@
1
+ \#*
2
+ *~
3
+ .#*
4
+ .DS_Store
5
+ .idea
6
+ .project
7
+ .sass-cache
8
+ coverage
9
+ Gemfile.lock
10
+ tmp
11
+ nbproject
12
+ pkg
13
+ *.swp
14
+ spec/dummy
@@ -0,0 +1,2 @@
1
+ java_script:
2
+ config_file: config/.jshint.json
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1
4
+ - 2.2
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'spree', github: 'spree/spree', branch: '2-3-stable'
4
+ # Provides basic authentication functionality for testing parts of your engine
5
+ gem 'spree_auth_devise', github: 'spree/spree_auth_devise', branch: '2-3-stable'
6
+
7
+ gemspec
@@ -0,0 +1,41 @@
1
+ SpreePiwik
2
+ ==========
3
+
4
+ Adds piwik tracking with Ecommerce tracking
5
+
6
+ Status
7
+ ------
8
+
9
+ [![Build Status](https://travis-ci.org/berkes/spree_piwik.svg?branch=develop)](https://travis-ci.org/berkes/spree_piwik)
10
+ [![Code Climate](https://codeclimate.com/github/berkes/spree_piwik/badges/gpa.svg)](https://codeclimate.com/github/berkes/spree_piwik)
11
+
12
+ Installation
13
+ ------------
14
+
15
+ Add spree_piwik to your Gemfile:
16
+
17
+ ```ruby
18
+ gem 'spree_piwik'
19
+ ```
20
+
21
+ Bundle your dependencies and run the installation generator:
22
+
23
+ ```shell
24
+ bundle
25
+ bundle exec rails g spree_piwik:install
26
+ ```
27
+
28
+ Testing
29
+ -------
30
+
31
+ First bundle your dependencies, then run `rake`. `rake` will default to building the dummy app if it does not exist, then it will run specs. The dummy app can be regenerated by using `rake test_app`.
32
+
33
+ ```shell
34
+ bundle
35
+ bundle exec rake
36
+ ```
37
+
38
+ When testing your applications integration with this extension you may use it's factories.
39
+ Simply add this require statement to your spec_helper:
40
+
41
+ Copyright (c) 2015 Bèr Kessels
@@ -0,0 +1,21 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ require 'spree/testing_support/extension_rake'
6
+
7
+ RSpec::Core::RakeTask.new
8
+
9
+ task :default do
10
+ if Dir["spec/dummy"].empty?
11
+ Rake::Task[:test_app].invoke
12
+ Dir.chdir("../../")
13
+ end
14
+ Rake::Task[:spec].invoke
15
+ end
16
+
17
+ desc 'Generates a dummy app for testing'
18
+ task :test_app do
19
+ ENV['LIB_NAME'] = 'spree_piwik'
20
+ Rake::Task['extension:test_app'].invoke
21
+ end
@@ -0,0 +1,2 @@
1
+ // Placeholder manifest file.
2
+ // the installer will append this file to the app vendored assets here: vendor/assets/javascripts/spree/backend/all.js'
@@ -0,0 +1,2 @@
1
+ // Placeholder manifest file.
2
+ // the installer will append this file to the app vendored assets here: vendor/assets/javascripts/spree/frontend/all.js'
@@ -0,0 +1,4 @@
1
+ /*
2
+ Placeholder manifest file.
3
+ the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/backend/all.css'
4
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Placeholder manifest file.
3
+ the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/frontend/all.css'
4
+ */
@@ -0,0 +1,5 @@
1
+ Spree::BaseHelper.class_eval do
2
+ def piwik_client
3
+ @piwik_client ||= Spree::Piwik::Client.new(product: @product, order: @order)
4
+ end
5
+ end
@@ -0,0 +1,29 @@
1
+ module Spree
2
+ module Piwik
3
+ class Client
4
+ include ActiveModel::Model
5
+
6
+ attr_accessor :product, :order
7
+
8
+ def product
9
+ Spree::Piwik::LineItem.from_product_ish(@product) if @product
10
+ end
11
+
12
+ def url
13
+ Spree::Piwik::Config.preferred_piwik_url
14
+ end
15
+
16
+ def site_id
17
+ Spree::Piwik::Config.preferred_piwik_id
18
+ end
19
+
20
+ def ecommerce_items
21
+ @order.line_items.map { |li| LineItem.from_product_ish(li) }
22
+ end
23
+
24
+ def track_cart_update?
25
+ !@order.nil? && @order.cart?
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,44 @@
1
+ module Spree
2
+ module Piwik
3
+ class LineItem
4
+ include ActiveModel::Model
5
+
6
+ ATTRIBUTES = [:sku, :name, :price, :quantity, :categories]
7
+ attr_accessor(*ATTRIBUTES)
8
+
9
+ ##
10
+ # Create a LineItem from a Spree::LinteItem-ish thing (line_items,
11
+ # products, variants)
12
+ #
13
+ # All attributes in ATTRIBUTES will be mapped:
14
+ #
15
+ # When we add a (private) method with the name
16
+ # map_product_ish_sku, this method will be called and get the
17
+ # product_ish to generate a value for that attribute.
18
+ #
19
+ # When the product_ish has the same attribute name and no
20
+ # mapping-method provided the value, it will simply use the value
21
+ # from that attribute on product_ish
22
+ def self.from_product_ish(product_ish)
23
+ values = ATTRIBUTES.map { |a| [a, product_ish_attr(a, product_ish)] }
24
+ new(Hash[values])
25
+ end
26
+
27
+ # Private
28
+ def self.product_ish_attr(attr_name, product_ish)
29
+ mapping_method = "map_product_ish_#{attr_name}".to_sym
30
+ if respond_to?(mapping_method)
31
+ send(mapping_method, product_ish)
32
+ else
33
+ product_ish.try(attr_name)
34
+ end
35
+ end
36
+
37
+ # Private
38
+ def self.map_product_ish_categories(product_ish)
39
+ defaulted_taxons = product_ish.try(:taxons) || []
40
+ defaulted_taxons.map(&:name).first(5)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,4 @@
1
+ class Spree::PiwikConfiguration < Spree::Preferences::Configuration
2
+ preference :piwik_url, :string, :default => "example.com"
3
+ preference :piwik_id, :integer, :default => 0
4
+ end
@@ -0,0 +1,5 @@
1
+ Deface::Override.new(
2
+ :virtual_path => 'spree/layouts/spree_application',
3
+ :name => 'add_piwik_tracking_tag_to_layout',
4
+ :insert_bottom => "[data-hook='body']",
5
+ :partial => 'spree/piwik')
@@ -0,0 +1,7 @@
1
+ _paq.push(['addEcommerceItem',
2
+ '<%= ecommerce_item.sku %>',
3
+ '<%= ecommerce_item.name %>',
4
+ <%= raw ecommerce_item.categories.to_json %>,
5
+ <%= ecommerce_item.price %>,
6
+ <%= ecommerce_item.quantity %>
7
+ ]);
@@ -0,0 +1,6 @@
1
+ _paq.push(['setEcommerceView',
2
+ '<%= product.sku %>',
3
+ '<%= product.name %>',
4
+ <%= raw product.categories.to_json %>,
5
+ <%= product.price %>
6
+ ]);
@@ -0,0 +1,19 @@
1
+ <!-- Piwik -->
2
+ <script type="text/javascript">
3
+ var _paq = _paq || [];
4
+ (function(){ var u=(('https:' == document.location.protocol) ? 'https://<%= piwik_client.url %>/' : 'http://<%= piwik_client.url %>/');
5
+ _paq.push(['setSiteId', <%= piwik_client.site_id %>]);
6
+ _paq.push(['setTrackerUrl', u+'piwik.php']);
7
+ <%- if piwik_client.product %>
8
+ <%= render partial: 'spree/ecommerce_view', locals: { product: piwik_client.product } %>
9
+ <% end %>
10
+ <% if piwik_client.track_cart_update? %>
11
+ <%= render :partial => 'spree/ecommerce_item', :collection => piwik_client.ecommerce_items %>
12
+ _paq.push(['trackEcommerceCartUpdate', <%= piwik_client.order.total %>]);
13
+ <% end %>
14
+ _paq.push(['trackPageView']);
15
+ _paq.push(['enableLinkTracking']);
16
+ var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.type='text/javascript'; g.defer=true; g.async=true; g.src=u+'piwik.js';
17
+ s.parentNode.insertBefore(g,s); })();
18
+ </script>
19
+ <!-- End Piwik Code -->
@@ -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_piwik/engine', __FILE__)
5
+
6
+ require 'rails/all'
7
+ require 'rails/engine/commands'
@@ -0,0 +1,3 @@
1
+ {
2
+ "maxlen": 110
3
+ }
@@ -0,0 +1,5 @@
1
+ # Sample localization file for English. Add more files in this directory for other locales.
2
+ # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3
+
4
+ en:
5
+ hello: "Hello world"
@@ -0,0 +1,3 @@
1
+ Spree::Core::Engine.routes.draw do
2
+ # Add your extension routes here
3
+ end
@@ -0,0 +1 @@
1
+ Spring.application_root = File.join('spec', 'dummy')
@@ -0,0 +1,32 @@
1
+ # Must be named SpreePiwik because dummygenerator expects it
2
+ module SpreePiwik
3
+ module Generators
4
+ class InstallGenerator < Rails::Generators::Base
5
+
6
+ class_option :auto_run_migrations, :type => :boolean, :default => false
7
+
8
+ def add_javascripts
9
+ append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/spree_piwik\n"
10
+ append_file 'vendor/assets/javascripts/spree/backend/all.js', "//= require spree/backend/spree_piwik\n"
11
+ end
12
+
13
+ def add_stylesheets
14
+ inject_into_file 'vendor/assets/stylesheets/spree/frontend/all.css', " *= require spree/frontend/spree_piwik\n", :before => /\*\//, :verbose => true
15
+ inject_into_file 'vendor/assets/stylesheets/spree/backend/all.css', " *= require spree/backend/spree_piwik\n", :before => /\*\//, :verbose => true
16
+ end
17
+
18
+ def add_migrations
19
+ run 'bundle exec rake railties:install:migrations FROM=spree_piwik'
20
+ end
21
+
22
+ def run_migrations
23
+ run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask 'Would you like to run the migrations now? [Y/n]')
24
+ if run_migrations
25
+ run 'bundle exec rake db:migrate'
26
+ else
27
+ puts 'Skipping rake db:migrate, don\'t forget to run it!'
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,32 @@
1
+ module Spree
2
+ module Piwik
3
+ end
4
+ end
5
+
6
+
7
+ module Spree
8
+ module Piwik
9
+ class Engine < Rails::Engine
10
+ require 'spree/core'
11
+ isolate_namespace Spree
12
+ engine_name 'spree_piwik'
13
+
14
+ # use rspec for tests
15
+ config.generators do |g|
16
+ g.test_framework :rspec
17
+ end
18
+
19
+ initializer 'spree.piwik.environment', :before => :load_config_initializers do |app|
20
+ Spree::Piwik::Config = Spree::PiwikConfiguration.new
21
+ end
22
+
23
+ def self.activate
24
+ Dir.glob(File.join(File.dirname(__FILE__), '../../../app/**/*_decorator*.rb')) do |c|
25
+ Rails.configuration.cache_classes ? require(c) : load(c)
26
+ end
27
+ end
28
+
29
+ config.to_prepare &method(:activate).to_proc
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,2 @@
1
+ require 'spree_core'
2
+ require 'spree/piwik/engine'
@@ -0,0 +1,49 @@
1
+ require "spec_helper"
2
+
3
+ feature "InteractWithCart", type: :feature do
4
+ scenario "An anonymous user adds a product to the cart" do
5
+ @product = create(:product, sku: "SKU-1", name: "RoR Shirt")
6
+ visit "products/#{@product.slug}"
7
+ click_button "Add To Cart"
8
+
9
+ tag_component = PiwikTagComponent.new
10
+ expect(tag_component.as_ecma).to eq fixture("one_product_in_cart.js")
11
+ end
12
+
13
+ scenario "An anonymous user adds multiple products to the cart" do
14
+ @shirt = create(:product, sku: "SKU-1", name: "RoR Shirt")
15
+ visit "products/#{@shirt.slug}"
16
+ click_button "Add To Cart"
17
+ @sticker = create(:product, sku: "SKU-2", name: "RoR Sticker", price: 0.99)
18
+ visit "products/#{@sticker.slug}"
19
+ click_button "Add To Cart"
20
+
21
+ tag_component = PiwikTagComponent.new
22
+ expect(tag_component.as_ecma).to eq fixture("multiple_products_in_cart.js")
23
+ end
24
+
25
+ scenario "An anonymous user removes a product from cart" do
26
+ @shirt = create(:product, sku: "SKU-1", name: "RoR Shirt")
27
+ visit "products/#{@shirt.slug}"
28
+ click_button "Add To Cart"
29
+ @sticker = create(:product, sku: "SKU-2", name: "RoR Sticker", price: 0.99)
30
+ visit "products/#{@sticker.slug}"
31
+ click_button "Add To Cart"
32
+
33
+ fill_in(:order_line_items_attributes_1_quantity, with: 0)
34
+ click_button "Update"
35
+
36
+ tag_component = PiwikTagComponent.new
37
+ expect(tag_component.as_ecma).to eq fixture("one_product_in_cart.js")
38
+ end
39
+
40
+ scenario "An anonymous user empties a cart" do
41
+ @shirt = create(:product, sku: "SKU-1", name: "RoR Shirt")
42
+ visit "products/#{@shirt.slug}"
43
+ click_button "Add To Cart"
44
+ click_button "Empty Cart"
45
+
46
+ tag_component = PiwikTagComponent.new
47
+ expect(tag_component.as_ecma).to eq fixture("empty_cart.js")
48
+ end
49
+ end
@@ -0,0 +1,21 @@
1
+ require "spec_helper"
2
+
3
+ feature "TrackingTagForIndex", type: :feature do
4
+ before do
5
+ # User has set piwik_url and piwik_id
6
+ Spree::Piwik::Config.set(piwik_url: "piwik.example.com")
7
+ Spree::Piwik::Config.set(piwik_id: 1337)
8
+ end
9
+
10
+ scenario "An anonymous user is tracked on the home page" do
11
+ visit "/"
12
+
13
+ # Sanity check
14
+ expect(page).to have_content "Powered by Spree"
15
+
16
+ tag_component = PiwikTagComponent.new
17
+ expect(tag_component).to be_in_html
18
+ expect(tag_component.tracker_url_assigment).to include("piwik.example.com")
19
+ expect(tag_component.as_ecma).to eq fixture("index_example_com.js")
20
+ end
21
+ end
@@ -0,0 +1,45 @@
1
+ require "spec_helper"
2
+
3
+ feature "TrackingTagForProduct", type: :feature do
4
+ # As a shop-owner
5
+ # I want to send stats with the product"s attributes to piwik
6
+ # So that I can allow Piwik to process reports on SKU and products.
7
+ scenario "An anonymous user is tracked on a product page" do
8
+ @product = create(:product, sku: "SKU-1", name: "RoR Shirt")
9
+ # TODO replace with visit "product/slug"
10
+ visit "/"
11
+ click_link(@product.name)
12
+
13
+ # Sanity check
14
+ expect(page).to have_content @product.name
15
+
16
+ tag_component = PiwikTagComponent.new
17
+ expect(tag_component).to be_in_html
18
+ expect(tag_component.to_s).to include "setEcommerceView"
19
+ expect(tag_component.as_ecma).to eq fixture("product_example_com.js")
20
+ end
21
+
22
+ # As a shop-owner
23
+ # I want to send the categories in a product to piwik
24
+ # So that can filter stats in Piwik on categories
25
+ scenario "An anonymous user is tracked on a product page with categories" do
26
+ taxons = [
27
+ create(:taxon, name: "shirts"),
28
+ create(:taxon, name: "ror"),
29
+ create(:taxon, name: "webdev"),
30
+ ]
31
+ @product = create(:product, sku: "SKU-1", name: "RoR Shirt", taxons: taxons)
32
+
33
+ # TODO replace with visit "product/slug"
34
+ visit "/"
35
+ click_link(@product.name)
36
+
37
+ # Sanity check
38
+ expect(page).to have_content @product.name
39
+
40
+ tag_component = PiwikTagComponent.new
41
+ expect(tag_component).to be_in_html
42
+ expect(tag_component.to_s).to include "setEcommerceView"
43
+ expect(tag_component.as_ecma).to eq fixture("product_with_categories.js")
44
+ end
45
+ end