stall 0.1.0

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 (82) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +7 -0
  4. data/Rakefile +22 -0
  5. data/app/assets/javascripts/stall.coffee +12 -0
  6. data/app/assets/javascripts/stall/add-to-cart-form.coffee +22 -0
  7. data/app/assets/javascripts/stall/application.js +13 -0
  8. data/app/assets/javascripts/stall/carts.js +2 -0
  9. data/app/assets/stylesheets/stall/application.css +15 -0
  10. data/app/assets/stylesheets/stall/carts.css +4 -0
  11. data/app/controllers/stall/application_controller.rb +18 -0
  12. data/app/controllers/stall/carts_controller.rb +28 -0
  13. data/app/controllers/stall/checkout/steps_controller.rb +32 -0
  14. data/app/controllers/stall/checkouts_controller.rb +9 -0
  15. data/app/controllers/stall/line_items_controller.rb +26 -0
  16. data/app/helpers/stall/add_to_cart_helper.rb +10 -0
  17. data/app/helpers/stall/application_helper.rb +4 -0
  18. data/app/helpers/stall/cart_helper.rb +28 -0
  19. data/app/helpers/stall/checkout_helper.rb +7 -0
  20. data/app/models/stall.rb +5 -0
  21. data/app/models/stall/address.rb +5 -0
  22. data/app/models/stall/address_ownership.rb +15 -0
  23. data/app/models/stall/cart.rb +14 -0
  24. data/app/models/stall/customer.rb +5 -0
  25. data/app/models/stall/line_item.rb +45 -0
  26. data/app/models/stall/product_list.rb +56 -0
  27. data/app/models/stall/shipment.rb +15 -0
  28. data/app/models/stall/shipping_method.rb +5 -0
  29. data/app/services/stall/add_to_cart_service.rb +52 -0
  30. data/app/services/stall/base_service.rb +4 -0
  31. data/app/services/stall/shipping_fee_calculator_service.rb +21 -0
  32. data/app/views/checkout/steps/_informations.html.haml +41 -0
  33. data/app/views/checkout/steps/_payment_method.html.haml +1 -0
  34. data/app/views/checkout/steps/_shipping_method.html.haml +10 -0
  35. data/app/views/layouts/stall/application.html.erb +15 -0
  36. data/app/views/stall/carts/show.html.haml +23 -0
  37. data/app/views/stall/checkout/steps/show.html.haml +1 -0
  38. data/app/views/stall/line_items/_add_error.html.haml +17 -0
  39. data/app/views/stall/line_items/_added.html.haml +23 -0
  40. data/app/views/stall/line_items/_form.html.haml +12 -0
  41. data/config/locales/stall.en.yml +60 -0
  42. data/config/locales/stall.fr.yml +60 -0
  43. data/db/migrate/20151103154313_create_stall_line_items.rb +19 -0
  44. data/db/migrate/20160118121116_create_stall_product_lists.rb +15 -0
  45. data/db/migrate/20160118124016_create_customers.rb +11 -0
  46. data/db/migrate/20160122143746_create_stall_addresses.rb +17 -0
  47. data/db/migrate/20160122143748_create_stall_address_ownerships.rb +17 -0
  48. data/db/migrate/20160124014144_create_stall_shipping_methods.rb +10 -0
  49. data/db/migrate/20160124020313_create_stall_shipments.rb +17 -0
  50. data/lib/generators/stall/checkout/step/step_generator.rb +20 -0
  51. data/lib/generators/stall/checkout/step/templates/step.rb.erb +2 -0
  52. data/lib/generators/stall/checkout/wizard/templates/wizard.rb.erb +3 -0
  53. data/lib/generators/stall/checkout/wizard/wizard_generator.rb +20 -0
  54. data/lib/generators/stall/install/install_generator.rb +17 -0
  55. data/lib/generators/stall/install/templates/initializer.rb +47 -0
  56. data/lib/stall.rb +38 -0
  57. data/lib/stall/addressable.rb +44 -0
  58. data/lib/stall/checkout.rb +15 -0
  59. data/lib/stall/checkout/informations_checkout_step.rb +41 -0
  60. data/lib/stall/checkout/payment_checkout_step.rb +6 -0
  61. data/lib/stall/checkout/payment_method_checkout_step.rb +6 -0
  62. data/lib/stall/checkout/shipping_method_checkout_step.rb +20 -0
  63. data/lib/stall/checkout/step.rb +47 -0
  64. data/lib/stall/checkout/wizard.rb +85 -0
  65. data/lib/stall/config.rb +27 -0
  66. data/lib/stall/engine.rb +30 -0
  67. data/lib/stall/rails/currency_helper.rb +26 -0
  68. data/lib/stall/rails/routing_mapper.rb +13 -0
  69. data/lib/stall/routes.rb +27 -0
  70. data/lib/stall/sellable.rb +8 -0
  71. data/lib/stall/sellable/mixin.rb +17 -0
  72. data/lib/stall/sellable/model.rb +46 -0
  73. data/lib/stall/shipping.rb +16 -0
  74. data/lib/stall/shipping/calculator.rb +28 -0
  75. data/lib/stall/shipping/config.rb +17 -0
  76. data/lib/stall/shipping/country_weight_table_calculator.rb +69 -0
  77. data/lib/stall/shipping/free_shipping_calculator.rb +23 -0
  78. data/lib/stall/utils.rb +22 -0
  79. data/lib/stall/utils/config_dsl.rb +19 -0
  80. data/lib/stall/version.rb +3 -0
  81. data/lib/tasks/stall_tasks.rake +4 -0
  82. metadata +362 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9caa6a04c98d0ebd94ac128e27de7e7ea7797a55
