active_shipping 0.12.4 → 0.12.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/active_shipping.rb +2 -1
  3. data/lib/active_shipping/shipping/base.rb +2 -2
  4. data/lib/active_shipping/shipping/carrier.rb +16 -13
  5. data/lib/active_shipping/shipping/carriers/benchmark_carrier.rb +3 -4
  6. data/lib/active_shipping/shipping/carriers/bogus_carrier.rb +1 -3
  7. data/lib/active_shipping/shipping/carriers/canada_post.rb +33 -44
  8. data/lib/active_shipping/shipping/carriers/canada_post_pws.rb +72 -81
  9. data/lib/active_shipping/shipping/carriers/fedex.rb +118 -109
  10. data/lib/active_shipping/shipping/carriers/kunaki.rb +33 -32
  11. data/lib/active_shipping/shipping/carriers/new_zealand_post.rb +9 -16
  12. data/lib/active_shipping/shipping/carriers/shipwire.rb +36 -35
  13. data/lib/active_shipping/shipping/carriers/stamps.rb +39 -51
  14. data/lib/active_shipping/shipping/carriers/ups.rb +280 -116
  15. data/lib/active_shipping/shipping/carriers/ups.rb.orig +456 -0
  16. data/lib/active_shipping/shipping/carriers/usps.rb +145 -100
  17. data/lib/active_shipping/shipping/carriers/usps.rb.orig +616 -0
  18. data/lib/active_shipping/shipping/errors.rb +1 -1
  19. data/lib/active_shipping/shipping/label_response.rb +25 -0
  20. data/lib/active_shipping/shipping/location.rb +18 -16
  21. data/lib/active_shipping/shipping/package.rb +51 -54
  22. data/lib/active_shipping/shipping/rate_estimate.rb +10 -12
  23. data/lib/active_shipping/shipping/rate_response.rb +3 -7
  24. data/lib/active_shipping/shipping/response.rb +6 -9
  25. data/lib/active_shipping/shipping/shipment_event.rb +2 -4
  26. data/lib/active_shipping/shipping/shipment_packer.rb +32 -17
  27. data/lib/active_shipping/shipping/shipping_response.rb +2 -4
  28. data/lib/active_shipping/shipping/tracking_response.rb +3 -5
  29. data/lib/active_shipping/version.rb +1 -1
  30. data/lib/vendor/quantified/lib/quantified/attribute.rb +79 -80
  31. data/lib/vendor/quantified/lib/quantified/length.rb +5 -5
  32. data/lib/vendor/quantified/lib/quantified/mass.rb +4 -4
  33. data/lib/vendor/quantified/test/length_test.rb +19 -15
  34. data/lib/vendor/quantified/test/mass_test.rb +14 -14
  35. data/lib/vendor/quantified/test/test_helper.rb +1 -2
  36. data/lib/vendor/test_helper.rb +0 -1
  37. data/lib/vendor/xml_node/benchmark/bench_generation.rb +2 -4
  38. data/lib/vendor/xml_node/lib/xml_node.rb +54 -55
  39. data/lib/vendor/xml_node/test/test_generating.rb +23 -28
  40. data/lib/vendor/xml_node/test/test_parsing.rb +5 -8
  41. metadata +6 -25
  42. checksums.yaml.gz.sig +0 -1
  43. data.tar.gz.sig +0 -0
  44. metadata.gz.sig +0 -0
@@ -1,13 +1,11 @@
1
1
  module ActiveMerchant #:nodoc:
2
-
3
2
  module Shipping #:nodoc:
4
-
5
3
  class Error < ActiveMerchant::ActiveMerchantError
6
4
  end
7
-
5
+
8
6
  class ResponseError < Error
9
7
  attr_reader :response
10
-
8
+
11
9
  def initialize(response = nil)
12
10
  if response.is_a? Response
13
11
  super(response.message)
@@ -15,17 +13,16 @@ module ActiveMerchant #:nodoc:
15
13
  else
16
14
  super(response)
17
15
  end
18
- end
16
+ end
19
17
  end
20
-
18
+
21
19
  class Response
22
-
23
20
  attr_reader :params
24
21
  attr_reader :message
25
22
  attr_reader :test
26
23
  attr_reader :xml
27
24
  attr_reader :request
28
-
25
+
29
26
  def initialize(success, message, params = {}, options = {})
30
27
  @success, @message, @params = success, message, params.stringify_keys
31
28
  @test = options[:test] || false
@@ -33,7 +30,7 @@ module ActiveMerchant #:nodoc:
33
30
  @request = options[:request]
