trackerific 0.3.5 → 0.4.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 (42) hide show
  1. data/Gemfile +1 -1
  2. data/README.rdoc +37 -13
  3. data/VERSION +1 -1
  4. data/doc/OptionsHelper.html +287 -0
  5. data/doc/Trackerific.html +588 -35
  6. data/doc/Trackerific/Configuration.html +354 -0
  7. data/doc/Trackerific/Details.html +1 -1
  8. data/doc/Trackerific/Error.html +1 -1
  9. data/doc/Trackerific/Event.html +1 -1
  10. data/doc/Trackerific/FedEx.html +52 -35
  11. data/doc/Trackerific/{Base.html → Service.html} +54 -49
  12. data/doc/Trackerific/UPS.html +48 -31
  13. data/doc/Trackerific/USPS.html +51 -34
  14. data/doc/_index.html +35 -4
  15. data/doc/class_list.html +1 -1
  16. data/doc/file.README.html +42 -15
  17. data/doc/index.html +42 -15
  18. data/doc/method_list.html +83 -19
  19. data/doc/top-level-namespace.html +2 -2
  20. data/lib/helpers/options_helper.rb +20 -0
  21. data/lib/trackerific.rb +52 -18
  22. data/lib/trackerific/configuration.rb +49 -0
  23. data/lib/trackerific/service.rb +68 -0
  24. data/lib/trackerific/services/fedex.rb +18 -16
  25. data/lib/trackerific/services/ups.rb +16 -14
  26. data/lib/trackerific/services/usps.rb +17 -15
  27. data/spec/lib/trackerific/configuration_spec.rb +9 -0
  28. data/spec/lib/trackerific/details_spec.rb +12 -9
  29. data/spec/lib/trackerific/error_spec.rb +6 -2
  30. data/spec/lib/trackerific/event_spec.rb +21 -8
  31. data/spec/lib/trackerific/service_spec.rb +34 -0
  32. data/spec/lib/trackerific/services/fedex_spec.rb +23 -9
  33. data/spec/lib/trackerific/services/ups_spec.rb +23 -8
  34. data/spec/lib/trackerific/services/usps_spec.rb +22 -8
  35. data/spec/lib/trackerific_spec.rb +29 -7
  36. data/spec/spec_helper.rb +1 -0
  37. data/spec/support/fixtures.rb +16 -10
  38. data/spec/support/trackerific.rb +2 -2
  39. data/trackerific.gemspec +13 -8
  40. metadata +30 -25
  41. data/lib/trackerific/base.rb +0 -70
  42. data/spec/lib/trackerific/base_spec.rb +0 -34
@@ -0,0 +1,20 @@
1
+ # Helper for validating required options
2
+ module OptionsHelper
3
+ # Validates a list of options against a list of required options
4
+ # @param [Array] options list of options to validate
5
+ # @param [Array] required list of required options
6
+ # @return true
7
+ # @raise [ArgumentError] if the options do not pass validation
8
+ # @api private
9
+ def validate_options(options, required)
10
+ # make sure all the required options exist
11
+ required.each do |k|
12
+ raise ArgumentError.new("Missing required parameter: #{k}") unless options.has_key?(k)
13
+ end
14
+ # make sure no invalid options exist
15
+ options.each do |k, v|
16
+ raise ArgumentError.new("Invalid parameter: #{k}") unless required.include?(k)
17
+ end
18
+ true
19
+ end
20
+ end
data/lib/trackerific.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require 'rails'
2
- require 'trackerific/base'
2
+ require 'helpers/options_helper'
3
+ require 'trackerific/configuration'
4
+ require 'trackerific/service'
3
5
  require 'trackerific/error'
4
6
  require 'trackerific/details'
5
7
  require 'trackerific/event'
@@ -10,6 +12,30 @@ Dir[File.expand_path(services_path)].each { |file| require file }
10
12
 
11
13
  # Trackerific provides package tracking to Rails apps.
12
14
  module Trackerific
15
+
16
+ class << self
17
+ # Gets a list of all Trackerific services
18
+ # @return [Array, Symbol] the services
19
+ # @api private
20
+ def services
21
+ # a service is any Trackerific class that descends from Trackerific::Service
22
+ Trackerific.constants.reject { |const|
23
+ const unless Trackerific.const_get(const).superclass == Trackerific::Service
24
+ }
25
+ end
26
+
27
+ # Gets a Trackerific::Service class
28
+ # @param [Symbol] name the name of the service
29
+ # @return [Trackerific::Service] the service, or nil
30
+ # @api private
31
+ def service_get(name)
32
+ services.each do |service|
33
+ return Trackerific.const_get(service) if name == service.to_s.downcase.to_sym
34
+ end
35
+ return nil
36
+ end
37
+ end
38
+
13
39
  # Checks a string for a valid package tracking service
