Class: ActiveModel::Validations::TrackingNumberValidator
- Inherits:
-
EachValidator
- Object
- EachValidator
- ActiveModel::Validations::TrackingNumberValidator
- Defined in:
- lib/active_validators/active_model/validations/tracking_number_validator.rb
Constant Summary collapse
- UPS_REGEXES =
UPS: ups tracking codes are validated solely on their format see www.ups.com/content/us/en/tracking/help/tracking/tnh.html
[ /\A1Z[a-zA-Z0-9]{16}\z/, /\A[a-zA-Z0-9]{12}\z/, /\A[a-zA-Z0-9]{9}\z/, /\AT[a-zA-Z0-9]{10}\z/ ]
- USS128_REGEX =
/\A(\d{19,21})(\d)\z/
- USS39_REGEX =
/\A[a-zA-Z0-9]{2}(\d{8})(\d)US\z/
- MOD10_WEIGHTS =
[3,1]
- MOD11_WEIGHTS =
[8,6,4,2,3,5,9,7]
Instance Method Summary collapse
- #usps_mod10(chars) ⇒ Object
- #usps_mod11(chars) ⇒ Object
- #uss228?(value) ⇒ Boolean
- #uss39?(value) ⇒ Boolean
- #valid_ups?(value) ⇒ Boolean
-
#valid_usps?(value) ⇒ Boolean
USPS: usps tracking codes are validated based on format (one of USS228 or USS39) and a check digit (using either of the USPS’s MOD10 or MOD11 algorithms) see USPS Publications: - #91 (05/2008) pp.
- #validate_each(record, attribute, value) ⇒ Object
-
#weighted_sum(value, weights) ⇒ Object
takes a string containing digits and calculates a checksum using the provided weight array cycles the weight array if it’s not long enough.
Instance Method Details
#usps_mod10(chars) ⇒ Object
46 47 48 |
# File 'lib/active_validators/active_model/validations/tracking_number_validator.rb', line 46 def usps_mod10(chars) (10 - weighted_sum(chars.reverse, MOD10_WEIGHTS) % 10) % 10 end |
#usps_mod11(chars) ⇒ Object
51 52 53 54 55 56 57 58 |
# File 'lib/active_validators/active_model/validations/tracking_number_validator.rb', line 51 def usps_mod11(chars) mod = weighted_sum(chars, MOD11_WEIGHTS) % 11 case mod when 0 then 5 when 1 then 0 else 11 - mod end end |
#uss228?(value) ⇒ Boolean
32 33 34 35 |
# File 'lib/active_validators/active_model/validations/tracking_number_validator.rb', line 32 def uss228?(value) m = value.match(USS128_REGEX) m.present? && (m[2].to_i == usps_mod10(m[1])) end |
#uss39?(value) ⇒ Boolean
38 39 40 41 42 43 |
# File 'lib/active_validators/active_model/validations/tracking_number_validator.rb', line 38 def uss39?(value) m = value.match(USS39_REGEX) # it appears to be valid for a USS39 barcode's checkdigit to be calculated with either the usps mod 10 # algorithm or the usps mod 11. m.present? && (m[2].to_i == usps_mod10(m[1]) || m[2].to_i == usps_mod11(m[1])) end |
#valid_ups?(value) ⇒ Boolean
16 17 18 |
# File 'lib/active_validators/active_model/validations/tracking_number_validator.rb', line 16 def valid_ups?(value) !!UPS_REGEXES.detect { |fmt| value.match(fmt) } end |
#valid_usps?(value) ⇒ Boolean
USPS: usps tracking codes are validated based on format (one of USS228 or USS39) and a check digit (using either of the USPS’s MOD10 or MOD11 algorithms) see USPS Publications:
- #91 (05/2008) pp. 38
- #97 (05/2002) pp. 62-63
- #109 (09/2007) pp. 19-21
27 28 29 |
# File 'lib/active_validators/active_model/validations/tracking_number_validator.rb', line 27 def valid_usps?(value) uss228?(value) || uss39?(value) end |
#validate_each(record, attribute, value) ⇒ Object
4 5 6 7 8 9 10 |
# File 'lib/active_validators/active_model/validations/tracking_number_validator.rb', line 4 def validate_each(record, attribute, value) carrier = [:carrier] || ([:carrier_field] && record.send([:carrier_field])) raise "Carrier option required" unless carrier method = "valid_#{carrier.to_s}?" raise "Tracking number validation not supported for carrier #{carrier}" unless self.respond_to?(method) record.errors.add(attribute) if value.blank? || !self.send(method, value) end |
#weighted_sum(value, weights) ⇒ Object
takes a string containing digits and calculates a checksum using the provided weight array cycles the weight array if it’s not long enough
62 63 64 65 66 |
# File 'lib/active_validators/active_model/validations/tracking_number_validator.rb', line 62 def weighted_sum(value, weights) digits = value.split('').map { |d| d.to_i } weights = weights.cycle.take(digits.count) if weights.count < digits.count digits.zip(weights).inject(0) { |s,p| s + p[0] * p[1] } end |