34
31
  raise ResponseError.new(self) unless success
35
32
  end
36
-
33
+
37
34
  def success?
38
35
  @success ? true : false
39
36
  end
@@ -1,10 +1,9 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Shipping
3
-
4
3
  class ShipmentEvent
5
4
  attr_reader :name, :time, :location, :message
6
-
7
- def initialize(name, time, location, message=nil)
5
+
6
+ def initialize(name, time, location, message = nil)
8
7
  @name, @time, @location, @message = name, time, location, message
9
8
  end
10
9
 
@@ -16,6 +15,5 @@ module ActiveMerchant #:nodoc:
16
15
  @status ||= name.downcase.gsub("\s", "_").to_sym
17
16
  end
18
17
  end
19
-
20
18
  end
21
19
  end
@@ -13,40 +13,55 @@ module ActiveMerchant
13
13
  # maximum_weight - maximum weight in grams
14
14
  # currency - ISO currency code
15
15
  def self.pack(items, dimensions, maximum_weight, currency)
16
+ return [] if items.empty?
16
17
  packages = []
17
18
 
18
- return packages if items.empty?
19
+ # Naive in that it assumes weight is equally distributed across all items
20
+ # Should raise early enough in most cases
21
+ total_weight = 0
22
+ items.map!(&:symbolize_keys).each do |item|
23
+ total_weight += item[:quantity].to_i * item[:grams].to_i
19
24
 
20
- items.map!(&:symbolize_keys)
25
+ if item[:grams].to_i > maximum_weight
26
+ raise OverweightItem, "The item with weight of #{item[:grams]}g is heavier than the allowable package weight of #{maximum_weight}g"
27
+ end
21
28
 
22
- if items.sum { |item| item[:quantity].to_i } >= EXCESS_PACKAGE_QUANTITY_THRESHOLD
23
- raise ExcessPackageQuantity, "Unable to pack more than #{EXCESS_PACKAGE_QUANTITY_THRESHOLD} packages"
29
+ if total_weight > maximum_weight * EXCESS_PACKAGE_QUANTITY_THRESHOLD
30
+ raise ExcessPackageQuantity, "Unable to pack more than #{EXCESS_PACKAGE_QUANTITY_THRESHOLD} packages"
31
+ end
24
32
  end
25
33
 
26
- items = items.map { |item| [item] * item[:quantity].to_i }.flatten
27
- state = :package_empty
34
+ items = items.deep_dup.sort_by! { |i| i[:grams].to_i }
28
35
 
36
+ state = :package_empty
29
37
  while state != :packing_finished
30
38
  case state
31
39
  when :package_empty
32
40
  package_weight, package_value = 0, 0
33
41
  state = :filling_package
34
42
  when :filling_package
35
- item = items.shift
36
- item_weight, item_price = item[:grams].to_i, Package.cents_from(item[:price])
43
+ items.each do |item|
44
+ quantity = if item[:grams].to_i <= 0
45
+ item[:quantity].to_i
46
+ else
47
+ # Grab the max amount of this item we can fit into this package
48
+ # Or, if there are fewer than the max for this item, put
49
+ # what is left into this package
50
+ [(maximum_weight - package_weight) / item[:grams].to_i, item[:quantity].to_i].min
51
+ end
37
52
 
38
- if item_weight > maximum_weight
39
- raise OverweightItem, "The item with weight of #{item_weight}g is heavier than the allowable package weight of #{maximum_weight}g"
40
- end
53
+ item_weight = quantity * item[:grams].to_i
54
+ item_value = quantity * Package.cents_from(item[:price])
41
55
 
42
- if (package_weight + item_weight) <= maximum_weight
43
56
  package_weight += item_weight
44
- package_value += item_price
45
- state = :package_full if items.empty?
46
- else
47
- items.unshift(item)
48
- state = :package_full
57
+ package_value += item_value
58
+
59
+ item[:quantity] = item[:quantity].to_i - quantity
49
60
  end
61
+
62
+ items.reject! { |i| i[:quantity].to_i == 0 }
63
+
64
+ state = :package_full
50
65
  when :package_full
51
66
  packages << ActiveMerchant::Shipping::Package.new(package_weight, dimensions, :value => package_value, :currency => currency)
52
67
  state = items.any? ? :package_empty : :packing_finished
@@ -1,16 +1,14 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Shipping
3
-
4
3
  class ShippingResponse < Response
5
4
  attr_reader :shipping_id # string
6
5
  attr_reader :tracking_number # string
7
-
6
+
8
7
  def initialize(success, message, params = {}, options = {})