14
40
  # @param [String] package_id the package identifier
15
41
  # @return [Trackerific::Base] the Trackerific class that can track the given
@@ -19,33 +45,41 @@ module Trackerific
19
45
  # tracking_service "183689015000001" # => Trackerific::FedEx
20
46
  # @api public
21
47
  def tracking_service(package_id)
22
- # loop through each constant in Trackerific
23
- Trackerific.constants.each do |const|
24
- # get the constant's class
25
- cls = Trackerific.const_get(const)
26
- # check if it descends from Trackerific::Base
27
- if cls.superclass == Trackerific::Base
28
- # loop through each package id matcher
29
- cls.package_id_matchers.each do |matcher|
30
- # return the class if it matches
31
- return cls if package_id =~ matcher
32
- end
48
+ # loop through all the services
49
+ Trackerific.services.each do |service|
50
+ # get the class associated with this service
51
+ cls = Trackerific.const_get(service)
52
+ # loop through all the packge id regular expressions
53
+ cls.package_id_matchers.each do |matcher|
54
+ # return this class if the regular expression matches
55
+ return cls if package_id =~ matcher
33
56
  end
34
57
  end
35
58
  # if we've made it this far, nothing matched
36
59
  nil
37
60
  end
38
61
 
39
- # TODO: Tracks a package by determining its service from the package id
62
+ # Tracks a package by determining its service from the package id
40
63
  # @param [String] package_id the package identifier
41
64
  # @return [Trackerific::Details] the tracking results
42
65
  # @raise [Trackerific::Error] raised when the server returns an error (invalid credentials, tracking package, etc.)
43
66
  # @example Track a package
44
67
  # include Trackerific
45
- # details = track_package("183689015000001")
68
+ # # make sure to configure Trackerific before hand with the different services credentials
69
+ # Trackerific.config do |config|
70
+ # config.fedex :meter => '123456789', :account => '123456789'
71
+ # end
72
+ # details = track_package "183689015000001"
46
73
  # @api public
47
- # def track_package(package_id)
48
- # service = tracking_service(package_id)
49
- #
50
- # end
74
+ def track_package(package_id)
75
+ # find the service that will be able to track this package
76
+ service = tracking_service package_id
77
+ raise Trackerific::Error "Cannot find a service to track package id #{package_id}" if service.nil?
78
+ # get the name of the service
79
+ service_name = service.to_s.split('::')[1].downcase
80
+ # get the default configuration for the service
81
+ options = Trackerific.configuration.send service_name
82
+ # track the package
83
+ service.new(options).track_package package_id
84
+ end
51
85
  end
