active_shipping 0.12.4 → 0.12.5

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 (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