spree_analytics_trackers 1.0.0 → 1.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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +21 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +29 -0
  5. data/Appraisals +17 -0
  6. data/Gemfile +7 -0
  7. data/LICENSE +26 -0
  8. data/README.md +73 -0
  9. data/Rakefile +21 -0
  10. data/app/controllers/spree/admin/trackers_controller.rb +6 -0
  11. data/app/controllers/spree/store_controller_decorator.rb +5 -0
  12. data/app/helpers/spree/trackers_helper.rb +23 -0
  13. data/app/models/spree/line_item_decorator.rb +6 -0
  14. data/app/models/spree/order_decorator.rb +7 -0
  15. data/app/models/spree/product_decorator.rb +11 -0
  16. data/app/models/spree/tracker.rb +25 -0
  17. data/app/models/spree/user_decorator.rb +5 -0
  18. data/app/overrides/add_analytics_to_admin_sidebar.rb +9 -0
  19. data/app/overrides/add_cart_viewed_to_orders_edit.rb +10 -0
  20. data/app/overrides/add_checkout_step_viewed_to_checkout_edit.rb +10 -0
  21. data/app/overrides/add_google_analytics_to_spree_application.rb +9 -0
  22. data/app/overrides/add_google_checkout_to_checkout_edit.rb +9 -0
  23. data/app/overrides/add_google_purchase_to_orders_show.rb +9 -0
  24. data/app/overrides/add_order_complete_to_orders_show.rb +10 -0
  25. data/app/overrides/add_product_added_to_orders_edit.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/views/spree/admin/shared/_analytics_sidebar_menu.html.erb +5 -0
  32. data/app/views/spree/admin/trackers/_form.html.erb +36 -0
  33. data/app/views/spree/admin/trackers/edit.html.erb +13 -0
  34. data/app/views/spree/admin/trackers/index.html.erb +44 -0
  35. data/app/views/spree/admin/trackers/new.html.erb +13 -0
  36. data/app/views/spree/shared/_google_add_items.html.erb +13 -0
  37. data/app/views/spree/shared/_google_analytics.html.erb +18 -0
  38. data/app/views/spree/shared/_google_checkout.html.erb +14 -0
  39. data/app/views/spree/shared/_google_purchase.html.erb +19 -0
  40. data/app/views/spree/shared/trackers/segment/_cart_viewed.js.erb +22 -0
  41. data/app/views/spree/shared/trackers/segment/_checkout_step_viewed.js.erb +12 -0
  42. data/app/views/spree/shared/trackers/segment/_initializer.js.erb +8 -0
  43. data/app/views/spree/shared/trackers/segment/_order_complete.js.erb +48 -0
  44. data/app/views/spree/shared/trackers/segment/_product_added.js.erb +9 -0
  45. data/app/views/spree/shared/trackers/segment/_product_list_filtered.js.erb +32 -0
  46. data/app/views/spree/shared/trackers/segment/_product_list_viewed.js.erb +22 -0
  47. data/app/views/spree/shared/trackers/segment/_product_viewed.js.erb +6 -0
  48. data/app/views/spree/shared/trackers/segment/_products_searched.js.erb +12 -0
  49. data/bin/rails +7 -0
  50. data/config/locales/en.yml +20 -0
  51. data/config/routes.rb +5 -0
  52. data/db/migrate/20171013160337_create_spree_trackers.rb +20 -0
  53. data/gemfiles/.bundle/config +2 -0
  54. data/gemfiles/spree_3_5.gemfile +9 -0
  55. data/gemfiles/spree_3_7.gemfile +9 -0
  56. data/gemfiles/spree_master.gemfile +10 -0
  57. data/lib/generators/spree_analytics_trackers/install/install_generator.rb +20 -0
  58. data/lib/spree_analytics_trackers.rb +4 -0
  59. data/lib/spree_analytics_trackers/engine.rb +20 -0
  60. data/lib/spree_analytics_trackers/factories.rb +9 -0
  61. data/lib/spree_analytics_trackers/version.rb +17 -0
  62. data/spec/features/admin/configuration/analytics_tracker_spec.rb +50 -0
  63. data/spec/models/spree/tracker_spec.rb +17 -0
  64. data/spec/spec_helper.rb +93 -0
  65. data/spree_analytics_trackers.gemspec +44 -0
  66. metadata +75 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48c88ea88bf027af900280e3c8b35acf53278b197fd1b5d52009a6c281745727