9
8
  @shipping_id = options[:shipping_id]
10
9
  @tracking_number = options[:tracking_number]
11
10
  super
12
11
  end
13
12
  end
14
-
15
13
  end
16
- end
14
+ end
@@ -1,20 +1,19 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Shipping
3
-
4
3
  class TrackingResponse < Response
5
4
  attr_reader :carrier # symbol
6
5
  attr_reader :carrier_name # string
7
6
  attr_reader :status # symbol
8
7
  attr_reader :status_code # string
9
- attr_reader :status_description #string
8
+ attr_reader :status_description # string
10
9
  attr_reader :ship_time # time
11
10
  attr_reader :scheduled_delivery_date # time
12
11
  attr_reader :actual_delivery_date # time
13
- attr_reader :delivery_signature #string
12
+ attr_reader :delivery_signature # string
14
13
  attr_reader :tracking_number # string
15
14
  attr_reader :shipment_events # array of ShipmentEvents in chronological order
16
15
  attr_reader :shipper_address, :origin, :destination # Location objects
17
-
16
+
18
17
  def initialize(success, message, params = {}, options = {})
19
18
  @carrier = options[:carrier].parameterize.to_sym
20
19
  @carrier_name = options[:carrier]
@@ -51,6 +50,5 @@ module ActiveMerchant #:nodoc:
51
50
  alias_method(:scheduled_delivery_time, :scheduled_delivery_date)
52
51
  alias_method(:actual_delivery_time, :actual_delivery_date)
53
52
  end
54
-
55
53
  end
56
54
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveShipping
2
- VERSION = "0.12.4"
2
+ VERSION = "0.12.5"
3
3
  end
@@ -1,206 +1,205 @@
1
1
  module Quantified
2
2
  class Attribute
3
3
  include Comparable
4
-
4
+
5
5
  attr_reader :amount, :unit
6
-
6
+
7
7
  def initialize(amount, unit)
8
8
  raise ArgumentError, "amount must be a Numeric" unless amount.is_a?(Numeric)
9
9
  @amount, @unit = amount, unit.to_sym
10
10
  end
11
-
11
+
12
12
  def to_s
13
13
  "#{amount} #{unit}"
14
14
  end
15
-
15
+
16
16
  def inspect
17
17
  "#<#{self.class.name}: #{amount} #{unit}>"
18
18
  end
19
-
19
+
20
20
  def ==(other)
21
- (BigDecimal.new(self.amount.to_s) == BigDecimal.new(other.amount.to_s) && self.unit == other.unit) || BigDecimal.new(self.class.convert(self.amount, self.unit, other.unit).to_s) == BigDecimal.new(other.amount.to_s)
21
+ (BigDecimal.new(amount.to_s) == BigDecimal.new(other.amount.to_s) && unit == other.unit) || BigDecimal.new(self.class.convert(amount, unit, other.unit).to_s) == BigDecimal.new(other.amount.to_s)
22
22
  rescue NoMethodError
23
- self.amount == other
23
+ amount == other
24
24
  end
25
-
25
+
26
26
  def eql?(other)
27
- self.class == other.class && BigDecimal.new(self.amount.to_s) == BigDecimal.new(other.amount.to_s) && self.unit == other.unit
27
+ self.class == other.class && BigDecimal.new(amount.to_s) == BigDecimal.new(other.amount.to_s) && unit == other.unit
28
28
  end
29
-
29
+
30
30
  def <=>(other)
31
31
  if self.class == other.class
32
- self.class.convert(self.amount, self.unit, other.unit) <=> other.amount
32
+ self.class.convert(amount, unit, other.unit) <=> other.amount
33
33
  else
34
- self.amount <=> other
34
+ amount <=> other
35
35
  end
36
36
  end
37
-
37
+
38
38
  def system
39
39
  self.class.units_to_systems[unit]
40
40
  end
41
41
 
42
42
  def coerce(other)
43
- [other, self.amount]
43
+ [other, amount]
44
44
  end
45
-
45
+
46
46
  def method_missing(meth, *args)
47
47
  if args.size == 1 && self.class == (other = args.first).class
48
- other_amount_in_self_units = self.class.convert(other.amount, other.unit, self.unit)
49
- self.class.new(amount.send(meth, other_amount_in_self_units), self.unit)
48
+ other_amount_in_self_units = self.class.convert(other.amount, other.unit, unit)
49
+ self.class.new(amount.send(meth, other_amount_in_self_units), unit)
50
50
  else
51
51
  amount.send(meth, *args)
52
52
  end
53
53
  end
54
-
54
+
55
55
  def self.conversion_rate(from, to)