@@ -0,0 +1,49 @@
1
+ module Trackerific
2
+ # Provides a dynamic configuration
3
+ class Configuration
4
+ include OptionsHelper
5
+ # Creates a new instance of Trackerific::Configuration
6
+ # @api private
7
+ def initialize
8
+ @options = {}
9
+ end
10
+ # Overriding the method_missing method allows dynamic methods
11
+ # @return [Hash]
12
+ # @api private
13
+ def method_missing(sym, *args, &block)
14
+ # Get a list of all the services (convert symbols to lower case)
15
+ services = Trackerific.services.map { |service| service.to_s.downcase.to_sym }
16
+ # Do not accept any configuration values for anything except services
17
+ raise NoMethodError unless services.include? sym
18
+ # Only accept Hashes
19
+ unless args.empty?
20
+ raise ArgumentError unless args[0].class == Hash
21
+ # Validate configuration values against the required options for that service
22
+ validate_options args[0], Trackerific.service_get(sym).required_options
23
+ # Store the configuration options
24
+ @options[sym] = args[0]
25
+ end
26
+ # return the configuration options
27
+ @options[sym]
28
+ end
29
+ end
30
+ class << self
31
+ # Stores the configuration options for Trackerific
32
+ # @return [Trackerific::Configuration]
33
+ # @api private
34
+ def configuration
35
+ @configuration ||= Trackerific::Configuration.new
36
+ end
37
+ # Configures Trackerific
38
+ # @return [Trackerific::Configuration]
39
+ # @example Defining credentials
40
+ # Trackerific.configure do |config|
41
+ # config.fedex :meter => '123456789', :account => '123456789'
42
+ # end
43
+ # @api public
44
+ def configure
45
+ yield configuration
46
+ configuration
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,68 @@
1
+ module Trackerific
2
+ # Base class for Trackerific services
3
+ class Service
4
+ include OptionsHelper
5
+
6
+ # Creates a new instance of Trackerific::Service with required options
7
+ # @api private
8
+ def initialize(options = {})
9
+ validate_options options, self.class.required_options
10
+ @options = options
11
+ end
12
+
13
+ # Gets the tracking information for the package from the server
14
+ # @param [String] package_id the package identifier
15
+ # @return [Trackerific::Details] the tracking details
16
+ # @example Override this method in your custom tracking service to implement tracking
17
+ # module Trackerific
18
+ # class MyTrackingService < Trackerific::Service
19
+ # def track_package
20
+ # # your tracking code here
21
+ # Trackerific::Details.new(
22
+ # "summary of tracking events",
23
+ # [Trackerific::Event.new(Time.now, "summary", "location")]
24
+ # )
25
+ # end
26
+ # end
27
+ # end
28
+ # @api semipublic
29
+ def track_package(package_id)
30
+ @package_id = package_id
31
+ end
32
+
33
+ class << self
34
+
35
+ # An Array of Regexp that matches valid package identifiers for your service
36
+ # @example Override this method in your custom tracking service
37
+ # module Trackerific
38
+ # class MyTrackingService < Service
39
+ # def self.package_id_matchers
40
+ # [ /^.Z/, /^[HK].{10}$/ ] # matchers for UPS package identifiers
41
+ # end
42
+ # end
43
+ # end
44
+ # @return [Array, Regexp] an array of regular expressions
45
+ # @api semipublic
46
+ def package_id_matchers
47
+ nil
48
+ end
49
+
50
+ # An array of options that are required to create a new instance of this class
51
+ # @return [Array] the required options
52
+ # @example Override this method in your custom tracking service to enforce some options
53
+ # module Trackerific
54
+ # class MyTrackingService < Service
55
+ # def self.required_options
56
+ # [:all, :these, :are, :required]
57
+ # end
58
+ # end
59
+ # end
60
+ # @api semipublic
61
+ def required_options
62
+ []
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+ end
@@ -3,12 +3,29 @@ require 'httparty'
3
3
  module Trackerific
4
4
 
5
5
  # Provides package tracking support for FedEx
6
- class FedEx < Base
6
+ class FedEx < Trackerific::Service
7
7
  # setup HTTParty
8
8
  include ::HTTParty
9
9
  format :xml
10
10
  base_uri "https://gateway.fedex.com"
11
11
 
12
+ class << self
13
+ # An Array of Regexp that matches valid FedEx package IDs
14
+ # @return [Array, Regexp] the regular expression
15
+ # @api private
16
+ def package_id_matchers
17
+ [ /^[0-9]{15}$/ ]
18
+ end
19
+
20
+ # Returns an Array of required options used when creating a new instance
21
+ # @return [Array] required options for tracking a FedEx package are :account
22
+ # and :meter
23
+ # @api private
24
+ def required_options
25
+ [:account, :meter]
26
+ end
27
+ end
28
+
12
29
  # Tracks a FedEx package
13
30
  # @param [String] package_id the package identifier
14
31
  # @return [Trackerific::Details] the tracking details
@@ -45,21 +62,6 @@ module Trackerific
45
62
  )
46
63
  end
47
64
 
48
- # An Array of Regexp that matches valid FedEx package IDs
49
- # @return [Array, Regexp] the regular expression
50
- # @api private
51
- def self.package_id_matchers
52
- [ /^[0-9]{15}$/ ]
53
- end
54
-
55
- # Returns an Array of required options used when creating a new instance
56
- # @return [Array] required options for tracking a FedEx package are :account
57
- # and :meter
58
- # @api private
59
- def self.required_options
60
- [:account, :meter]
61
- end
62
-
63
65
  protected
64
66
 
65
67
  # Builds the XML request to send to FedEx
@@ -4,7 +4,7 @@ module Trackerific
4
4
  require 'httparty'
5
5
 
6
6
  # Provides package tracking support for UPS.
7
- class UPS < Base
7
+ class UPS < Trackerific::Service
8
8
  # setup HTTParty
9
9
  include ::HTTParty
10
10
  format :xml
@@ -14,6 +14,21 @@ module Trackerific
14
14
  when 'production' then 'https://www.ups.com/ups.app/xml'
15
15
  end : 'https://www.ups.com/ups.app/xml'
16
16
 
