spree_core 2.2.14 → 2.3.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 (172) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/{spree.js.coffee → spree.js.coffee.erb} +11 -2
  3. data/app/controllers/spree/base_controller.rb +1 -0
  4. data/app/helpers/spree/base_helper.rb +5 -6
  5. data/app/helpers/spree/orders_helper.rb +4 -0
  6. data/app/helpers/spree/products_helper.rb +8 -7
  7. data/app/mailers/spree/base_mailer.rb +2 -2
  8. data/app/mailers/spree/order_mailer.rb +2 -2
  9. data/app/mailers/spree/shipment_mailer.rb +1 -1
  10. data/app/mailers/spree/test_mailer.rb +1 -1
  11. data/app/models/spree/ability.rb +15 -16
  12. data/app/models/spree/address.rb +3 -6
  13. data/app/models/spree/adjustment.rb +2 -2
  14. data/app/models/spree/alert.rb +1 -1
  15. data/app/models/spree/app_configuration.rb +17 -20
  16. data/app/models/spree/asset.rb +2 -2
  17. data/app/models/spree/base.rb +9 -0
  18. data/app/models/spree/calculator.rb +1 -1
  19. data/app/models/spree/calculator/flat_rate.rb +1 -1
  20. data/app/models/spree/calculator/flexi_rate.rb +1 -1
  21. data/app/models/spree/calculator/price_sack.rb +1 -3
  22. data/app/models/spree/calculator/shipping/flat_rate.rb +1 -1
  23. data/app/models/spree/calculator/shipping/flexi_rate.rb +1 -1
  24. data/app/models/spree/calculator/shipping/per_item.rb +1 -1
  25. data/app/models/spree/calculator/shipping/price_sack.rb +1 -3
  26. data/app/models/spree/classification.rb +1 -1
  27. data/app/models/spree/configuration.rb +1 -1
  28. data/app/models/spree/country.rb +1 -1
  29. data/app/models/spree/credit_card.rb +8 -12
  30. data/app/models/spree/gateway.rb +0 -3
  31. data/app/models/spree/gateway/bogus.rb +2 -3
  32. data/app/models/spree/image.rb +3 -1
  33. data/app/models/spree/inventory_unit.rb +5 -6
  34. data/app/models/spree/item_adjustments.rb +3 -4
  35. data/app/models/spree/legacy_user.rb +1 -1
  36. data/app/models/spree/line_item.rb +6 -13
  37. data/app/models/spree/log_entry.rb +1 -1
  38. data/app/models/spree/option_type.rb +1 -1
  39. data/app/models/spree/option_value.rb +1 -3
  40. data/app/models/spree/order.rb +52 -70
  41. data/app/models/spree/order/checkout.rb +17 -10
  42. data/app/models/spree/order/currency_updater.rb +1 -1
  43. data/app/models/spree/order_contents.rb +7 -4
  44. data/app/models/spree/order_populator.rb +1 -1
  45. data/app/models/spree/order_updater.rb +8 -21
  46. data/app/models/spree/payment.rb +26 -12
  47. data/app/models/spree/payment/processing.rb +5 -16
  48. data/app/models/spree/payment_capture_event.rb +1 -1
  49. data/app/models/spree/payment_method.rb +2 -2
  50. data/app/models/spree/payment_method/check.rb +0 -2
  51. data/app/models/spree/preference.rb +1 -31
  52. data/app/models/spree/preferences/configuration.rb +2 -6
  53. data/app/models/spree/preferences/preferable.rb +46 -74
  54. data/app/models/spree/preferences/preferable_class_methods.rb +11 -46
  55. data/app/models/spree/preferences/scoped_store.rb +33 -0
  56. data/app/models/spree/preferences/store.rb +8 -7
  57. data/app/models/spree/price.rb +1 -3
  58. data/app/models/spree/product.rb +59 -87
  59. data/app/models/spree/product/scopes.rb +22 -13
  60. data/app/models/spree/product_option_type.rb +1 -1
  61. data/app/models/spree/product_property.rb +1 -3
  62. data/app/models/spree/product_scope/scopes.rb +1 -1
  63. data/app/models/spree/promotion.rb +4 -5
  64. data/app/models/spree/promotion/actions/create_adjustment.rb +11 -2
  65. data/app/models/spree/promotion/actions/create_item_adjustments.rb +19 -2
  66. data/app/models/spree/promotion/actions/create_line_items.rb +2 -12
  67. data/app/models/spree/promotion/rules/user.rb +5 -1
  68. data/app/models/spree/promotion_action.rb +1 -1
  69. data/app/models/spree/promotion_action_line_item.rb +1 -1
  70. data/app/models/spree/promotion_handler/cart.rb +2 -14
  71. data/app/models/spree/promotion_handler/coupon.rb +3 -13
  72. data/app/models/spree/promotion_rule.rb +1 -1
  73. data/app/models/spree/property.rb +1 -3
  74. data/app/models/spree/prototype.rb +1 -1
  75. data/app/models/spree/return_authorization.rb +4 -10
  76. data/app/models/spree/role.rb +1 -1
  77. data/app/models/spree/shipment.rb +1 -9
  78. data/app/models/spree/shipping_category.rb +3 -3
  79. data/app/models/spree/shipping_method.rb +1 -1
  80. data/app/models/spree/shipping_method_category.rb +2 -2
  81. data/app/models/spree/shipping_rate.rb +3 -3
  82. data/app/models/spree/state.rb +1 -1
  83. data/app/models/spree/state_change.rb +1 -1
  84. data/app/models/spree/stock/availability_validator.rb +7 -3
  85. data/app/models/spree/stock/package.rb +0 -23
  86. data/app/models/spree/stock/splitter/backordered.rb +1 -1
  87. data/app/models/spree/stock/splitter/shipping_category.rb +1 -1
  88. data/app/models/spree/stock/splitter/weight.rb +1 -1
  89. data/app/models/spree/stock_item.rb +7 -10
  90. data/app/models/spree/stock_location.rb +2 -6
  91. data/app/models/spree/stock_movement.rb +1 -3
  92. data/app/models/spree/stock_transfer.rb +1 -3
  93. data/app/models/spree/store.rb +33 -0
  94. data/app/models/spree/tax_category.rb +2 -2
  95. data/app/models/spree/tax_rate.rb +21 -52
  96. data/app/models/spree/taxon.rb +9 -8
  97. data/app/models/spree/taxonomy.rb +1 -1
  98. data/app/models/spree/tracker.rb +1 -1
  99. data/app/models/spree/variant.rb +13 -15
  100. data/app/models/spree/variant/scopes.rb +1 -1
  101. data/app/models/spree/zone.rb +22 -22
  102. data/app/models/spree/zone_member.rb +2 -2
  103. data/config/initializers/user_class_extensions.rb +0 -8
  104. data/config/locales/en.yml +7 -42
  105. data/db/default/spree/countries.rb +2 -3
  106. data/db/default/spree/stores.rb +9 -0
  107. data/db/migrate/20130611054351_rename_shipping_methods_zones_to_spree_shipping_methods_zones.rb +0 -5
  108. data/db/migrate/20130807024301_upgrade_adjustments.rb +4 -5
  109. data/db/migrate/20130807024302_rename_adjustment_fields.rb +5 -2
  110. data/db/migrate/20131118183431_add_line_item_id_to_spree_inventory_units.rb +1 -1
  111. data/db/migrate/20140106065820_remove_value_type_from_spree_preferences.rb +8 -0
  112. data/db/migrate/20140227112348_add_preference_store_to_everything.rb +8 -0
  113. data/db/migrate/20140309023735_migrate_old_preferences.rb +23 -0
  114. data/db/migrate/20140309024355_create_spree_stores.rb +25 -0
  115. data/db/migrate/20140309033438_create_store_from_preferences.rb +30 -0
  116. data/db/migrate/20140315053743_add_timestamps_to_spree_assets.rb +6 -0
  117. data/db/migrate/20140331100557_add_additional_store_fields.rb +8 -0
  118. data/db/migrate/20140410141842_add_many_missing_indexes.rb +18 -0
  119. data/db/migrate/20140410150358_correct_some_polymorphic_index_and_add_more_missing.rb +66 -0
  120. data/db/migrate/20140508151342_change_spree_price_amount_precision.rb +1 -1
  121. data/db/migrate/20140518174634_add_token_to_spree_orders.rb +5 -0
  122. data/db/migrate/20140530024945_move_order_token_from_tokenized_permission.rb +29 -0
  123. data/db/migrate/20140601011216_set_shipment_total_for_users_upgrading.rb +5 -3
  124. data/db/migrate/20140604135309_drop_credit_card_first_name_and_last_name.rb +6 -0
  125. data/lib/generators/spree/dummy/dummy_generator.rb +1 -0
  126. data/lib/generators/spree/dummy/templates/initializers/devise.rb +3 -0
  127. data/lib/generators/spree/dummy/templates/rails/routes.rb +0 -1
  128. data/lib/generators/spree/install/install_generator.rb +8 -17
  129. data/lib/generators/spree/install/templates/config/initializers/spree.rb +2 -2
  130. data/lib/spree/core.rb +13 -9
  131. data/lib/spree/core/calculated_adjustments.rb +1 -1
  132. data/lib/spree/core/controller_helpers/auth.rb +27 -18
  133. data/lib/spree/core/controller_helpers/common.rb +2 -2
  134. data/lib/spree/core/controller_helpers/order.rb +15 -24
  135. data/lib/spree/core/controller_helpers/store.rb +19 -0
  136. data/lib/spree/core/delegate_belongs_to.rb +2 -2
  137. data/lib/spree/core/engine.rb +0 -10
  138. data/lib/spree/core/importer.rb +1 -0
  139. data/lib/spree/core/importer/order.rb +16 -44
  140. data/lib/spree/core/importer/product.rb +62 -0
  141. data/lib/spree/core/product_filters.rb +0 -4
  142. data/lib/spree/core/routes.rb +4 -6
  143. data/lib/spree/core/validators/email.rb +23 -1
  144. data/lib/spree/core/version.rb +1 -1
  145. data/lib/spree/money.rb +1 -169
  146. data/lib/spree/permitted_attributes.rb +6 -4
  147. data/lib/spree/testing_support/authorization_helpers.rb +23 -21
  148. data/lib/spree/testing_support/capybara_ext.rb +11 -21
  149. data/lib/spree/testing_support/common_rake.rb +3 -1
  150. data/lib/spree/testing_support/controller_requests.rb +0 -2
  151. data/lib/spree/testing_support/factories/credit_card_factory.rb +1 -1
  152. data/lib/spree/testing_support/factories/line_item_factory.rb +4 -1
  153. data/lib/spree/testing_support/factories/order_factory.rb +5 -4
  154. data/lib/spree/testing_support/factories/product_factory.rb +0 -4
  155. data/lib/spree/testing_support/factories/promotion_factory.rb +5 -7
  156. data/lib/spree/testing_support/factories/shipment_factory.rb +0 -1
  157. data/lib/spree/testing_support/factories/stock_factory.rb +2 -2
  158. data/lib/spree/testing_support/factories/store_factory.rb +8 -0
  159. data/lib/spree/testing_support/preferences.rb +3 -3
  160. data/lib/tasks/core.rake +2 -2
  161. metadata +48 -39
  162. data/app/models/spree/stock/order_counter.rb +0 -55
  163. data/app/models/spree/tokenized_permission.rb +0 -6
  164. data/app/views/spree/shared/_routes.html.erb +0 -13
  165. data/db/migrate/20140804185157_add_default_to_shipment_cost.rb +0 -10
  166. data/db/migrate/20141021194502_add_state_lock_version_to_order.rb +0 -5
  167. data/lib/spree/core/adjustment_source.rb +0 -26
  168. data/lib/spree/core/mail_interceptor.rb +0 -22
  169. data/lib/spree/core/mail_method.rb +0 -27
  170. data/lib/spree/core/mail_settings.rb +0 -55
  171. data/lib/spree/core/ransackable_attributes.rb +0 -15
  172. data/lib/spree/core/token_resource.rb +0 -27