56
- return nil unless self.conversions[from] and self.conversions[to]
57
- return self.conversions[from][to] ||=
58
- (1.0 / self.conversions[to][from] if self.conversions[to][from]) || begin
59
- shared_conversions = self.conversions[from].keys & self.conversions[to].keys
56
+ return nil unless conversions[from] and conversions[to]
57
+ conversions[from][to] ||=
58
+ (1.0 / conversions[to][from] if conversions[to][from]) || begin
59
+ shared_conversions = conversions[from].keys & conversions[to].keys
60
60
  if shared_conversions.any?
61
61
  primitive = shared_conversions.first
62
- self.conversions[from][primitive] * (1.0 / self.conversions[to][primitive])
62
+ conversions[from][primitive] * (1.0 / conversions[to][primitive])
63
63
  else
64
- self.conversions[from].each do |conversion_unit, multiple|
65
- if self.conversions[to].include?(conversion_unit)
66
- return multiple * conversion_rate(conversion) * (1.0 / self.conversions[to][conversion_unit])
64
+ conversions[from].each do |conversion_unit, multiple|
65
+ if conversions[to].include?(conversion_unit)
66
+ return multiple * conversion_rate(conversion) * (1.0 / conversions[to][conversion_unit])
67
67
  end
68
68
  end
69
- from_primitive = (self.conversions[from].keys & self.primitives).first
70
- to_primitive = (self.conversions[to].keys & self.primitives).first
69
+ from_primitive = (conversions[from].keys & primitives).first
70
+ to_primitive = (conversions[to].keys & primitives).first
71
71
  if from_primitive_to_primitive_multiple = conversion_rate(from_primitive, to_primitive)
72
- return self.conversions[from][from_primitive] * from_primitive_to_primitive_multiple * (1.0 / self.conversions[to][to_primitive])
72
+ return conversions[from][from_primitive] * from_primitive_to_primitive_multiple * (1.0 / conversions[to][to_primitive])
73
73
  end
74
74
  raise StandardError, "No conversion path from #{from} to #{to}"
75
75
  end
76
76
  end
77
77
  end
78
-
79
- def self.units(system=nil)
78
+
79
+ def self.units(system = nil)
80
80
  if system
81
- self.systems_to_units[system.to_sym].dup
81
+ systems_to_units[system.to_sym].dup
82
82
  else
83
- self.primitives | self.conversions.keys
83
+ primitives | conversions.keys
84
84
  end
85
85
  end
86
-
86
+
87
87
  def self.non_primitives
88
- self.conversions.keys
88
+ conversions.keys
89
89
  end
90
-
90
+
91
91
  def self.systems
92
- self.systems_to_units.keys
92
+ systems_to_units.keys
93
93
  end
94
-
94
+
95
95
  def self.add_numeric_methods?
96
- self.add_numeric_methods
96
+ add_numeric_methods
97
97
  end
98
-
98
+
99
99
  def self.numeric_methods(*args)
100
100
  args.each do |arg|
101
101
  add_numeric_method_for(arg.to_sym)
102
102
  end
103
103
  end
104
-
104
+
105
105
  protected
106
106
 
107
107
  class << self
108
108
  def primitives; @primitives ||= []; end
109
109
  def add_numeric_methods; @add_numeric_methods ||= false; end
110
- def add_numeric_methods=(v); @add_numeric_methods = v; end
110
+ attr_writer :add_numeric_methods
111
111
  def conversions; @conversions ||= {}; end
112
- def current_system; @current_system; end
113
- def current_system=(v); @current_system = v; end
112
+ attr_reader :current_system
113
+ attr_writer :current_system
114
114
  def systems_to_units; @systems_to_units ||= {}; end
115
115
  def units_to_systems; @units_to_systems ||= {}; end
116
116
  end
117
117
 
118
-
119
118
  def self.system(system_name, &block)
120
- old_system = self.current_system
119
+ old_system = current_system
121
120
  self.current_system = system_name.to_sym
122
121
  yield
123
122
  self.current_system = old_system
124
123
  end
125
-
126
- def self.primitive(sym, options={})
124
+
125
+ def self.primitive(sym, options = {})
127
126
  unit_sym = (options[:plural] || sym.to_s.pluralize).to_sym
128
- self.primitives << unit_sym
127
+ primitives << unit_sym
129
128
  add_to_system(unit_sym)
130
129
  add_methods_for(unit_sym, options)
131
130
  end
132
-
131
+
133
132
  def self.add_to_system(unit_sym)
