spree_piwik 0.0.1

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.
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