Class: TrackingNumber::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/tracking_number/base.rb

Direct Known Subclasses

Unknown

Defined Under Namespace

Classes: PartnerStruct

Constant Summary collapse

LENGTH_WEIGHT =
0.1
CHECKSUM_WEIGHT =
5.0

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tracking_number) ⇒ Base

Returns a new instance of Base.



7
8
9
10
# File 'lib/tracking_number/base.rb', line 7

def initialize(tracking_number)
  @original_number = tracking_number
  @tracking_number = tracking_number.strip.gsub(' ', '').upcase
end

Instance Attribute Details

#original_numberObject

Returns the value of attribute original_number.



3
4
5
# File 'lib/tracking_number/base.rb', line 3

def original_number
  @original_number
end

#partnerObject

Returns the value of attribute partner.



3
4
5
# File 'lib/tracking_number/base.rb', line 3

def partner
  @partner
end

#partner_dataObject

Returns the value of attribute partner_data.



3
4
5
# File 'lib/tracking_number/base.rb', line 3

def partner_data
  @partner_data
end

#tracking_numberObject

Returns the value of attribute tracking_number.



3
4
5
# File 'lib/tracking_number/base.rb', line 3

def tracking_number
  @tracking_number
end

Class Method Details

.scan(body) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/tracking_number/base.rb', line 23

def self.scan(body)
  # matches with match groups within the match data
  matches = []

  body.scan(const_get(:SEARCH_PATTERN)) do
    # get the match data instead, which is needed with these types of regexes
    matches << $~
  end

  if matches
    matches.collect { |m| m[0] }
  else
    []
  end
end

.search(body) ⇒ Object



12
13
14
15
16
17
18
19
20
21
# File 'lib/tracking_number/base.rb', line 12

def self.search(body)
  valids = scan(body).uniq.collect { |possible| new(possible) }.select { |t| t.valid? }

  uniques = {}
  valids.each do |t|
    uniques[t.tracking_number] = t unless uniques.has_key?(t.tracking_number)
  end

  uniques.values
end

Instance Method Details

#carrier?Boolean

Returns:

  • (Boolean)


177
178
179
180
181
# File 'lib/tracking_number/base.rb', line 177

def carrier?
  return true unless partnership?

  partners.carrier == self
end

#check_digitObject



54
55
56
# File 'lib/tracking_number/base.rb', line 54

def check_digit
  match_group('CheckDigit')
end

#checksum?Boolean

Returns:

  • (Boolean)


100
101
102
# File 'lib/tracking_number/base.rb', line 100

def checksum?
  !!self.class.const_get(:VALIDATION)[:checksum]
end

#confidenceObject



107
108
109
# File 'lib/tracking_number/base.rb', line 107

def confidence
  (checksum? ? CHECKSUM_WEIGHT : 0) + tracking_number.length * LENGTH_WEIGHT
end

#courier_codeObject Also known as: carrier, carrier_code



141
142
143
# File 'lib/tracking_number/base.rb', line 141

def courier_code
  self.class.const_get(:COURIER_CODE).to_sym
end

#courier_infoObject



157
158
159
160
161
162
163
164
165
# File 'lib/tracking_number/base.rb', line 157

def courier_info
  basics = { name: courier_name, code: courier_code }

  if info = matching_additional['Courier']
    basics.merge!(name: info[:courier], url: info[:courier_url], country: info[:country])
  end

  @courier ||= Info.new(basics)
end

#courier_nameObject Also known as: carrier_name



145
146
147
148
149
150
151
# File 'lib/tracking_number/base.rb', line 145

def courier_name
  if matching_additional['Courier']
    matching_additional['Courier'][:courier]
  elsif self.class.constants.include?(:COURIER_INFO)
    self.class.const_get(:COURIER_INFO)[:name]
  end
end

#decodeObject



58
59
60
61
62
63
64
65
66
# File 'lib/tracking_number/base.rb', line 58

def decode
  decoded = {}
  (matches.try(:names) || []).each do |name|
    sym = name.underscore.to_sym
    decoded[sym] = matches[name]
  end

  decoded
end

#destination_zipObject



212
213
214
# File 'lib/tracking_number/base.rb', line 212

def destination_zip
  match_group('DestinationZip')
end

#infoObject



127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/tracking_number/base.rb', line 127

def info
  Info.new({
             courier: courier_info,
             service_type: service_type,
             service_description: service_description,
             destination_zip: destination_zip,
             shipper_id: shipper_id,
             package_type: package_type,
             tracking_url: tracking_url,
             partners: partners,
             decode: decode
           })
end

#inspectObject



115
116
117
# File 'lib/tracking_number/base.rb', line 115

def inspect
  format('#<%s:%#0x %s%s>', self.class.to_s, object_id, tracking_number, partnership_inspect)
end

#matching_additionalObject



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/tracking_number/base.rb', line 231