4
+ data.tar.gz: 024289eded82201eaf2a5c0dcc3c1cde5f071011
5
+ SHA512:
6
+ metadata.gz: 76d45836e3b79f5ab7bde8809ff4ed35779d1bfb5dc28aeb99985b8b6b31ad9f1f3aa82b0870b32a770c293322ede4ccdad39fcfaa24d1ebce474f9cc541b9b1
7
+ data.tar.gz: ea9f53aca3a34913111afc301c48616912fc9ff39831fe2679fa0dcfb77123316236fbafdc87a980cd9d4adb5a5164680ff8c8f3e94314f78f9d80fcb4f1e9c0
@@ -0,0 +1,20 @@
1
+ Copyright 2015 vala
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,7 @@
1
+ # Stall
2
+
3
+
4
+
5
+ # Licence
6
+
7
+ This project rocks and uses MIT-LICENSE.
@@ -0,0 +1,22 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Stall'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+ load 'rails/tasks/statistics.rake'
20
+
21
+ Bundler::GemHelper.install_tasks
22
+
@@ -0,0 +1,12 @@
1
+ #= require vertebra
2
+ #= require_self
3
+ #= require stall/add-to-cart-form
4
+
5
+ @Stall =
6
+ onDomReady: (callback) ->
7
+ event = if window.Turbolinks && window.Turbolinks.supported
8
+ 'page:change'
9
+ else
10
+ 'ready'
11
+
12
+ $(document).on(event, callback)
@@ -0,0 +1,22 @@
1
+ class Stall.AddToCartForm extends Vertebra.View
2
+ @create = ($el) ->
3
+ return if $el.data('stall.add-to-cart-form')
4
+ instance = new Stall.AddToCartForm(el: $el)
5
+ $el.data('stall.add-to-cart-form', instance)
6
+
7
+ events:
8
+ 'ajax:success': 'onSuccess'
9
+
10
+ onSuccess: (e, resp) ->
11
+ @$modal = $(resp).appendTo('body').modal()
12
+ @updateTotalQuantityCounter()
13
+
14
+ updateTotalQuantityCounter: ->
15
+ quantity = @$modal.data('cart-total-quantity')
16
+
17
+ if ($counter = $('[data-cart-quantity-counter]')).length
18
+ $counter.text(quantity)
19
+
20
+ Stall.onDomReady ->
21
+ $('body').on 'ajax:beforeSend', '[data-add-to-cart-form]', (e) ->
22
+ Stall.AddToCartForm.create($(e.currentTarget))
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,18 @@
1
+ module Stall
2
+ class ApplicationController < Stall.config.application_controller_ancestor.constantize
3
+ layout :_set_stall_layout
4
+
5
+ private
6
+
7
+ # Enforce app-level layout by defaulting to the "application" layout if
8
+ # no parent layout was set before, thus avoiding the "stall/application"
9
+ # layout to be rendered
10
+ def _set_stall_layout
11
+ parent_controller = self.class.ancestors.find do |ancestor|
12
+ !ancestor.name.match(/^Stall::/)
13
+ end
14
+
15
+ parent_controller._layout ||= 'application'
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ module Stall
2
+ class CartsController < ApplicationController
3
+ before_action :load_cart
4
+
5
+ def show
6
+ end
7
+
8
+ def update
9
+ if @cart.update_attributes(cart_params)
10
+ flash[:success] = t('stall.carts.flashes.update.success')
11
+ redirect_to cart_path(@cart)
12
+ else
13
+ flash[:error] = t('stall.carts.flashes.update.error')
14
+ render 'show'
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def load_cart
21
+ @cart = Cart.find_by_token(params[:id])
22
+ end
23
+
24
+ def cart_params
25
+ params.require(:cart).permit(line_items_attributes: [:id, :quantity])
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ module Stall
2
+ module Checkout
3
+ class StepsController < Stall::ApplicationController
4
+ include Stall::CheckoutHelper
5
+
6
+ before_action :load_step
7
+
8
+ def show
9
+ @step.prepare
10
+ end
11
+
12
+ def update
13
+ if @step.process
14
+ @wizard.validate_current_step!
15
+ redirect_to step_path(@cart)
16
+ else
17
+ @step.prepare
18
+ flash[:error] = t("stall.checkout.#{ @wizard.current_step_name }.error")
19
+ render 'show'
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def load_step
26
+ @cart = Stall::Cart.find_by_token(params[:cart_id])
27
+ @wizard = @cart.wizard.new(@cart)
28
+ @step = @wizard.current_step.new(@cart, params)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,9 @@
1
+ module Stall
2
+ class CheckoutsController < ApplicationController
3
+ def show
4
+ @cart = Stall::Cart.find_by_token(params[:id])
5
+ @cart.reset_state!
6
+ redirect_to checkout_step_path(@cart.wizard.route_key, @cart)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,26 @@
1
+ module Stall
2
+ class LineItemsController < ApplicationController
3
+ def create
4
+ service = Stall::AddToCartService.new(cart, line_item_params)
5
+
6
+ if service.call
7
+ @quantity = params[:line_item][:quantity].to_i
8
+ @line_item = service.line_item
9
+ render partial: 'added'
10
+ else
11
+ @line_item = service.line_item
12
+ render partial: 'add_error'
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def line_item_params
19
+ params.require(:line_item).permit(:sellable_type, :sellable_id, :quantity)
20
+ end
21
+
22
+ def cart
23
+ @cart ||= Stall::Cart.find_by_token(params[:cart_id])
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ module Stall
2
+ module AddToCartHelper
3
+ def add_to_cart_form_for(sellable, cart: nil)
4
+ render partial: 'stall/line_items/form', locals: {
5
+ cart: (cart || current_cart),
6
+ line_item: Stall::LineItem.new(sellable: sellable)
7
+ }
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ module Stall
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,28 @@
1
+ module Stall
2
+ module CartHelper
3
+ def current_cart
4
+ RequestStore.store[cart_store_key_for(:default)] ||= load_current_cart
5
+ end
6
+
7
+ private
8
+
9
+ def load_current_cart
10
+ if (cart_token = session[cart_store_key_for(:default)])
11
+ if (cart = Stall::Cart.find_by_token(cart_token))
12
+ return cart
13
+ end
14
+ end
15
+
16
+ # If no token was stored or the token does not exist anymore, create a
17
+ # new cart and store the new token
18
+ #
19
+ Stall::Cart.create!.tap do |cart|
20
+ session[cart_store_key_for(:default)] = cart.token
21
+ end
22
+ end
23
+
24
+ def cart_store_key_for(identifier)
25
+ ['stall', 'cart', identifier.to_s].join('.')
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ module Stall
2
+ module CheckoutHelper
3
+ def step_path(cart)
4
+ checkout_step_path(cart.wizard.route_key, cart)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ module Stall
2
+ def self.table_name_prefix
3
+ 'stall_'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Stall
2
+ class Address < ActiveRecord::Base
3
+ has_one :addressable_ownership, dependent: :destroy
4
+ end
5
+ end
@@ -0,0 +1,15 @@
1
+ module Stall
2
+ class AddressOwnership < ActiveRecord::Base
3
+ belongs_to :address
4
+ accepts_nested_attributes_for :address
5
+
6
+ belongs_to :addressable, polymorphic: true
7
+
8
+ def type_name
9
+ types = []
10
+ types << :billing if billing
11
+ types << :shipping if shipping
12
+ types.join('-')
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module Stall
2
+ class Cart < Stall::ProductList
3
+ include Stall::Addressable
4
+
5
+ has_one :shipment, dependent: :destroy, inverse_of: :cart
6
+ accepts_nested_attributes_for :shipment
7
+
8
+ def total_weight
9
+ line_items.reduce(0) do |total, line_item|
10
+ total + (line_item.weight || Stall.config.default_product_weight)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ module Stall
2
+ class Customer < ActiveRecord::Base
3
+ include Stall::Addressable
4
+ end
5
+ end
@@ -0,0 +1,45 @@
1
+ module Stall
2
+ class LineItem < ActiveRecord::Base
3
+ store_accessor :data, :weight
4
+
5
+ monetize :unit_eot_price_cents, :unit_price_cents,
6
+ :eot_price_cents, :price_cents,
7
+ with_model_currency: :currency, allow_nil: true
8
+
9
+ belongs_to :sellable, polymorphic: true
10
+ belongs_to :product_list
11
+
12
+ validates :name, :unit_price, :unit_eot_price, :vat_rate, :price, :quantity,
13
+ :eot_price, :sellable, presence: true
14
+
15
+ validates :unit_price, :unit_eot_price, :vat_rate, :price, :eot_price,
16
+ numericality: true
17
+
18
+ validates :quantity, numericality: { greater_than: 0 }
19
+
20
+ validate :stock_availability
21
+
22
+ before_validation :refresh_total_prices
23
+
24
+ def like?(other)
25
+ [:sellable_id, :sellable_type].all? do |property|
26
+ public_send(property) == other.public_send(property)
27
+ end
28
+ end
29
+
30
+ def currency
31
+ product_list.try(:currency) || Money.default_currency
32
+ end
33
+
34
+ private
35
+
36
+ # TODO : Stocks availibility handling
37
+ def stock_availability
38
+ end
39
+
40
+ def refresh_total_prices
41
+ self.eot_price = unit_eot_price * quantity if unit_eot_price && quantity
42
+ self.price = unit_price * quantity if unit_price && quantity
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,56 @@
1
+ module Stall
2
+ class ProductList < ActiveRecord::Base
3
+ has_secure_token
4
+
5
+ has_many :line_items, dependent: :destroy
6
+ accepts_nested_attributes_for :line_items
7
+
8
+ belongs_to :customer, class_name: 'Stall::Customer'
9
+ accepts_nested_attributes_for :customer
10
+
11
+ validates :type, presence: true
12
+
13
+ after_initialize :ensure_currency
14
+ after_initialize :ensure_state
15
+
16
+ def state
17
+ read_attribute(:state).try(:to_sym)
18
+ end
19
+
20
+ def reset_state!
21
+ update_column(:state, wizard.steps.first)
22
+ end
23
+
24
+ def to_param
25
+ token
26
+ end
27
+
28
+ def total_quantity
29
+ line_items.map(&:quantity).sum
30
+ end
31
+
32
+ def wizard
33
+ @wizard ||= begin
34
+ wizard_name = Stall.config.default_checkout_wizard
35
+
36
+ if (wizard = Stall::Utils.try_load_constant(wizard_name))
37
+ wizard
38
+ else
39
+ raise Stall::Checkout::WizardNotFoundError.new,
40
+ "The checkout wizard #{ wizard_name } was not found. You must generate it " +
41
+ "with `rails g stall:wizard #{ wizard_name.underscore.gsub('_checkout_wizard', '') }`"
42
+ end
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def ensure_currency
49
+ self.currency ||= Money.default_currency
50
+ end
51
+
52
+ def ensure_state
53
+ self.state ||= wizard.steps.first
54
+ end
55
+ end
56
+ end