mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Merge pull request #5733 from luisramos0/base_ctrl
Move lib/spree to OFN
This commit is contained in:
16
app/controllers/spree/base_controller.rb
Normal file
16
app/controllers/spree/base_controller.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'cancan'
|
||||
|
||||
module Spree
|
||||
class BaseController < ApplicationController
|
||||
include Spree::Core::ControllerHelpers::Auth
|
||||
include Spree::Core::ControllerHelpers::RespondWith
|
||||
include Spree::Core::ControllerHelpers::SSL
|
||||
include Spree::Core::ControllerHelpers::Common
|
||||
|
||||
respond_to :html
|
||||
end
|
||||
end
|
||||
|
||||
require 'spree/i18n/initializer'
|
||||
58
lib/spree/core.rb
Normal file
58
lib/spree/core.rb
Normal file
@@ -0,0 +1,58 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails/all'
|
||||
require 'active_merchant'
|
||||
require 'acts_as_list'
|
||||
require 'awesome_nested_set'
|
||||
require 'cancan'
|
||||
require 'kaminari'
|
||||
require 'mail'
|
||||
require 'paperclip'
|
||||
require 'paranoia'
|
||||
require 'ransack'
|
||||
require 'state_machine'
|
||||
|
||||
module Spree
|
||||
mattr_accessor :user_class
|
||||
|
||||
def self.user_class
|
||||
if @@user_class.is_a?(Class)
|
||||
raise "Spree.user_class MUST be a String object, not a Class object."
|
||||
end
|
||||
|
||||
return unless @@user_class.is_a?(String)
|
||||
|
||||
@@user_class.constantize
|
||||
end
|
||||
|
||||
# Used to configure Spree.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# Spree.config do |config|
|
||||
# config.site_name = "An awesome Spree site"
|
||||
# end
|
||||
#
|
||||
# This method is defined within the core gem on purpose.
|
||||
# Some people may only wish to use the Core part of Spree.
|
||||
def self.config
|
||||
yield(Spree::Config)
|
||||
end
|
||||
end
|
||||
|
||||
require 'spree/core/version'
|
||||
require 'spree/core/engine'
|
||||
|
||||
require 'spree/i18n'
|
||||
require 'spree/money'
|
||||
|
||||
require 'spree/core/delegate_belongs_to'
|
||||
require 'spree/core/ext/active_record'
|
||||
require 'spree/core/permalinks'
|
||||
require 'spree/core/token_resource'
|
||||
require 'spree/core/calculated_adjustments'
|
||||
require 'spree/core/product_duplicator'
|
||||
|
||||
ActiveRecord::Base.class_eval do
|
||||
include CollectiveIdea::Acts::NestedSet
|
||||
end
|
||||
81
lib/spree/core/calculated_adjustments.rb
Normal file
81
lib/spree/core/calculated_adjustments.rb
Normal file
@@ -0,0 +1,81 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Spree
|
||||
module Core
|
||||
module CalculatedAdjustments
|
||||
def self.included(klass)
|
||||
klass.class_eval do
|
||||
has_one :calculator, class_name: "Spree::Calculator", as: :calculable, dependent: :destroy
|
||||
accepts_nested_attributes_for :calculator
|
||||
validates :calculator, presence: true
|
||||
|
||||
def self.calculators
|
||||
spree_calculators.__send__(model_name_without_spree_namespace)
|
||||
end
|
||||
|
||||
def calculator_type
|
||||
calculator.class.to_s if calculator
|
||||
end
|
||||
|
||||
def calculator_type=(calculator_type)
|
||||
klass = calculator_type.constantize if calculator_type
|
||||
self.calculator = klass.new if klass && !calculator.is_a?(klass)
|
||||
end
|
||||
|
||||
# Creates a new adjustment for the target object
|
||||
# (which is any class that has_many :adjustments) and sets amount based on the
|
||||
# calculator as applied to the given calculable (Order, LineItems[], Shipment, etc.)
|
||||
# By default the adjustment will not be considered mandatory
|
||||
def create_adjustment(label, target, calculable, mandatory = false, state = "closed")
|
||||
# Adjustment calculations done on Spree::Shipment objects MUST
|
||||
# be done on their to_package'd variants instead
|
||||
# It's only the package that contains the correct information.
|
||||
# See https://github.com/spree/spree_active_shipping/pull/96 et. al
|
||||
old_calculable = calculable
|
||||
calculable = calculable.to_package if calculable.is_a?(Spree::Shipment)
|
||||
amount = compute_amount(calculable)
|
||||
return if amount.zero? && !mandatory
|
||||
|
||||
target.adjustments.create(
|
||||
amount: amount,
|
||||
source: old_calculable,
|
||||
originator: self,
|
||||
label: label,
|
||||
mandatory: mandatory,
|
||||
state: state
|
||||
)
|
||||
end
|
||||
|
||||
# Updates the amount of the adjustment using our Calculator and
|
||||
# calling the +compute+ method with the +calculable+
|
||||
# referenced passed to the method.
|
||||
def update_adjustment(adjustment, calculable)
|
||||
# Adjustment calculations done on Spree::Shipment objects MUST
|
||||
# be done on their to_package'd variants instead
|
||||
# It's only the package that contains the correct information.
|
||||
# See https://github.com/spree/spree_active_shipping/pull/96 et. al
|
||||
calculable = calculable.to_package if calculable.is_a?(Spree::Shipment)
|
||||
adjustment.update_column(:amount, compute_amount(calculable))
|
||||
end
|
||||
|
||||
# Calculate the amount to be used when creating an adjustment
|
||||
# NOTE: May be overriden by classes where this module is included into.
|
||||
# Such as Spree::Promotion::Action::CreateAdjustment.
|
||||
def compute_amount(calculable)
|
||||
calculator.compute(calculable)
|
||||
end
|
||||
|
||||
def self.model_name_without_spree_namespace
|
||||
to_s.tableize.gsub('/', '_').sub('spree_', '')
|
||||
end
|
||||
private_class_method :model_name_without_spree_namespace
|
||||
|
||||
def self.spree_calculators
|
||||
Rails.application.config.spree.calculators
|
||||
end
|
||||
private_class_method :spree_calculators
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
94
lib/spree/core/delegate_belongs_to.rb
Normal file
94
lib/spree/core/delegate_belongs_to.rb
Normal file
@@ -0,0 +1,94 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
##
|
||||
# Creates methods on object which delegate to an association proxy.
|
||||
# see delegate_belongs_to for two uses
|
||||
#
|
||||
# Todo - integrate with ActiveRecord::Dirty to make sure changes to delegate object are noticed
|
||||
# Should do
|
||||
# class User < ActiveRecord::Base; delegate_belongs_to :contact, :firstname; end
|
||||
# class Contact < ActiveRecord::Base; end
|
||||
# u = User.first
|
||||
# u.changed? # => false
|
||||
# u.firstname = 'Bobby'
|
||||
# u.changed? # => true
|
||||
#
|
||||
# Right now the second call to changed? would return false
|
||||
#
|
||||
# Todo - add has_one support. fairly straightforward addition
|
||||
##
|
||||
module DelegateBelongsTo
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
@@default_rejected_delegate_columns = ['created_at', 'created_on', 'updated_at',
|
||||
'updated_on', 'lock_version', 'type', 'id',
|
||||
'position', 'parent_id', 'lft', 'rgt']
|
||||
mattr_accessor :default_rejected_delegate_columns
|
||||
|
||||
##
|
||||
# Creates methods for accessing and setting attributes on an association. Uses same
|
||||
# default list of attributes as delegates_to_association.
|
||||
# delegate_belongs_to :contact
|
||||
# delegate_belongs_to :contact, [:defaults] ## same as above, and useless
|
||||
# delegate_belongs_to :contact, [:defaults, :address, :fullname], :class_name => 'VCard'
|
||||
##
|
||||
def delegate_belongs_to(association, *attrs)
|
||||
opts = attrs.extract_options!
|
||||
initialize_association :belongs_to, association, opts
|
||||
attrs = get_association_column_names(association) if attrs.empty?
|
||||
attrs.concat get_association_column_names(association) if attrs.delete :defaults
|
||||
attrs.each do |attr|
|
||||
class_def attr do |*args|
|
||||
if args.empty?
|
||||
__send__(:delegator_for, association).__send__(attr)
|
||||
else
|
||||
__send__(:delegator_for, association).__send__(attr, *args)
|
||||
end
|
||||
end
|
||||
class_def "#{attr}=" do |val|
|
||||
__send__(:delegator_for, association).__send__("#{attr}=", val)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def get_association_column_names(association, without_default_rejected_delegate_columns = true)
|
||||
association_klass = reflect_on_association(association).klass
|
||||
methods = association_klass.column_names
|
||||
if without_default_rejected_delegate_columns
|
||||
methods.reject!{ |x| default_rejected_delegate_columns.include?(x.to_s) }
|
||||
end
|
||||
methods
|
||||
rescue
|
||||
[]
|
||||
end
|
||||
|
||||
##
|
||||
# initialize_association :belongs_to, :contact
|
||||
def initialize_association(type, association, opts = {})
|
||||
unless [:belongs_to].include?(type.to_s.to_sym)
|
||||
raise 'Illegal or unimplemented association type.'
|
||||
end
|
||||
|
||||
__send__(type, association, opts) if reflect_on_association(association).nil?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def class_def(name, method = nil, &blk)
|
||||
class_eval { method.nil? ? define_method(name, &blk) : define_method(name, method) }
|
||||
end
|
||||
end
|
||||
|
||||
def delegator_for(association)
|
||||
if __send__(association).nil?
|
||||
__send__("#{association}=", self.class.reflect_on_association(association).klass.new)
|
||||
end
|
||||
__send__(association)
|
||||
end
|
||||
protected :delegator_for
|
||||
end
|
||||
|
||||
ActiveRecord::Base.include(DelegateBelongsTo)
|
||||
13
lib/spree/core/environment/calculators.rb
Normal file
13
lib/spree/core/environment/calculators.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Spree
|
||||
module Core
|
||||
class Environment
|
||||
class Calculators
|
||||
include EnvironmentExtension
|
||||
|
||||
attr_accessor :shipping_methods, :tax_rates
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
27
lib/spree/core/environment_extension.rb
Normal file
27
lib/spree/core/environment_extension.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Spree
|
||||
module Core
|
||||
module EnvironmentExtension
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def add_class(name)
|
||||
instance_variable_set "@#{name}", Set.new
|
||||
|
||||
create_method( "#{name}=".to_sym ) { |val|
|
||||
instance_variable_set( "@" + name, val)
|
||||
}
|
||||
|
||||
create_method(name.to_sym) do
|
||||
instance_variable_get( "@" + name )
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_method(name, &block)
|
||||
self.class.__send__(:define_method, name, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
7
lib/spree/core/gateway_error.rb
Normal file
7
lib/spree/core/gateway_error.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Spree
|
||||
module Core
|
||||
class GatewayError < RuntimeError; end
|
||||
end
|
||||
end
|
||||
24
lib/spree/core/mail_interceptor.rb
Normal file
24
lib/spree/core/mail_interceptor.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Allows us to intercept any outbound mail message and make last minute changes
|
||||
# (such as specifying a "from" address or sending to a test email account)
|
||||
#
|
||||
# See http://railscasts.com/episodes/206-action-mailer-in-rails-3 for more details.
|
||||
module Spree
|
||||
module Core
|
||||
class MailInterceptor
|
||||
def self.delivering_email(message)
|
||||
return unless MailSettings.override?
|
||||
|
||||
if Config[:intercept_email].present?
|
||||
message.subject = "#{message.to} #{message.subject}"
|
||||
message.to = Config[:intercept_email]
|
||||
end
|
||||
|
||||
return if Config[:mail_bcc].blank?
|
||||
|
||||
message.bcc ||= Config[:mail_bcc]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
63
lib/spree/core/mail_settings.rb
Normal file
63
lib/spree/core/mail_settings.rb
Normal file
@@ -0,0 +1,63 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Spree
|
||||
module Core
|
||||
class MailSettings
|
||||
MAIL_AUTH = ['None', 'plain', 'login', 'cram_md5'].freeze
|
||||
SECURE_CONNECTION_TYPES = ['None', 'SSL', 'TLS'].freeze
|
||||
|
||||
# Override the Rails application mail settings based on preferences
|
||||
# This makes it possible to configure the mail settings through an admin
|
||||
# interface instead of requiring changes to the Rails envrionment file
|
||||
def self.init
|
||||
new.override! if override?
|
||||
end
|
||||
|
||||
def self.override?
|
||||
Config.override_actionmailer_config
|
||||
end
|
||||
|
||||
def override!
|
||||
if Config.enable_mail_delivery
|
||||
ActionMailer::Base.default_url_options[:host] ||= Config.site_url
|
||||
ActionMailer::Base.smtp_settings = mail_server_settings
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
else
|
||||
ActionMailer::Base.perform_deliveries = false
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def mail_server_settings
|
||||
settings = if need_authentication?
|
||||
basic_settings.merge(user_credentials)
|
||||
else
|
||||
basic_settings
|
||||
end
|
||||
|
||||
settings.merge(enable_starttls_auto: secure_connection?)
|
||||
end
|
||||
|
||||
def user_credentials
|
||||
{ user_name: Config.smtp_username,
|
||||
password: Config.smtp_password }
|
||||
end
|
||||
|
||||
def basic_settings
|
||||
{ address: Config.mail_host,
|
||||
domain: Config.mail_domain,
|
||||
port: Config.mail_port,
|
||||
authentication: Config.mail_auth_type }
|
||||
end
|
||||
|
||||
def need_authentication?
|
||||
Config.mail_auth_type != 'None'
|
||||
end
|
||||
|
||||
def secure_connection?
|
||||
Config.secure_connection_type == 'TLS'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
75
lib/spree/core/permalinks.rb
Normal file
75
lib/spree/core/permalinks.rb
Normal file
@@ -0,0 +1,75 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'stringex'
|
||||
|
||||
module Spree
|
||||
module Core
|
||||
module Permalinks
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
class_attribute :permalink_options
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def make_permalink(options = {})
|
||||
options[:field] ||= :permalink
|
||||
self.permalink_options = options
|
||||
|
||||
return unless connected? &&
|
||||
table_exists? &&
|
||||
column_names.include?(permalink_options[:field].to_s)
|
||||
|
||||
before_validation(on: :create) { save_permalink }
|
||||
end
|
||||
|
||||
def find_by_param(value, *args)
|
||||
__send__("find_by_#{permalink_field}", value, *args)
|
||||
end
|
||||
|
||||
def find_by_param!(value, *args)
|
||||
__send__("find_by_#{permalink_field}!", value, *args)
|
||||
end
|
||||
|
||||
def permalink_field
|
||||
permalink_options[:field]
|
||||
end
|
||||
|
||||
def permalink_prefix
|
||||
permalink_options[:prefix] || ""
|
||||
end
|
||||
|
||||
def permalink_order
|
||||
order = permalink_options[:order]
|
||||
"#{order} ASC," if order
|
||||
end
|
||||
end
|
||||
|
||||
def generate_permalink
|
||||
"#{self.class.permalink_prefix}#{Array.new(9) { rand(9) }.join}"
|
||||
end
|
||||
|
||||
def save_permalink(permalink_value = to_param)
|
||||
with_lock do
|
||||
permalink_value ||= generate_permalink
|
||||
|
||||
field = self.class.permalink_field
|
||||
|
||||
# Do other links exist with this permalink?
|
||||
other = self.class.
|
||||
where("#{self.class.table_name}.#{field} LIKE ?", "#{permalink_value}%")
|
||||
if other.any?
|
||||
# Find the existing permalink with the highest number, and increment that number.
|
||||
# (If none of the existing permalinks have a number, this will evaluate to 1.)
|
||||
number = other.map { |o| o.__send__(field)[/-(\d+)$/, 1].to_i }.max + 1
|
||||
permalink_value += "-#{number}"
|
||||
end
|
||||
write_attribute(field, permalink_value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecord::Base.include(Spree::Core::Permalinks)
|
||||
ActiveRecord::Relation.include(Spree::Core::Permalinks)
|
||||
35
lib/spree/core/s3_support.rb
Normal file
35
lib/spree/core/s3_support.rb
Normal file
@@ -0,0 +1,35 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Spree
|
||||
module Core
|
||||
# This module exists to reduce duplication in S3 settings between
|
||||
# the Image and Taxon models in Spree
|
||||
module S3Support
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
def self.supports_s3(field)
|
||||
# Load user defined paperclip settings
|
||||
config = Spree::Config
|
||||
return unless config[:use_s3]
|
||||
|
||||
s3_creds = { access_key_id: config[:s3_access_key],
|
||||
secret_access_key: config[:s3_secret],
|
||||
bucket: config[:s3_bucket] }
|
||||
attachment_definitions[field][:storage] = :s3
|
||||
attachment_definitions[field][:s3_credentials] = s3_creds
|
||||
attachment_definitions[field][:s3_headers] = ActiveSupport::JSON.
|
||||
decode(config[:s3_headers])
|
||||
attachment_definitions[field][:bucket] = config[:s3_bucket]
|
||||
if config[:s3_protocol].present?
|
||||
attachment_definitions[field][:s3_protocol] = config[:s3_protocol].downcase
|
||||
end
|
||||
|
||||
return if config[:s3_host_alias].blank?
|
||||
|
||||
attachment_definitions[field][:s3_host_alias] = config[:s3_host_alias]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
28
lib/spree/core/token_resource.rb
Normal file
28
lib/spree/core/token_resource.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Spree
|
||||
module Core
|
||||
module TokenResource
|
||||
module ClassMethods
|
||||
def token_resource
|
||||
has_one :tokenized_permission, as: :permissable
|
||||
delegate :token, to: :tokenized_permission, allow_nil: true
|
||||
after_create :create_token
|
||||
end
|
||||
end
|
||||
|
||||
def create_token
|
||||
permission = build_tokenized_permission
|
||||
permission.token = token = ::SecureRandom.hex(8)
|
||||
permission.save!
|
||||
token
|
||||
end
|
||||
|
||||
def self.included(receiver)
|
||||
receiver.extend ClassMethods
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecord::Base.class_eval { include Spree::Core::TokenResource }
|
||||
38
lib/spree/i18n.rb
Normal file
38
lib/spree/i18n.rb
Normal file
@@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'i18n'
|
||||
require 'active_support/core_ext/array/extract_options'
|
||||
require 'spree/i18n/base'
|
||||
|
||||
module Spree
|
||||
extend ActionView::Helpers::TranslationHelper
|
||||
|
||||
class << self
|
||||
# Add spree namespace and delegate to Rails TranslationHelper for some nice
|
||||
# extra functionality. e.g return reasonable strings for missing translations
|
||||
def translate(*args)
|
||||
@virtual_path = virtual_path
|
||||
|
||||
options = args.extract_options!
|
||||
options[:scope] = [*options[:scope]].unshift(:spree)
|
||||
args << options
|
||||
super(*args)
|
||||
end
|
||||
|
||||
alias_method :t, :translate
|
||||
|
||||
def context
|
||||
Spree::ViewContext.context
|
||||
end
|
||||
|
||||
def virtual_path
|
||||
return unless context
|
||||
|
||||
path = context.instance_variable_get("@virtual_path")
|
||||
|
||||
return unless path
|
||||
|
||||
path.gsub(/spree/, '')
|
||||
end
|
||||
end
|
||||
end
|
||||
19
lib/spree/i18n/base.rb
Normal file
19
lib/spree/i18n/base.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Spree
|
||||
module ViewContext
|
||||
def self.context=(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
def self.context
|
||||
@context
|
||||
end
|
||||
|
||||
def view_context
|
||||
super.tap do |context|
|
||||
Spree::ViewContext.context = context
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
3
lib/spree/i18n/initializer.rb
Normal file
3
lib/spree/i18n/initializer.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
Spree::BaseController.include(Spree::ViewContext)
|
||||
42
lib/spree/money.rb
Normal file
42
lib/spree/money.rb
Normal file
@@ -0,0 +1,42 @@
|
||||
# frozen_string_literal: false
|
||||
|
||||
require 'money'
|
||||
|
||||
module Spree
|
||||
class Money
|
||||
attr_reader :money
|
||||
|
||||
delegate :cents, to: :money
|
||||
|
||||
def initialize(amount, options = {})
|
||||
@money = ::Money.parse([amount, (options[:currency] || Spree::Config[:currency])].join)
|
||||
@options = {}
|
||||
@options[:with_currency] = Spree::Config[:display_currency]
|
||||
@options[:symbol_position] = Spree::Config[:currency_symbol_position].to_sym
|
||||
@options[:no_cents] = Spree::Config[:hide_cents]
|
||||
@options[:decimal_mark] = Spree::Config[:currency_decimal_mark]
|
||||
@options[:thousands_separator] = Spree::Config[:currency_thousands_separator]
|
||||
@options.merge!(options)
|
||||
# Must be a symbol because the Money gem doesn't do the conversion
|
||||
@options[:symbol_position] = @options[:symbol_position].to_sym
|
||||
end
|
||||
|
||||
def to_s
|
||||
@money.format(@options)
|
||||
end
|
||||
|
||||
def to_html(options = { html: true })
|
||||
output = @money.format(@options.merge(options))
|
||||
if options[:html]
|
||||
# 1) prevent blank, breaking spaces
|
||||
# 2) prevent escaping of HTML character entities
|
||||
output = output.gsub(" ", " ").html_safe
|
||||
end
|
||||
output
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
@money == other.money
|
||||
end
|
||||
end
|
||||
end
|
||||
63
lib/spree/product_duplicator.rb
Normal file
63
lib/spree/product_duplicator.rb
Normal file
@@ -0,0 +1,63 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Spree
|
||||
class ProductDuplicator
|
||||
attr_accessor :product
|
||||
|
||||
def initialize(product)
|
||||
@product = product
|
||||
end
|
||||
|
||||
def duplicate
|
||||
new_product = duplicate_product
|
||||
|
||||
# don't dup the actual variants, just the characterising types
|
||||
new_product.option_types = product.option_types if product.has_variants?
|
||||
|
||||
# allow site to do some customization
|
||||
new_product.__send__(:duplicate_extra, product) if new_product.respond_to?(:duplicate_extra)
|
||||
new_product.save!
|
||||
new_product
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def duplicate_product
|
||||
product.dup.tap do |new_product|
|
||||
new_product.name = "COPY OF #{product.name}"
|
||||
new_product.taxons = product.taxons
|
||||
new_product.created_at = nil
|
||||
new_product.deleted_at = nil
|
||||
new_product.updated_at = nil
|
||||
new_product.product_properties = reset_properties
|
||||
new_product.master = duplicate_master
|
||||
end
|
||||
end
|
||||
|
||||
def duplicate_master
|
||||
master = product.master
|
||||
master.dup.tap do |new_master|
|
||||
new_master.sku = "COPY OF #{master.sku}"
|
||||
new_master.deleted_at = nil
|
||||
new_master.images = master.images.map { |image| duplicate_image image }
|
||||
new_master.price = master.price
|
||||
new_master.currency = master.currency
|
||||
end
|
||||
end
|
||||
|
||||
def duplicate_image(image)
|
||||
new_image = image.dup
|
||||
new_image.assign_attributes(attachment: image.attachment.clone)
|
||||
new_image
|
||||
end
|
||||
|
||||
def reset_properties
|
||||
product.product_properties.map do |prop|
|
||||
prop.dup.tap do |new_prop|
|
||||
new_prop.created_at = nil
|
||||
new_prop.updated_at = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
57
lib/spree/responder.rb
Normal file
57
lib/spree/responder.rb
Normal file
@@ -0,0 +1,57 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Spree
|
||||
class Responder < ::ActionController::Responder #:nodoc:
|
||||
attr_accessor :on_success, :on_failure
|
||||
|
||||
def initialize(controller, resources, options = {})
|
||||
super
|
||||
|
||||
class_name = controller.class.name.to_sym
|
||||
action_name = options.delete(:action_name)
|
||||
|
||||
result = Spree::BaseController.spree_responders[class_name].
|
||||
try(:[], action_name).
|
||||
try(:[], self.format.to_sym)
|
||||
return unless result
|
||||
|
||||
self.on_success = handler(controller, result, :success)
|
||||
self.on_failure = handler(controller, result, :failure)
|
||||
end
|
||||
|
||||
def to_html
|
||||
if !(on_success || on_failure)
|
||||
super
|
||||
return
|
||||
end
|
||||
|
||||
has_errors? ? controller.instance_exec(&on_failure) : controller.instance_exec(&on_success)
|
||||
end
|
||||
|
||||
def to_format
|
||||
if !(on_success || on_failure)
|
||||
super
|
||||
return
|
||||
end
|
||||
|
||||
has_errors? ? controller.instance_exec(&on_failure) : controller.instance_exec(&on_success)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def handler(controller, result, status)
|
||||
return result if result.respond_to? :call
|
||||
|
||||
case result
|
||||
when Hash
|
||||
if result[status].is_a? Symbol
|
||||
controller.method(result[status])
|
||||
else
|
||||
result[status]
|
||||
end
|
||||
when Symbol
|
||||
controller.method(result)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
73
spec/lib/spree/core/calculated_adjustments_spec.rb
Normal file
73
spec/lib/spree/core/calculated_adjustments_spec.rb
Normal file
@@ -0,0 +1,73 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
# Its pretty difficult to test this module in isolation b/c it needs to work in conjunction
|
||||
# with an actual class that extends ActiveRecord::Base and has a corresponding table in the DB.
|
||||
# So we'll just test it using Order and ShippingMethod. These classes are including the module.
|
||||
describe Spree::Core::CalculatedAdjustments do
|
||||
let(:calculator) { build(:calculator) }
|
||||
let(:tax_rate) { Spree::TaxRate.new(calculator: calculator) }
|
||||
|
||||
before do
|
||||
allow(calculator).to receive(:compute) { 10 }
|
||||
allow(calculator).to receive(:[]) { nil }
|
||||
end
|
||||
|
||||
it "should add has_one :calculator relationship" do
|
||||
assert Spree::ShippingMethod.
|
||||
reflect_on_all_associations(:has_one).map(&:name).include?(:calculator)
|
||||
end
|
||||
|
||||
context "#create_adjustment and its resulting adjustment" do
|
||||
let(:order) { Spree::Order.create }
|
||||
let(:target) { order }
|
||||
|
||||
it "should be associated with the target" do
|
||||
expect(target.adjustments).to receive(:create)
|
||||
tax_rate.create_adjustment("foo", target, order)
|
||||
end
|
||||
|
||||
it "should have the correct originator and an amount derived from the calculator and supplied calculable" do
|
||||
adjustment = tax_rate.create_adjustment("foo", target, order)
|
||||
expect(adjustment).not_to be_nil
|
||||
expect(adjustment.amount).to eq 10
|
||||
expect(adjustment.source).to eq order
|
||||
expect(adjustment.originator).to eq tax_rate
|
||||
end
|
||||
|
||||
it "should be mandatory if true is supplied for that parameter" do
|
||||
adjustment = tax_rate.create_adjustment("foo", target, order, true)
|
||||
expect(adjustment).to be_mandatory
|
||||
end
|
||||
|
||||
context "when the calculator returns 0" do
|
||||
before { allow(calculator).to receive_messages(compute: 0) }
|
||||
|
||||
context "when adjustment is mandatory" do
|
||||
before { tax_rate.create_adjustment("foo", target, order, true) }
|
||||
|
||||
it "should create an adjustment" do
|
||||
expect(Spree::Adjustment.count).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
context "when adjustment is not mandatory" do
|
||||
before { tax_rate.create_adjustment("foo", target, order, false) }
|
||||
|
||||
it "should not create an adjustment" do
|
||||
expect(Spree::Adjustment.count).to eq 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "#update_adjustment" do
|
||||
it "should update the adjustment using its calculator (and the specified source)" do
|
||||
adjustment = double(:adjustment).as_null_object
|
||||
calculable = double :calculable
|
||||
expect(adjustment).to receive(:update_column).with(:amount, 10)
|
||||
tax_rate.update_adjustment(adjustment, calculable)
|
||||
end
|
||||
end
|
||||
end
|
||||
80
spec/lib/spree/core/mail_interceptor_spec.rb
Normal file
80
spec/lib/spree/core/mail_interceptor_spec.rb
Normal file
@@ -0,0 +1,80 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
# We'll use the OrderMailer as a quick and easy way to test. IF it works here
|
||||
# it works for all email (in theory.)
|
||||
describe Spree::OrderMailer do
|
||||
let(:order) { Spree::Order.new(email: "customer@example.com") }
|
||||
let(:message) { Spree::OrderMailer.confirm_email(order) }
|
||||
|
||||
before(:all) do
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.deliveries.clear
|
||||
end
|
||||
|
||||
context "#deliver" do
|
||||
before do
|
||||
ActionMailer::Base.delivery_method = :test
|
||||
end
|
||||
|
||||
after { ActionMailer::Base.deliveries.clear }
|
||||
|
||||
it "should use the from address specified in the preference" do
|
||||
Spree::Config[:mails_from] = "no-reply@foobar.com"
|
||||
message.deliver
|
||||
@email = ActionMailer::Base.deliveries.first
|
||||
expect(@email.from).to eq ["no-reply@foobar.com"]
|
||||
end
|
||||
|
||||
it "should use the provided from address" do
|
||||
Spree::Config[:mails_from] = "preference@foobar.com"
|
||||
message.from = "override@foobar.com"
|
||||
message.to = "test@test.com"
|
||||
message.deliver
|
||||
email = ActionMailer::Base.deliveries.first
|
||||
expect(email.from).to eq ["override@foobar.com"]
|
||||
expect(email.to).to eq ["test@test.com"]
|
||||
end
|
||||
|
||||
it "should add the bcc email when provided" do
|
||||
Spree::Config[:mail_bcc] = "bcc-foo@foobar.com"
|
||||
message.deliver
|
||||
@email = ActionMailer::Base.deliveries.first
|
||||
expect(@email.bcc).to eq ["bcc-foo@foobar.com"]
|
||||
end
|
||||
|
||||
context "when intercept_email is provided" do
|
||||
it "should strip the bcc recipients" do
|
||||
expect(message.bcc).to be_blank
|
||||
end
|
||||
|
||||
it "should strip the cc recipients" do
|
||||
expect(message.cc).to be_blank
|
||||
end
|
||||
|
||||
it "should replace the receipient with the specified address" do
|
||||
Spree::Config[:intercept_email] = "intercept@foobar.com"
|
||||
message.deliver
|
||||
@email = ActionMailer::Base.deliveries.first
|
||||
expect(@email.to).to eq ["intercept@foobar.com"]
|
||||
end
|
||||
|
||||
it "should modify the subject to include the original email" do
|
||||
Spree::Config[:intercept_email] = "intercept@foobar.com"
|
||||
message.deliver
|
||||
@email = ActionMailer::Base.deliveries.first
|
||||
expect(@email.subject.match(/customer@example\.com/)).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context "when intercept_mode is not provided" do
|
||||
it "should not modify the recipient" do
|
||||
Spree::Config[:intercept_email] = ""
|
||||
message.deliver
|
||||
@email = ActionMailer::Base.deliveries.first
|
||||
expect(@email.to).to eq ["customer@example.com"]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
90
spec/lib/spree/core/mail_settings_spec.rb
Normal file
90
spec/lib/spree/core/mail_settings_spec.rb
Normal file
@@ -0,0 +1,90 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
module Spree
|
||||
module Core
|
||||
describe MailSettings do
|
||||
let!(:subject) { MailSettings.new }
|
||||
|
||||
context "override option is true" do
|
||||
before { Config.override_actionmailer_config = true }
|
||||
|
||||
context "init" do
|
||||
it "calls override!" do
|
||||
expect(MailSettings).to receive(:new).and_return(subject)
|
||||
expect(subject).to receive(:override!)
|
||||
MailSettings.init
|
||||
end
|
||||
end
|
||||
|
||||
context "enable delivery" do
|
||||
before { Config.enable_mail_delivery = true }
|
||||
|
||||
context "overrides appplication defaults" do
|
||||
context "authentication method is none" do
|
||||
before do
|
||||
Config.mail_host = "smtp.example.com"
|
||||
Config.mail_domain = "example.com"
|
||||
Config.mail_port = 123
|
||||
Config.mail_auth_type = MailSettings::SECURE_CONNECTION_TYPES[0]
|
||||
Config.smtp_username = "schof"
|
||||
Config.smtp_password = "hellospree!"
|
||||
Config.secure_connection_type = "TLS"
|
||||
subject.override!
|
||||
end
|
||||
|
||||
it { expect(ActionMailer::Base.smtp_settings[:address]).to eq "smtp.example.com" }
|
||||
it { expect(ActionMailer::Base.smtp_settings[:domain]).to eq "example.com" }
|
||||
it { expect(ActionMailer::Base.smtp_settings[:port]).to eq 123 }
|
||||
it { expect(ActionMailer::Base.smtp_settings[:authentication]).to eq "None" }
|
||||
it { expect(ActionMailer::Base.smtp_settings[:enable_starttls_auto]).to be_truthy }
|
||||
|
||||
it "doesnt touch user name config" do
|
||||
expect(ActionMailer::Base.smtp_settings[:user_name]).to be_nil
|
||||
end
|
||||
|
||||
it "doesnt touch password config" do
|
||||
expect(ActionMailer::Base.smtp_settings[:password]).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when mail_auth_type is other than none" do
|
||||
before do
|
||||
Config.mail_auth_type = "login"
|
||||
Config.smtp_username = "schof"
|
||||
Config.smtp_password = "hellospree!"
|
||||
subject.override!
|
||||
end
|
||||
|
||||
context "overrides user credentials" do
|
||||
it { expect(ActionMailer::Base.smtp_settings[:user_name]).to eq "schof" }
|
||||
it { expect(ActionMailer::Base.smtp_settings[:password]).to eq "hellospree!" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "do not enable delivery" do
|
||||
before do
|
||||
Config.enable_mail_delivery = false
|
||||
subject.override!
|
||||
end
|
||||
|
||||
it { expect(ActionMailer::Base.perform_deliveries).to be_falsy }
|
||||
end
|
||||
end
|
||||
|
||||
context "override option is false" do
|
||||
before { Config.override_actionmailer_config = false }
|
||||
|
||||
context "init" do
|
||||
it "doesnt calls override!" do
|
||||
expect(subject).not_to receive(:override!)
|
||||
MailSettings.init
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
35
spec/lib/spree/core/token_resource_spec.rb
Normal file
35
spec/lib/spree/core/token_resource_spec.rb
Normal file
@@ -0,0 +1,35 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
# Its pretty difficult to test this module in isolation b/c it needs to work in conjunction
|
||||
# with an actual class that extends ActiveRecord::Base and has a corresponding table in the DB.
|
||||
# So we'll just test it using Order instead since it included the module.
|
||||
describe Spree::Core::TokenResource do
|
||||
let(:order) { Spree::Order.new }
|
||||
let(:permission) { double(Spree::TokenizedPermission) }
|
||||
|
||||
it 'should add has_one :tokenized_permission relationship' do
|
||||
assert Spree::Order.
|
||||
reflect_on_all_associations(:has_one).map(&:name).include?(:tokenized_permission)
|
||||
end
|
||||
|
||||
context '#token' do
|
||||
it 'should return the token of the associated permission' do
|
||||
allow(order).to receive_messages tokenized_permission: permission
|
||||
allow(permission).to receive_messages token: 'foo'
|
||||
expect(order.token).to eq 'foo'
|
||||
end
|
||||
|
||||
it 'should return nil if there is no associated permission' do
|
||||
expect(order.token).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context '#create_token' do
|
||||
it 'should create a randomized 16 character token' do
|
||||
token = order.create_token
|
||||
expect(token.size).to eq 16
|
||||
end
|
||||
end
|
||||
end
|
||||
127
spec/lib/spree/i18n_spec.rb
Normal file
127
spec/lib/spree/i18n_spec.rb
Normal file
@@ -0,0 +1,127 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rspec/expectations'
|
||||
require 'spree/i18n'
|
||||
require 'spree/testing_support/i18n'
|
||||
|
||||
describe "i18n" do
|
||||
before do
|
||||
I18n.backend.store_translations(
|
||||
:en,
|
||||
{
|
||||
spree: {
|
||||
foo: "bar",
|
||||
bar: {
|
||||
foo: "bar within bar scope",
|
||||
invalid: nil,
|
||||
legacy_translation: "back in the day..."
|
||||
},
|
||||
invalid: nil,
|
||||
legacy_translation: "back in the day..."
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
it "translates within the spree scope" do
|
||||
expect(Spree.normal_t(:foo)).to eql("bar")
|
||||
expect(Spree.translate(:foo)).to eql("bar")
|
||||
end
|
||||
|
||||
it "translates within the spree scope using a path" do
|
||||
allow(Spree).to receive(:virtual_path).and_return('bar')
|
||||
|
||||
expect(Spree.normal_t('.legacy_translation')).to eql("back in the day...")
|
||||
expect(Spree.translate('.legacy_translation')).to eql("back in the day...")
|
||||
end
|
||||
|
||||
it "raise error without any context when using a path" do
|
||||
expect {
|
||||
Spree.normal_t('.legacy_translation')
|
||||
}.to raise_error
|
||||
|
||||
expect {
|
||||
Spree.translate('.legacy_translation')
|
||||
}.to raise_error
|
||||
end
|
||||
|
||||
it "prepends a string scope" do
|
||||
expect(Spree.normal_t(:foo, scope: "bar")).to eql("bar within bar scope")
|
||||
end
|
||||
|
||||
it "prepends to an array scope" do
|
||||
expect(Spree.normal_t(:foo, scope: ["bar"])).to eql("bar within bar scope")
|
||||
end
|
||||
|
||||
it "returns two translations" do
|
||||
expect(Spree.normal_t([:foo, 'bar.foo'])).to eql(["bar", "bar within bar scope"])
|
||||
end
|
||||
|
||||
it "returns reasonable string for missing translations" do
|
||||
expect(Spree.t(:missing_entry)).to include("<span")
|
||||
end
|
||||
|
||||
context "missed + unused translations" do
|
||||
def key_with_locale(key)
|
||||
"#{key} (#{I18n.locale})"
|
||||
end
|
||||
|
||||
before do
|
||||
Spree.used_translations = []
|
||||
end
|
||||
|
||||
context "missed translations" do
|
||||
def assert_missing_translation(key)
|
||||
key = key_with_locale(key)
|
||||
message = Spree.missing_translation_messages.detect { |m| m == key }
|
||||
expect(message).not_to(be_nil, "expected '#{key}' to be missing, but it wasn't.")
|
||||
end
|
||||
|
||||
it "logs missing translations" do
|
||||
Spree.t(:missing, scope: [:else, :where])
|
||||
Spree.check_missing_translations
|
||||
assert_missing_translation("else")
|
||||
assert_missing_translation("else.where")
|
||||
assert_missing_translation("else.where.missing")
|
||||
end
|
||||
|
||||
it "does not log present translations" do
|
||||
Spree.t(:foo)
|
||||
Spree.check_missing_translations
|
||||
expect(Spree.missing_translation_messages).to be_empty
|
||||
end
|
||||
|
||||
it "does not break when asked for multiple translations" do
|
||||
Spree.t [:foo, 'bar.foo']
|
||||
Spree.check_missing_translations
|
||||
expect(Spree.missing_translation_messages).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context "unused translations" do
|
||||
def assert_unused_translation(key)
|
||||
key = key_with_locale(key)
|
||||
message = Spree.unused_translation_messages.detect { |m| m == key }
|
||||
expect(message).not_to(be_nil, "expected '#{key}' to be unused, but it was used.")
|
||||
end
|
||||
|
||||
def assert_used_translation(key)
|
||||
key = key_with_locale(key)
|
||||
message = Spree.unused_translation_messages.detect { |m| m == key }
|
||||
expect(message).to(be_nil, "expected '#{key}' to be used, but it wasn't.")
|
||||
end
|
||||
|
||||
it "logs translations that aren't used" do
|
||||
Spree.check_unused_translations
|
||||
assert_unused_translation("bar.legacy_translation")
|
||||
assert_unused_translation("legacy_translation")
|
||||
end
|
||||
|
||||
it "does not log used translations" do
|
||||
Spree.t(:foo)
|
||||
Spree.check_unused_translations
|
||||
assert_used_translation("foo")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
135
spec/lib/spree/money_spec.rb
Normal file
135
spec/lib/spree/money_spec.rb
Normal file
@@ -0,0 +1,135 @@
|
||||
# frozen_string_literal: false
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Spree::Money do
|
||||
before do
|
||||
configure_spree_preferences do |config|
|
||||
config.currency = "USD"
|
||||
config.currency_symbol_position = :before
|
||||
config.display_currency = false
|
||||
end
|
||||
end
|
||||
|
||||
it "formats correctly" do
|
||||
money = Spree::Money.new(10)
|
||||
expect(money.to_s).to eq("$10.00")
|
||||
end
|
||||
|
||||
it "can get cents" do
|
||||
money = Spree::Money.new(10)
|
||||
expect(money.cents).to eq(1000)
|
||||
end
|
||||
|
||||
context "with currency" do
|
||||
it "passed in option" do
|
||||
money = Spree::Money.new(10, with_currency: true, html: false)
|
||||
expect(money.to_s).to eq("$10.00 USD")
|
||||
end
|
||||
|
||||
it "config option" do
|
||||
Spree::Config[:display_currency] = true
|
||||
money = Spree::Money.new(10, html: false)
|
||||
expect(money.to_s).to eq("$10.00 USD")
|
||||
end
|
||||
end
|
||||
|
||||
context "hide cents" do
|
||||
it "hides cents suffix" do
|
||||
Spree::Config[:hide_cents] = true
|
||||
money = Spree::Money.new(10)
|
||||
expect(money.to_s).to eq("$10")
|
||||
end
|
||||
|
||||
it "shows cents suffix" do
|
||||
Spree::Config[:hide_cents] = false
|
||||
money = Spree::Money.new(10)
|
||||
expect(money.to_s).to eq("$10.00")
|
||||
end
|
||||
end
|
||||
|
||||
context "currency parameter" do
|
||||
context "when currency is specified in Canadian Dollars" do
|
||||
it "uses the currency param over the global configuration" do
|
||||
money = Spree::Money.new(10, currency: 'CAD', with_currency: true, html: false)
|
||||
expect(money.to_s).to eq("$10.00 CAD")
|
||||
end
|
||||
end
|
||||
|
||||
context "when currency is specified in Japanese Yen" do
|
||||
it "uses the currency param over the global configuration" do
|
||||
money = Spree::Money.new(100, currency: 'JPY', html: false)
|
||||
expect(money.to_s).to eq("¥100")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "symbol positioning" do
|
||||
it "passed in option" do
|
||||
money = Spree::Money.new(10, symbol_position: :after, html: false)
|
||||
expect(money.to_s).to eq("10.00 $")
|
||||
end
|
||||
|
||||
it "passed in option string" do
|
||||
money = Spree::Money.new(10, symbol_position: "after", html: false)
|
||||
expect(money.to_s).to eq("10.00 $")
|
||||
end
|
||||
|
||||
it "config option" do
|
||||
Spree::Config[:currency_symbol_position] = :after
|
||||
money = Spree::Money.new(10, html: false)
|
||||
expect(money.to_s).to eq("10.00 $")
|
||||
end
|
||||
end
|
||||
|
||||
context "JPY" do
|
||||
before do
|
||||
configure_spree_preferences do |config|
|
||||
config.currency = "JPY"
|
||||
config.currency_symbol_position = :before
|
||||
config.display_currency = false
|
||||
end
|
||||
end
|
||||
|
||||
it "formats correctly" do
|
||||
money = Spree::Money.new(1000, html: false)
|
||||
expect(money.to_s).to eq("¥1,000")
|
||||
end
|
||||
end
|
||||
|
||||
context "EUR" do
|
||||
before do
|
||||
configure_spree_preferences do |config|
|
||||
config.currency = "EUR"
|
||||
config.currency_symbol_position = :after
|
||||
config.display_currency = false
|
||||
end
|
||||
end
|
||||
|
||||
# Regression test for Spree #2634
|
||||
it "formats as plain by default" do
|
||||
money = Spree::Money.new(10)
|
||||
expect(money.to_s).to eq("10.00 €")
|
||||
end
|
||||
|
||||
# Regression test for Spree #2632
|
||||
it "acknowledges decimal mark option" do
|
||||
Spree::Config[:currency_decimal_mark] = ","
|
||||
money = Spree::Money.new(10)
|
||||
expect(money.to_s).to eq("10,00 €")
|
||||
end
|
||||
|
||||
# Regression test for Spree #2632
|
||||
it "acknowledges thousands separator option" do
|
||||
Spree::Config[:currency_thousands_separator] = "."
|
||||
money = Spree::Money.new(1000)
|
||||
expect(money.to_s).to eq("1.000.00 €")
|
||||
end
|
||||
|
||||
it "formats as HTML if asked (nicely) to" do
|
||||
money = Spree::Money.new(10)
|
||||
# The HTMLified version of the euro sign
|
||||
expect(money.to_html).to eq("10.00 €")
|
||||
end
|
||||
end
|
||||
end
|
||||
86
spec/lib/spree/product_duplicator_spec.rb
Normal file
86
spec/lib/spree/product_duplicator_spec.rb
Normal file
@@ -0,0 +1,86 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
module Spree
|
||||
describe Spree::ProductDuplicator do
|
||||
let(:product) do
|
||||
double 'Product',
|
||||
name: "foo",
|
||||
taxons: [],
|
||||
product_properties: [property],
|
||||
master: variant,
|
||||
has_variants?: false
|
||||
end
|
||||
|
||||
let(:new_product) do
|
||||
double 'New Product',
|
||||
save!: true
|
||||
end
|
||||
|
||||
let(:property) do
|
||||
double 'Property'
|
||||
end
|
||||
|
||||
let(:new_property) do
|
||||
double 'New Property'
|
||||
end
|
||||
|
||||
let(:variant) do
|
||||
double 'Variant',
|
||||
sku: "12345",
|
||||
price: 19.99,
|
||||
currency: "AUD",
|
||||
images: [image]
|
||||
end
|
||||
|
||||
let(:new_variant) do
|
||||
double 'New Variant',
|
||||
sku: "12345"
|
||||
end
|
||||
|
||||
let(:image) do
|
||||
double 'Image',
|
||||
attachment: double('Attachment')
|
||||
end
|
||||
|
||||
let(:new_image) do
|
||||
double 'New Image'
|
||||
end
|
||||
|
||||
before do
|
||||
expect(product).to receive(:dup).and_return(new_product)
|
||||
expect(variant).to receive(:dup).and_return(new_variant)
|
||||
expect(image).to receive(:dup).and_return(new_image)
|
||||
expect(property).to receive(:dup).and_return(new_property)
|
||||
end
|
||||
|
||||
it "can duplicate a product" do
|
||||
duplicator = Spree::ProductDuplicator.new(product)
|
||||
expect(new_product).to receive(:name=).with("COPY OF foo")
|
||||
expect(new_product).to receive(:taxons=).with([])
|
||||
expect(new_product).to receive(:product_properties=).with([new_property])
|
||||
expect(new_product).to receive(:created_at=).with(nil)
|
||||
expect(new_product).to receive(:updated_at=).with(nil)
|
||||
expect(new_product).to receive(:deleted_at=).with(nil)
|
||||
expect(new_product).to receive(:master=).with(new_variant)
|
||||
|
||||
expect(new_variant).to receive(:sku=).with("COPY OF 12345")
|
||||
expect(new_variant).to receive(:deleted_at=).with(nil)
|
||||
expect(new_variant).to receive(:images=).with([new_image])
|
||||
expect(new_variant).to receive(:price=).with(variant.price)
|
||||
expect(new_variant).to receive(:currency=).with(variant.currency)
|
||||
|
||||
expect(image.attachment).to receive(:clone).and_return(image.attachment)
|
||||
|
||||
expect(new_image).to receive(:assign_attributes).
|
||||
with(attachment: image.attachment).
|
||||
and_return(new_image)
|
||||
|
||||
expect(new_property).to receive(:created_at=).with(nil)
|
||||
expect(new_property).to receive(:updated_at=).with(nil)
|
||||
|
||||
duplicator.duplicate
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user