@@ -0,0 +1,19 @@
1
+ module Spree
2
+ module Core
3
+ module ControllerHelpers
4
+ module Store
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+
9
+ def current_store
10
+ @current_store ||= Spree::Store.current(request.env['SERVER_NAME'])
11
+ end
12
+ helper_method :current_store
13
+
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+ end
@@ -4,8 +4,8 @@
4
4
  #
5
5
  # Todo - integrate with ActiveRecord::Dirty to make sure changes to delegate object are noticed
6
6
  # Should do
7
- # class User < ActiveRecord::Base; delegate_belongs_to :contact, :firstname; end
8
- # class Contact < ActiveRecord::Base; end
7
+ # class User < Spree::Base; delegate_belongs_to :contact, :firstname; end
8
+ # class Contact < Spree::Base; end
9
9
  # u = User.first
10
10
  # u.changed? # => false
11
11
  # u.firstname = 'Bobby'
@@ -9,10 +9,6 @@ module Spree
9
9
  Spree::Config = app.config.spree.preferences #legacy access
10
10
  end
11
11
 
12
- initializer "spree.load_preferences", :before => "spree.environment" do
13
- ::ActiveRecord::Base.send :include, Spree::Preferences::Preferable
14
- end
15
-
16
12
  initializer "spree.register.calculators" do |app|