17
+ class << self
18
+ # An Array of Regexp that matches valid UPS package IDs
19
+ # @return [Array, Regexp] the regular expression
20
+ # @api private
21
+ def package_id_matchers
22
+ [ /^.Z/, /^[HK].{10}$/ ]
23
+ end
24
+ # The required options for tracking a UPS package
25
+ # @return [Array] the required options for tracking a UPS package
26
+ # @api private
27
+ def required_options
28
+ [:key, :user_id, :password]
29
+ end
30
+ end
31
+
17
32
  # Tracks a UPS package
18
33
  # @param [String] package_id the package identifier
19
34
  # @return [Trackerific::Details] the tracking details
@@ -37,19 +52,6 @@ module Trackerific
37
52
  end
38
53
  end
39
54
 
40
- # An Array of Regexp that matches valid UPS package IDs
41
- # @return [Array, Regexp] the regular expression
42
- # @api private
43
- def self.package_id_matchers
44
- [ /^.Z/, /^[HK].{10}$/ ]
45
- end
46
- # The required options for tracking a UPS package
47
- # @return [Array] the required options for tracking a UPS package
48
- # @api private
49
- def self.required_options
50
- [:key, :user_id, :password]
51
- end
52
-
53
55
  protected
54
56
 
55
57
  # Parses the response from UPS
@@ -5,7 +5,7 @@ module Trackerific
5
5
  require 'httparty'
6
6
 
7
7
  # Provides package tracking support for USPS.
8
- class USPS < Base
8
+ class USPS < Trackerific::Service
9
9
  # setup HTTParty
10
10
  include HTTParty
11
11
  format :xml
@@ -15,6 +15,22 @@ module Trackerific
15
15
  when 'production' then 'https://secure.shippingapis.com'
16
16
  end : 'https://secure.shippingapis.com'
17
17
 
18
+ class << self
19
+ # An Array of Regexp that matches valid USPS package IDs
20
+ # @return [Array, Regexp] the regular expression
21
+ # @api private
22
+ def package_id_matchers
23
+ [ /^E\D{1}\d{9}\D{2}$|^9\d{15,21}$/ ]
24
+ end
25
+
26
+ # The required options for tracking a UPS package
27
+ # @return [Array] the required options for tracking a UPS package
28
+ # @api private
29
+ def required_options
30
+ [:user_id]
31
+ end
32
+ end
33
+
18
34
  # Tracks a USPS package
19
35
  # @param [String] package_id the package identifier
20
36
  # @return [Trackerific::Details] the tracking details
@@ -56,20 +72,6 @@ module Trackerific
56
72
  )
57
73
  end
58
74
 
59
- # An Array of Regexp that matches valid USPS package IDs
60
- # @return [Array, Regexp] the regular expression
61
- # @api private
62
- def self.package_id_matchers
63
- [ /^E\D{1}\d{9}\D{2}$|^9\d{15,21}$/ ]
64
- end
65
-
66
- # The required options for tracking a UPS package
67
- # @return [Array] the required options for tracking a UPS package
68
- # @api private
69
- def self.required_options
70
- [:user_id]
71
- end
72
-
73
75
  protected
74
76
 
75
77
  # Builds an XML request to send to USPS
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Trackerific.configuration" do
4
+ include Trackerific
5
+
6
+ subject { Trackerific.configuration }
7
+ it { should be_a Trackerific::Configuration }
8
+
9
+ end
@@ -1,19 +1,22 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Trackerific::Details do
4
- before(:all) do
5
- @details = Trackerific::Details.new(String.new, String.new, Array.new)
6
- end
4
+
5
+ before { @details = Trackerific::Details.new(String.new, String.new, Array.new) }
7
6
 
8
- describe "events" do
9
- specify { @details.events.should be_a Array }
7
+ describe :events do
8
+ subject { @details.events }
9
+ it { should be_a Array }
10
10
  end
11
11
 
12
- describe "package_id" do
13
- specify { @details.package_id.should be_a String }
12
+ describe :package_id do
13
+ subject { @details.package_id }
14
+ it { should be_a String }
14
15
  end
15
16
 
16
- describe "summary" do
17
- specify { @details.summary.should be_a String }
17
+ describe :summary do
18
+ subject { @details.summary }
19
+ it { should be_a String }
18
20
  end
21
+
19
22
  end
@@ -1,5 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe 'Trackerific::Error' do
4
- # nothing to do here, yet
3
+ describe Trackerific::Error do
4
+
5
+ before { @error = Trackerific::Error.new }
6
+ subject { @error }
7
+ specify { should be_a StandardError }
8
+
5
9
  end