134
- if self.current_system
135
- self.units_to_systems[unit_sym] ||= begin
136
- sys_ary = self.systems_to_units[self.current_system] ||= []
133
+ if current_system
134
+ units_to_systems[unit_sym] ||= begin
135
+ sys_ary = systems_to_units[current_system] ||= []
137
136
  sys_ary << unit_sym
138
- self.current_system
137
+ current_system
139
138
  end
140
139
  end
141
140
  end
142
-
143
- def self.one(sym, options={})
141
+
142
+ def self.one(sym, options = {})
144
143
  unit_sym = (options[:plural] || sym.to_s.pluralize).to_sym
145
144
  add_to_system(unit_sym)
146
145
  register_unit(unit_sym, options[:is].unit, options[:is].amount)
147
146
  add_methods_for(unit_sym, options)
148
147
  end
149
-
148
+
150
149
  def self.register_unit(multiple_unit, other_unit, multiple)
151
150
  multiple_unit, other_unit = multiple_unit.to_sym, other_unit.to_sym
152
- self.conversions[multiple_unit] ||= {}
153
- self.conversions[other_unit] ||= {}
154
-
155
- if self.primitives.include?(multiple_unit) || self.primitives.include?(other_unit)
151
+ conversions[multiple_unit] ||= {}
152
+ conversions[other_unit] ||= {}
153
+
154
+ if primitives.include?(multiple_unit) || primitives.include?(other_unit)
156
155
  add_conversion(multiple_unit, other_unit, multiple)
157
156
  else
158
157
  [multiple_unit, other_unit].each do |this_unit|
159
- self.conversions[this_unit].each do |this_other_unit, this_multiple|
160
- if self.primitives.include?(this_other_unit)
158
+ conversions[this_unit].each do |this_other_unit, this_multiple|
159
+ if primitives.include?(this_other_unit)
161
160
  add_conversion(multiple_unit, this_other_unit, multiple * this_multiple)
162
161
  end
163
162
  end
164
163
  end
165
164
  end
166
165
  end
167
-
166
+
168
167
  def self.add_conversion(multiple_unit, other_unit, multiple)
169
- self.conversions[multiple_unit] ||={}
170
- self.conversions[multiple_unit][other_unit] = multiple
171
- self.conversions[other_unit] ||= {}
172
- self.conversions[other_unit][multiple_unit] = (1.0 / multiple)
168
+ conversions[multiple_unit] ||= {}
169
+ conversions[multiple_unit][other_unit] = multiple
170
+ conversions[other_unit] ||= {}
171
+ conversions[other_unit][multiple_unit] = (1.0 / multiple)
173
172
  end
174
-
173
+
175
174
  def self.convert(amount, from, to)
176
175
  from, to = from.to_sym, to.to_sym
177
176
  amount * conversion_rate(from, to)
178
177
  end
179
-
180
- def self.add_methods_for(sym, options={})
178
+
179
+ def self.add_methods_for(sym, options = {})
181
180
  add_conversion_method_for(sym, options)
182
181
  add_numeric_method = if options.has_key?(:add_numeric_methods)
183
182
  options[:add_numeric_methods]
184
183
  else
185
- self.add_numeric_methods
184
+ add_numeric_methods
186
185
  end
187
186
  add_numeric_method_for(sym.to_s, options) if add_numeric_method
188
187
  end
189
-
190
- def self.add_conversion_method_for(sym, options={})
188
+
189
+ def self.add_conversion_method_for(sym, options = {})
191
190
  unit_name = sym.to_s
192
191
  class_eval do
193
192
  define_method("to_#{unit_name}") do
194
- return self if unit_name == self.unit.to_s
195
- self.class.new(self.class.convert(self.amount, self.unit, unit_name), unit_name)
193
+ return self if unit_name == unit.to_s
194
+ self.class.new(self.class.convert(amount, unit, unit_name), unit_name)
196
195
  end
197
- alias_method("in_#{unit_name}","to_#{unit_name}")
196
+ alias_method("in_#{unit_name}", "to_#{unit_name}")
198
197
  end
199
198
  end
200
-
201
- def self.add_numeric_method_for(unit_name, options={})
199
+
200
+ def self.add_numeric_method_for(unit_name, options = {})
202
201
  unit_name = unit_name.to_sym
203
- raise ArgumentError, "#{unit_name.inspect} is not a unit in #{self.name}" unless units.include?(unit_name)
202
+ raise ArgumentError, "#{unit_name.inspect} is not a unit in #{name}" unless units.include?(unit_name)
204
203
  klass = self
205
204
  Numeric.class_eval do
206
205
  define_method(unit_name) do