17
13
  app.config.spree.calculators.shipping_methods = [
18
14
  Spree::Calculator::Shipping::FlatPercentItemTotal,
@@ -39,12 +35,6 @@ module Spree
39
35
  Spree::PaymentMethod::Check ]
40
36
  end
41
37
 
42
- initializer "spree.mail.settings" do |app|
43
- ActionMailer::Base.add_delivery_method :spree, Spree::Core::MailMethod
44
- Spree::Core::MailSettings.init
45
- Mail.register_interceptor(Spree::Core::MailInterceptor)
46
- end
47
-
48
38
  # We need to define promotions rules here so extensions and existing apps
49
39
  # can add their custom classes on their initializer files
50
40
  initializer 'spree.promo.environment' do |app|
@@ -6,3 +6,4 @@ module Spree
6
6
  end
7
7
 
8
8
  require 'spree/core/importer/order'
9
+ require 'spree/core/importer/product'
@@ -13,34 +13,20 @@ module Spree
13
13
  order = Spree::Order.create!
14
14
  order.associate_user!(user)
15
15
 
16
- shipments_attrs = params.delete(:shipments_attributes)
17
-
18
- create_shipments_from_params(shipments_attrs, order)
16
+ create_shipments_from_params(params.delete(:shipments_attributes), order)
19
17
  create_line_items_from_params(params.delete(:line_items_attributes),order)