4
- data.tar.gz: 93b920ab1f3a819701c598af63a6714986d90653f524dabf49610b1079c20e78
3
+ metadata.gz: 73068544992b2ab73b42d2d9973c61217f2e9006b1eeaf82bb487d0c06694253
4
+ data.tar.gz: b5487f54f759141810e350a7aabe3c89870a2586d0f79c234c02a9a2f962c96d
5
5
  SHA512:
6
- metadata.gz: 8a4693c38576c0bd279305648343fbe87d82be9d0583a94e4c1368266fb8374377e414006aa7eed9f919d9d4ca2a06f3515f3bc6e382158a2937cb68b4f9e561
7
- data.tar.gz: 758080aec33605cfb61d6517ff477b0db916fb6bea5ecba2609ca655d61a338d3edf8cba8fe0fc75cb12b3edd1aa070cc702c60399d46e20c3ed4472ab5ffc94
6
+ metadata.gz: 961549e600a21cb31dad51459269e95f304c2540ad6e4603780a74548e77331507f0ffa9b7e64663915f9ade40107422e105ec4ab3e735b22c269176b7cb0ba6
7
+ data.tar.gz: bb01db4431806581753611af24c40f6bfe60b9454c063a3907676bbecce138a2c5260a4e52290cecb9c05cb65e448c32056d6d91da7714b7250d7fd3987a4d22
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ \#*
2
+ *~
3
+ .#*
4
+ .DS_Store
5
+ .idea
6
+ .localeapp/locales
7
+ .project
8
+ coverage
9
+ default
10
+ Gemfile.lock
11
+ tmp
12
+ nbproject
13
+ pkg
14
+ *.sw?
15
+ spec/dummy
16
+ .rvmrc
17
+ .sass-cache
18
+ public/spree
19
+ .ruby-version
20
+ .ruby-gemset
21
+ gemfiles/*.gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ -r spec_helper
3
+ -f documentation
data/.travis.yml ADDED
@@ -0,0 +1,29 @@
1
+ sudo: required
2
+ dist: trusty
3
+
4
+ language: ruby
5
+
6
+ env:
7
+ - DB=postgres
8
+ - DB=mysql
9
+
10
+ gemfile:
11
+ - gemfiles/spree_3_5.gemfile
12
+ - gemfiles/spree_3_7.gemfile
13
+ - gemfiles/spree_master.gemfile
14
+
15
+ script:
16
+ - bundle exec rake test_app
17
+ - bundle exec rake spec
18
+
19
+ rvm:
20
+ - 2.3.3
21
+ - 2.4.2
22
+ - 2.5.2
23
+
24
+ matrix:
25
+ allow_failures:
26
+ - gemfile: gemfiles/spree_master.gemfile
27
+
28
+ before_install:
29
+ - mysql -u root -e "GRANT ALL ON *.* TO 'travis'@'%';"
data/Appraisals ADDED
@@ -0,0 +1,17 @@
1
+ appraise 'spree-3-5' do
2
+ gem 'spree', '~> 3.5.0'
3
+ gem 'spree_auth_devise', '~> 3.3.0'
4
+ gem 'rails-controller-testing'
5
+ end
6
+
7
+ appraise 'spree-3-7' do
8
+ gem 'spree', '~> 3.7.0'
9
+ gem 'spree_auth_devise', '~> 3.5.0'
10
+ gem 'rails-controller-testing'
11
+ end
12
+
13
+ appraise 'spree-master' do
14
+ gem 'spree', github: 'spree/spree', branch: 'master'
15
+ gem 'spree_auth_devise', github: 'spree/spree_auth_devise', branch: 'master'
16
+ gem 'rails-controller-testing'
17
+ end
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'spree', github: 'spree/spree', branch: 'master'
4
+ gem 'spree_auth_devise', github: 'spree/spree_auth_devise', branch: 'master'
5
+ gem 'rails-controller-testing'
6
+
7
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2017-2018 Spark Solutions Sp. z o.o.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ * Neither the name Spree nor the names of its contributors may be used to
13
+ endorse or promote products derived from this software without specific
14
+ prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # Spree Analytics Trackers
2
+
3
+ [![Build Status](https://travis-ci.org/spree-contrib/spree_analytics_trackers.svg?branch=master)](https://travis-ci.org/spree-contrib/spree_analytics_trackers)
4
+
5
+ This extensions adds support for [Google Analytics Enhanced Ecommerce](https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce)
6
+ and [Segment Ecommerce](https://segment.com/docs/spec/ecommerce/v2/) tracking to [Spree Commerce](https://spreecommerce.org) stores.
7
+
8
+ Via Segment.com you can plug-in also a lot of additional vendors and services like:
9
+ * Mixpanel
10
+ * Kissmetrics
11
+ * Google Tag Manager
12
+ * Facebook Pixels
13
+ * Google Adwords remarketing
14
+ * [and hundreds others](https://segment.com/catalog#integrations/all)
15
+
16
+ No additional development required.
17
+
18
+ ## Installation
19
+
20
+ 1. Add this extension to your Gemfile with this line:
21
+ ```ruby
22
+ gem 'spree_analytics_trackers', '~> 1.0'
23
+ ```
24
+
25
+ 2. Install the gem using Bundler:
26
+ ```ruby
27
+ bundle install
28
+ ```
29
+
30
+ 3. Copy & run migrations
31
+ ```ruby
32
+ bundle exec rails g spree_analytics_trackers:install
33
+ ```
34
+
35
+ 4. Restart your server
36
+
37
+ If your server was running, restart it so that it can find the assets properly.
38
+
39
+ ## Testing
40
+
41
+ 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`.
42
+
43
+ ```shell
44
+ bundle
45
+ bundle exec rake
46
+ ```
47
+
48
+ When testing your applications integration with this extension you may use it's factories.
49
+ Simply add this require statement to your spec_helper:
50
+
51
+ ```ruby
52
+ require 'spree_analytics_trackers/factories'
53
+ ```
54
+
55
+
56
+ ## License
57
+
58
+ Spree Analytics Trackers is copyright © 2017-2018
59
+ [Spark Solutions Sp. z o.o.][spark]. It is free software,
60
+ and may be redistributed under the terms specified in the
61
+ [LICENCE](LICENSE) file.
62
+
63
+ [LICENSE]: https://github.com/spree-contrib/spree_analytics_trackers/blob/master/LICENSE
64
+
65
+ ## About Spark Solutions
66
+ [![Spark Solutions](http://sparksolutions.co/wp-content/uploads/2015/01/logo-ss-tr-221x100.png)][spark]
67
+
68
+ Spree Analytics Trackers is maintained by [Spark Solutions Sp. z o.o.](http://sparksolutions.co?utm_source=github).
69
+
70
+ We are passionate about open source software.
71
+ We are [available for hire][spark].
72
+
73
+ [spark]:http://sparksolutions.co?utm_source=github
data/Rakefile ADDED
@@ -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_analytics_trackers'
20
+ Rake::Task['extension:test_app'].invoke
21
+ end
@@ -0,0 +1,6 @@
1
+ module Spree
2
+ module Admin
3
+ class TrackersController < ResourceController
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ if defined?(Spree::StoreController)
2
+ Spree::StoreController.class_eval do
3
+ helper Spree::TrackersHelper
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ module Spree
2
+ module TrackersHelper
3
+ def product_for_segment(product, optional = {})
4
+ {
5
+ product_id: product.id,
6
+ sku: product.sku,
7
+ category: product.category.try(:name),
8
+ name: product.name,
9
+ brand: product.brand.try(:name),
10
+ price: product.price.to_f,
11
+ currency: product.currency,
12
+ url: product_url(product),
13
+ }.tap do |hash|
14
+ # This method returns either asset_url (Paperclip) or rails_blob_path (ActiveStorage) to return the specified image(s)
15
+ if defined?(ActiveStorage) && !Rails.application.config.use_paperclip
16
+ hash[:image_url] = request.base_url + Rails.application.routes.url_helpers.rails_blob_path(optional.delete(:image).attachment, only_path: true) if optional[:image]
17
+ else
18
+ hash[:image_url] = request.base_url + asset_url(optional.delete(:image).attachment) if optional[:image]
19
+ end
20
+ end.merge(optional).to_json.html_safe
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,6 @@
1
+ if Gem.loaded_specs['spree_core'].version < Gem::Version.create('3.2.0')
2
+ Spree::LineItem.class_eval do
3
+ delegate :brand, :category, to: :product
4
+ delegate :options_text, to: :variant
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ if Gem.loaded_specs['spree_core'].version < Gem::Version.create('3.3.0')
2
+ Spree::Order.class_eval do
3
+ def promo_code
4
+ promotions.pluck(:code).compact.first
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ if Gem.loaded_specs['spree_core'].version < Gem::Version.create('3.2.0')
2
+ Spree::Product.class_eval do
3
+ def category
4
+ taxons.joins(:taxonomy).find_by(spree_taxonomies: { name: Spree.t(:taxonomy_categories_name) })
5
+ end
6
+
7
+ def brand
8
+ taxons.joins(:taxonomy).find_by(spree_taxonomies: { name: Spree.t(:taxonomy_brands_name) })
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ module Spree
2
+ class Tracker < Spree::Base
3
+ TRACKING_ENGINES = %i(google_analytics segment).freeze
4
+ enum engine: TRACKING_ENGINES
5
+
6
+ after_commit :clear_cache
7
+
8
+ validates :analytics_id, presence: true, uniqueness: { scope: :engine, case_sensitive: false }
9
+
10
+ scope :active, -> { where(active: true) }
11
+
12
+ def self.current(engine = TRACKING_ENGINES.first)
13
+ tracker = Rails.cache.fetch("current_tracker/#{engine}") do
14
+ send(engine).active.first
15
+ end
16
+ tracker.analytics_id.present? ? tracker : nil if tracker
17
+ end
18
+
19
+ def clear_cache
20
+ TRACKING_ENGINES.each do |engine|
21
+ Rails.cache.delete("current_tracker/#{engine}")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,5 @@
1
+ Spree.user_class.class_eval do
2
+ def analytics_id
3
+ id
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ if Gem.loaded_specs['spree_core'].version >= Gem::Version.create('3.5.0')
2
+ Deface::Override.new(
3
+ virtual_path: 'spree/admin/shared/sub_menu/_configuration',
4
+ name: 'add_analytics_to_admin_configurations_sidebar_menu',
5
+ insert_bottom: '[data-hook="admin_configurations_sidebar_menu"]',
6
+ original: '72f6a33afb2627a467ae458d42991ab0ce4cc6df',
7
+ text: '<%= configurations_sidebar_menu_item(Spree.t(:analytics_trackers), spree.admin_trackers_path) if can? :manage, Spree::Tracker %>'
8
+ )
9
+ end
@@ -0,0 +1,10 @@
1
+ spree_version = Gem.loaded_specs['spree_core'].version
2
+ unless spree_version >= Gem::Version.create('3.4.0') && spree_version < Gem::Version.create('3.5.0')
3
+ Deface::Override.new(
4
+ virtual_path: 'spree/orders/edit',
5
+ name: 'add_cart_viewed_to_orders_edit',
6
+ insert_top: "[data-hook='cart_container']",
7
+ partial: 'spree/shared/trackers/segment/cart_viewed.js',
8
+ original: 'e0a3dcdf759c4ec9fad085cd8a5360c8d68167f5'
9
+ )
10
+ end
@@ -0,0 +1,10 @@
1
+ spree_version = Gem.loaded_specs['spree_core'].version
2
+ unless spree_version >= Gem::Version.create('3.4.0') && spree_version < Gem::Version.create('3.5.0')
3
+ Deface::Override.new(
4
+ virtual_path: 'spree/checkout/edit',
5
+ name: 'add_checkout_step_viewed_to_checkout_edit',
6
+ insert_bottom: "[data-hook='checkout_summary_box']",
7
+ partial: 'spree/shared/trackers/segment/checkout_step_viewed.js',
8
+ original: 'f7b787e3485b705d0373e248361fcb590a45478e'
9
+ )
10
+ end
@@ -0,0 +1,9 @@
1
+ if Gem.loaded_specs['spree_core'].version >= Gem::Version.create('3.5.0.alpha')
2
+ Deface::Override.new(
3
+ virtual_path: 'spree/layouts/spree_application',
4
+ name: 'add_google_analytics_to_spree_application',
5
+ insert_top: "[data-hook='body']",
6
+ partial: 'spree/shared/google_analytics',
7
+ original: 'cfa30a2831d9a41394c03229cd28b3c7eee69585'
8
+ )
9
+ end
@@ -0,0 +1,9 @@
1
+ if Gem.loaded_specs['spree_core'].version >= Gem::Version.create('3.5.0')
2
+ Deface::Override.new(
3
+ virtual_path: 'spree/checkout/edit',
4
+ name: 'add_google_checkout_to_checkout_edit',
5
+ insert_before: "#checkout",
6
+ partial: 'spree/shared/google_checkout',
7
+ original: '1bc9409097210501fee61bc448df937682546308'
8
+ )
9
+ end
@@ -0,0 +1,9 @@
1
+ if Gem.loaded_specs['spree_core'].version >= Gem::Version.create('3.5.0')
2
+ Deface::Override.new(
3
+ virtual_path: 'spree/orders/show',
4
+ name: 'add_google_purchase_to_orders_show',
5
+ insert_before: "#order_summary",
6
+ partial: 'spree/shared/google_purchase',
7
+ original: 'ac7f302922a3c1bb080095296ccc20e42bab2967'
8
+ )
9
+ end
@@ -0,0 +1,10 @@
1
+ spree_version = Gem.loaded_specs['spree_core'].version
2
+ unless spree_version >= Gem::Version.create('3.3.0') && spree_version < Gem::Version.create('3.5.0')
3
+ Deface::Override.new(
4
+ virtual_path: 'spree/orders/show',
5
+ name: 'add_order_complete_to_orders_show',
6
+ insert_before: "#order_summary",
7
+ partial: 'spree/shared/trackers/segment/order_complete.js',
8
+ original: 'ac7f302922a3c1bb080095296ccc20e42bab2967'
9
+ )
10
+ end
@@ -0,0 +1,10 @@
1
+ spree_version = Gem.loaded_specs['spree_core'].version
2
+ unless spree_version >= Gem::Version.create('3.4.0') && spree_version < Gem::Version.create('3.5.0')
3
+ Deface::Override.new(
4
+ virtual_path: 'spree/orders/edit',
5
+ name: 'add_product_added_to_orders_edit',
6
+ insert_top: "[data-hook='cart_container']",
7
+ partial: 'spree/shared/trackers/segment/product_added.js',
8
+ original: '75a9171ce7647353a00bdfa178b131872e7f9bdc'
9
+ )
10
+ end
@@ -0,0 +1,10 @@
1
+ spree_version = Gem.loaded_specs['spree_core'].version
2
+ unless spree_version >= Gem::Version.create('3.4.0') && spree_version < Gem::Version.create('3.5.0')
3
+ Deface::Override.new(
4
+ virtual_path: 'spree/taxons/show',
5
+ name: 'add_product_list_filtered_to_taxons_show',
6
+ insert_before: ".taxon-title",
7
+ original: '2d71d85f4cb141a6ff90264e48915493d6856e9b',
8
+ partial: 'spree/shared/trackers/segment/product_list_filtered.js'
9
+ )
10
+ end
@@ -0,0 +1,10 @@
1
+ spree_version = Gem.loaded_specs['spree_core'].version
2
+ unless spree_version >= Gem::Version.create('3.4.0') && spree_version < Gem::Version.create('3.5.0')
3
+ Deface::Override.new(
4
+ virtual_path: 'spree/shared/_products',
5
+ name: 'add_product_list_viewed_to_products_list',
6
+ insert_before: "[data-hook='products_search_results_heading']",
7
+ partial: 'spree/shared/trackers/segment/product_list_viewed.js',
8
+ original: '8b47dddca9dfaaacca3d462459c6c1ef06c09926'
9
+ )
10
+ end