def matching_additional
  additional = self.class.const_get(:ADDITIONAL) || []

  relevant_sections = {}

  additional.each do |additional_info|
    next unless matches && matches.length > 0 # skip if no match groups
    value = matches[additional_info[:regex_group_name]].gsub(/\s/, '') # match is empty
    next unless value

    matches = additional_info[:lookup].find do |i|
      if i[:matches]
        value == i[:matches]
      elsif i[:matches_regex]
        value =~ Regexp.new(i[:matches_regex])
      end
    end

    # has matching value
    relevant_sections[additional_info[:name]] = matches
  end

  relevant_sections
end

#package_typeObject



208
209
210
# File 'lib/tracking_number/base.rb', line 208

def package_type
  @package_type ||= Info.new(matching_additional['Container Type']).name if matching_additional['Container Type']
end

#partnersObject



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/tracking_number/base.rb', line 183

def partners
  return unless self.class.const_defined?(:PARTNERS)

  partner_hash = {}

  return unless (partner_tn = find_matching_partner)

  possible_twin = partner_tn.send(:find_matching_partner)

  if possible_twin.instance_of?(self.class) && possible_twin.tracking_number == tracking_number
    partner_hash[partner_data[:partner_type].to_sym] = partner_tn
    partner_hash[partner_tn.partner_data[:partner_type].to_sym] = self
  end

  PartnerStruct.new(partner_hash[:shipper], partner_hash[:carrier]) if partner_hash.keys.any?
end

#partnership?Boolean

Returns:

  • (Boolean)


167
168
169
# File 'lib/tracking_number/base.rb', line 167

def partnership?
  partners.present?
end

#partnership_inspectObject



119
120
121
122
123
124
125
# File 'lib/tracking_number/base.rb', line 119

def partnership_inspect
  if shipper? ^ carrier?
    ' (partnership)'
  else
    ''
  end
end

#serial_numberObject



39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/tracking_number/base.rb', line 39

def serial_number
  return match_group('SerialNumber') unless self.class.const_get('VALIDATION')

  format_info   = self.class.const_get(:VALIDATION)[:serial_number_format]
  raw_serial    = match_group('SerialNumber')

  if format_info && format_info[:prepend_if] && raw_serial.match(Regexp.new(format_info[:prepend_if][:matches_regex]))
    return "#{format_info[:prepend_if][:content]}#{raw_serial}"
  # elsif format_info && format_info[:prepend_if_missing]

  end

  raw_serial
end

#service_descriptionObject



204
205
206
# File 'lib/tracking_number/base.rb', line 204

def service_description
  @service_description ||= Info.new(matching_additional['Service Type']).description if matching_additional['Service Type']
end

#service_typeObject



200
201
202
# File 'lib/tracking_number/base.rb', line 200

def service_type
  @service_type ||= Info.new(matching_additional['Service Type']).name if matching_additional['Service Type']
end

#shipper?Boolean

Returns:

  • (Boolean)


171
172
173
174
175
# File 'lib/tracking_number/base.rb', line 171

def shipper?
  return true unless partnership?

  partners.shipper == self
end

#shipper_idObject



216
217
218
# File 'lib/tracking_number/base.rb', line 216

def shipper_id
  match_group('ShipperId')
end

#to_sObject



111
112
113
# File 'lib/tracking_number/base.rb', line 111

def to_s
  tracking_number
end

#tracking_urlObject



220
221
222
223
224
225
226
227
228
229
# File 'lib/tracking_number/base.rb', line 220

def tracking_url
  url = nil
  if matching_additional['Courier']
    url = matching_additional['Courier'][:tracking_url]
  elsif self.class.const_defined?(:TRACKING_URL)
    url = self.class.const_get(:TRACKING_URL)
  end

  url.sub('%s', tracking_number) if url
end

#valid?Boolean

Returns:

  • (Boolean)


68
69
70
71
72
73
74
# File 'lib/tracking_number/base.rb', line 68

def valid?
  return false unless valid_format?
  return false unless valid_checksum?
  return false unless valid_optional_checks?

  true
end

#valid_checksum?Boolean

Returns:

  • (Boolean)


88
89
90
91
92
93
94
95
96
97
98
# File 'lib/tracking_number/base.rb', line 88

def valid_checksum?
  return false unless valid_format?

  checksum_info = self.class.const_get(:VALIDATION)[:checksum]
  return true unless checksum_info

  name            = checksum_info[:name]
  method_name     = "validates_#{name}?"

  TrackingNumber::ChecksumValidations.send(method_name, serial_number, check_digit, checksum_info)
end

#valid_format?Boolean

Returns:

  • (Boolean)


76
77
78
# File 'lib/tracking_number/base.rb', line 76

def valid_format?
  !matches.nil?
end

#valid_optional_checks?Boolean

Returns:

  • (Boolean)


80
81
82
83
84
85
86
# File 'lib/tracking_number/base.rb', line 80

def valid_optional_checks?
  additional_check = self.class.const_get('VALIDATION')[:additional]
  return true unless additional_check

  exist_checks = (additional_check[:exists] ||= [])
  exist_checks.all? { |w| matching_additional[w] }
end