20
18
  create_adjustments_from_params(params.delete(:adjustments_attributes), order)
21
19
  create_payments_from_params(params.delete(:payments_attributes), order)
22
20
 
23
- if completed_at = params.delete(:completed_at)
21
+ if(completed_at = params.delete(:completed_at))
24
22
  order.completed_at = completed_at
25
23
  order.state = 'complete'
26
24
  end
27
25
 
28
- user_id = params.delete(:user_id)
29
- if user.has_spree_role? "admin"
30
- order.user_id = user_id
31
- end
32
-
33
26
  order.update_attributes!(params)
34
-
35
- order.create_proposed_shipments unless shipments_attrs.present?
36
-
37
- # Really ensure that the order totals & states are correct
38
- order.updater.update
39
- if shipments_attrs.present?
40
- order.shipments.each_with_index do |shipment, index|
41
- shipment.update_columns(cost: shipments_attrs[index][:cost].to_f) if shipments_attrs[index][:cost].present?
42
- end
43
- end
27
+ # Really ensure that the order totals are correct
28
+ order.update_totals
29
+ order.persist_totals
44
30
  order.reload
45
31
  rescue Exception => e
46
32
  order.destroy if order && order.persisted?
@@ -53,13 +39,8 @@ module Spree
53
39
  shipments_hash.each do |s|
54
40
  begin
55
41
  shipment = order.shipments.build
56
- shipment.tracking = s[:tracking]
57
- shipment.stock_location = Spree::StockLocation.find_by_admin_name(s[:stock_location]) || Spree::StockLocation.find_by_name!(s[:stock_location])
58
-
59
- if s[:shipped_at].present?
60
- shipment.shipped_at = s[:shipped_at]
61
- shipment.state = 'shipped'
62
- end
42
+ shipment.tracking = s[:tracking]
43
+ shipment.stock_location = Spree::StockLocation.find_by_name!(s[:stock_location])
63
44
 
64
45
  inventory_units = s[:inventory_units] || []
65
46
  inventory_units.each do |iu|
@@ -76,7 +57,6 @@ module Spree
76
57
  rate = shipment.shipping_rates.create!(:shipping_method => shipping_method,
77
58
  :cost => s[:cost])
78
59
  shipment.selected_shipping_rate_id = rate.id
79
- shipment.update_amounts
80
60
 
81
61
  rescue Exception => e
82
62
  raise "Order import shipments: #{e.message} #{s}"
@@ -88,15 +68,12 @@ module Spree
88
68
  return {} unless line_items_hash
89
69
  line_items_hash.each_key do |k|
90
70
  begin
91
- extra_params = line_items_hash[k].except(:variant_id, :quantity, :sku)
92
- line_item = ensure_variant_id_from_params(line_items_hash[k])
71
+ line_item = line_items_hash[k]
72
+ ensure_variant_id_from_params(line_item)
73
+
74
+ extra_params = line_item.except(:variant_id, :quantity)
93
75
  line_item = order.contents.add(Spree::Variant.find(line_item[:variant_id]), line_item[:quantity])
94
- # Raise any errors with saving to prevent import succeeding with line items failing silently.
95
- if extra_params.present?
96
- line_item.update_attributes!(extra_params)
97
- else
98
- line_item.save!
99
- end
76
+ line_item.update_attributes(extra_params) unless extra_params.empty?
100
77
  rescue Exception => e
101
78
  raise "Order import line items: #{e.message} #{line_item}"
102
79
  end
@@ -121,11 +98,9 @@ module Spree
121
98
  return [] unless payments_hash
122
99
  payments_hash.each do |p|
123
100
  begin
124
- payment = order.payments.build order: order
101
+ payment = order.payments.build
125
102
  payment.amount = p[:amount].to_f
126
- # Order API should be using state as that's the normal payment field.
127
- # spree_wombat serializes payment state as status so imported orders should fall back to status field.
128
- payment.state = p[:state] || p[:status] || 'completed'
103
+ payment.state = p.fetch(:state, 'completed')
129
104
  payment.payment_method = Spree::PaymentMethod.find_by_name!(p[:payment_method])
130
105
  payment.save!
131
106
  rescue Exception => e
@@ -136,13 +111,10 @@ module Spree
136
111
 
137
112
  def self.ensure_variant_id_from_params(hash)
138
113
  begin
139
- sku = hash.delete(:sku)
140
114
  unless hash[:variant_id].present?
141
- hash[:variant_id] = Spree::Variant.active.find_by_sku!(sku).id
115
+ hash[:variant_id] = Spree::Variant.active.find_by_sku!(hash[:sku]).id
116
+ hash.delete(:sku)
142
117
  end
143
- hash
144
- rescue ActiveRecord::RecordNotFound => e
145
- raise "Ensure order import variant: Variant w/SKU #{sku} not found."
146
118
  rescue Exception => e
147
119
  raise "Ensure order import variant: #{e.message} #{hash}"
148
120
  end
@@ -0,0 +1,62 @@
1
+ module Spree
2
+ module Core
3
+ module Importer
4
+ class Product
5
+ attr_reader :product, :product_attrs, :variants_attrs, :options_attrs
6
+
7
+ def initialize(product, product_params, options = {})
8
+ @product = product || Spree::Product.new(product_params)
9
+
10
+ @product_attrs = product_params
11
+ @variants_attrs = options[:variants_attrs] || []
12
+ @options_attrs = options[:options_attrs] || []
13
+ end
14
+
15
+ def create
16
+ if product.save
17
+ variants_attrs.each do |variant_attribute|
18
+ # make sure the product is assigned before the options=
19
+ product.variants.create({ product: product }.merge(variant_attribute))
20
+ end
21
+
22
+ set_up_options
23
+ end
24
+
25
+ product
26
+ end
27
+
28
+ def update
29
+ if product.update_attributes(product_attrs)
30
+ variants_attrs.each do |variant_attribute|
31
+ # update the variant if the id is present in the payload
32
+ if variant_attribute['id'].present?
33
+ product.variants.find(variant_attribute['id'].to_i).update_attributes(variant_attribute)
34
+ else
35
+ # make sure the product is assigned before the options=
36
+ product.variants.create({ product: product }.merge(variant_attribute))
37
+ end
38
+ end
39
+
40
+ set_up_options
41
+ end
42
+
43
+ product
44
+ end
45
+
46
+ private
47
+ def set_up_options
48
+ options_attrs.each do |name|
49
+ option_type = Spree::OptionType.where(name: name).first_or_initialize do |option_type|
50
+ option_type.presentation = name
51
+ option_type.save!
52
+ end
53
+
54
+ unless product.option_types.include?(option_type)
55
+ product.option_types << option_type
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -42,10 +42,6 @@ module Spree
42
42
  # or taxons), eg see the taxon model/controller.
43
43
 
44
44
  # See specific filters below for concrete examples.
45
-
46
- # This module is included by Taxon. In development mode that inclusion does not
47
- # happen until Taxon class is loaded. Ensure that Taxon class is loaded before
48
- # you try something like Product.price_range_any
49
45
  module ProductFilters
50
46
  # Example: filtering by price
51
47
  # The named scope just maps incoming labels onto their conditions, and builds the conjunction
@@ -38,11 +38,9 @@ module Spree
38
38
  @append_routes = []
39
39
  end
40
40
 
41
- private
42
-
43
- def eval_block(&block)
44
- Spree::Core::Engine.routes.eval_block(block)
45
- end
41
+ def eval_block(&block)
42
+ Spree::Core::Engine.routes.eval_block(block)
43
+ end
46
44
  end
47
45
  end
48
- end
46
+ end
@@ -1,7 +1,29 @@
1
+ # Borrowed from http://my.rails-royce.org/2010/07/21/email-validation-in-ruby-on-rails-without-regexp/
2
+ # Mentioned in tweet here: https://twitter.com/_sohara/status/177120126083141633
3
+ require 'mail'
1
4
  class EmailValidator < ActiveModel::EachValidator
2
5
  def validate_each(record,attribute,value)
3
- unless value =~ /\A([^@\.]|[^@\.]([^@\s]*)[^@\.])@([^@\s]+\.)+[^@\s]+\z/
6
+ unless valid?(value)
4
7
  record.errors.add(attribute, :invalid, {:value => value}.merge!(options))
5
8
  end
6
9
  end
10
+
11
+ def valid?(email)
12
+ begin
13
+ m = Mail::Address.new(email)
14
+ # We must check that value contains a domain and that value is an email address
15
+ r = m.domain && m.address == email
16
+ t = m.__send__(:tree)
17
+ # We need to dig into treetop
18
+ # A valid domain must have dot_atom_text elements size > 1
19
+ # user@localhost is excluded
20
+ # treetop must respond to domain
21
+ # We exclude valid email values like <user@localhost.com>
22
+ # Hence we use m.__send__(tree).domain
23
+ r &&= (t.domain.dot_atom_text.elements.size > 1)
24
+ rescue Exception => e
25
+ r = false
26
+ end
27
+ r
28
+ end
7
29
  end
@@ -1,5 +1,5 @@
1
1
  module Spree
2
2
  def self.version
3
- '2.2.14'
3
+ "2.3.0"
4
4
  end
5
5
  end
data/lib/spree/money.rb CHANGED
@@ -9,7 +9,7 @@ module Spree
9
9
  delegate :cents, :to => :money
10
10
 
11
11
  def initialize(amount, options={})
12
- @money = self.class.parse([amount, (options[:currency] || Spree::Config[:currency])].join)
12
+ @money = Monetize.parse([amount, (options[:currency] || Spree::Config[:currency])].join)
13
13
  @options = {}
14
14
  @options[:with_currency] = Spree::Config[:display_currency]
15
15
  @options[:symbol_position] = Spree::Config[:currency_symbol_position].to_sym
@@ -22,174 +22,6 @@ module Spree
22
22
  @options[:symbol_position] = @options[:symbol_position].to_sym
23
23
  end
24
24
 
25
- # This method is being deprecated in Money 6.1.0, so now lives here.
26
- def self.parse(input, currency = nil)
27
- i = input.to_s.strip
28
-
29
- # raise Money::Currency.table.collect{|c| c[1][:symbol]}.inspect
30
-
31
- # Check the first character for a currency symbol, alternatively get it
32
- # from the stated currency string
33
- c = if ::Monetize.assume_from_symbol && i =~ /^(\$|€|£)/
34
- case i
35
- when /^\$/ then "USD"
36
- when /^€/ then "EUR"
37
- when /^£/ then "GBP"
38
- end
39
- else
40
- i[/[A-Z]{2,3}/]
41
- end
42
-
43
- # check that currency passed and embedded currency are the same,
44
- # and negotiate the final currency
45
- if currency.nil? and c.nil?
46
- currency = ::Money.default_currency
47
- elsif currency.nil?
48
- currency = c
49
- elsif c.nil?
50
- currency = currency
51
- elsif currency != c
52
- # TODO: ParseError
53
- raise ArgumentError, "Mismatching Currencies"
54
- end
55
- currency = ::Money::Currency.wrap(currency)
56
-
57
- fractional = extract_cents(i, currency)
58
- ::Money.new(fractional, currency)
59
- end
60
-
61
- # This method is being deprecated in Money 6.1.0, so now lives here.
62
- def self.extract_cents(input, currency = Money.default_currency)
63
- # remove anything that's not a number, potential thousands_separator, or minus sign
64
- num = input.gsub(/[^\d.,'-]/, '')
65
-
66
- # set a boolean flag for if the number is negative or not
67
- negative = num =~ /^-|-$/ ? true : false
68
-
69
- # decimal mark character
70
- decimal_char = currency.decimal_mark
71
-
72
- # if negative, remove the minus sign from the number
73
- # if it's not negative, the hyphen makes the value invalid
74
- if negative
75
- num = num.sub(/^-|-$/, '')
76
- end
77
-
78
- raise ArgumentError, "Invalid currency amount (hyphen)" if num.include?('-')
79
-
80
- #if the number ends with punctuation, just throw it out. If it means decimal,
81
- #it won't hurt anything. If it means a literal period or comma, this will
82
- #save it from being mis-interpreted as a decimal.
83
- num.chop! if num.match(/[\.|,]$/)
84
-
85
- # gather all decimal_marks within the result number
86
- used_delimiters = num.scan(/[^\d]/)
87
-
88
- # determine the number of unique decimal_marks within the number
89
- #
90
- # e.g.
91
- # $1,234,567.89 would return 2 (, and .)
92
- # $125,00 would return 1
93
- # $199 would return 0
94
- # $1 234,567.89 would raise an error (decimal_marks are space, comma, and period)
95
- case used_delimiters.uniq.length
96
- # no decimal_mark or thousands_separator; major (dollars) is the number, and minor (cents) is 0
97
- when 0 then major, minor = num, 0
98
-
99
- # two decimal_marks, so we know the last item in this array is the
100
- # major/minor thousands_separator and the rest are decimal_marks
101
- when 2
102
- thousands_separator, decimal_mark = used_delimiters.uniq
103
-
104
- # remove all thousands_separator, split on the decimal_mark
105
- major, minor = num.gsub(thousands_separator, '').split(decimal_mark)
106
- min = 0 unless min
107
- when 1
108
- # we can't determine if the comma or period is supposed to be a decimal_mark or a thousands_separator
109
- # e.g.
110
- # 1,00 - comma is a thousands_separator
111
- # 1.000 - period is a thousands_separator
112
- # 1,000 - comma is a decimal_mark
113
- # 1,000,000 - comma is a decimal_mark
114
- # 10000,00 - comma is a thousands_separator
115
- # 1000,000 - comma is a thousands_separator
116
-
117
- # assign first decimal_mark for reusability
118
- decimal_mark = used_delimiters.first
119
-
120
- # When we have identified the decimal mark character
121
- if decimal_char == decimal_mark
122
- major, minor = num.split(decimal_char)
123
-
124
- else
125
- # decimal_mark is used as a decimal_mark when there are multiple instances, always
126
- if num.scan(decimal_mark).length > 1 # multiple matches; treat as decimal_mark
127
- major, minor = num.gsub(decimal_mark, ''), 0
128
- else
129
- # ex: 1,000 - 1.0000 - 10001.000
130
- # split number into possible major (dollars) and minor (cents) values
131
- possible_major, possible_minor = num.split(decimal_mark)
132
- possible_major ||= "0"
133
- possible_minor ||= "00"
134
-
135
- # if the minor (cents) length isn't 3, assign major/minor from the possibles
136
- # e.g.
137
- # 1,00 => 1.00
138
- # 1.0000 => 1.00
139
- # 1.2 => 1.20
140
- if possible_minor.length != 3 # thousands_separator
141
- major, minor = possible_major, possible_minor
142
- else
143
- # minor length is three
144
- # let's try to figure out intent of the thousands_separator
145
-
146
- # the major length is greater than three, which means
147
- # the comma or period is used as a thousands_separator
148
- # e.g.
149
- # 1000,000
150
- # 100000,000
151
- if possible_major.length > 3
152
- major, minor = possible_major, possible_minor
153
- else
154
- # number is in format ###{sep}### or ##{sep}### or #{sep}###
155
- # handle as , is sep, . is thousands_separator
156
- if decimal_mark == '.'
157
- major, minor = possible_major, possible_minor
158
- else
159
- major, minor = "#{possible_major}#{possible_minor}", 0
160
- end
161
- end
162
- end
163
- end
164
- end
165
- else
166
- # TODO: ParseError
167
- raise ArgumentError, "Invalid currency amount"
168
- end
169
-
170
- # build the string based on major/minor since decimal_mark/thousands_separator have been removed
171
- # avoiding floating point arithmetic here to ensure accuracy
172
- cents = (major.to_i * currency.subunit_to_unit)
173
- # Because of an bug in JRuby, we can't just call #floor
174
- minor = minor.to_s
175
- minor = if minor.size < currency.decimal_places
176
- (minor + ("0" * currency.decimal_places))[0,currency.decimal_places].to_i
177
- elsif minor.size > currency.decimal_places
178
- if minor[currency.decimal_places,1].to_i >= 5
179
- minor[0,currency.decimal_places].to_i+1
180
- else
181
- minor[0,currency.decimal_places].to_i
182
- end
183
- else
184
- minor.to_i
185
- end
186
-
187
- cents += minor
188
-
189
- # if negative, multiply by -1; otherwise, return positive cents
190
- negative ? cents * -1 : cents
191
- end
192
-
193
25
  def to_s
194
26
  @money.format(@options)
195
27
  end