Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit.

  • Loading branch information...
commit 2fa2455ec6ba2080c2c91efcf0438585ce210b7e 0 parents
@mojombo authored
2  .gitignore
@@ -0,0 +1,2 @@
+/experiments
+/test/config.yml
57 README.md
@@ -0,0 +1,57 @@
+OmniShip
+========
+
+Everyone likes to ship stuff around the world. And as coders, we like to
+interact with our favorite shipper via their API.
+
+
+Currently Supported Calls
+-------------------------
+
+* UPS
+ * Track
+
+
+Usage
+-----
+
+Require the library:
+
+ require 'rubygems'
+ require 'omniship'
+
+Set authentication details:
+
+ OmniShip::UPS.username = 'johndoe'
+ OmniShip::UPS.password = 'xk793Ab4G'
+ OmniShip::UPS.token = 'DFFBF2984239A2C6'
+
+Track a package by tracking number:
+
+ trk = OmniShip::UPS.track('1z3050790327433970')
+
+The result:
+
+ {
+ :response => {
+ :response_status => {
+ :code => "1",
+ :description => "Success"
+ }
+ },
+ :shipment => {
+ :inquiry_number => {
+ :value => "1z3050790327433970"
+ },
+ :shipper => {
+ :shipper_number => "790097",
+ :address => {
+ :address_line =>
+ :city =>
+ :state_province_code =>
+ :postal_code =>
+ :
+ }
+ }
+ }
+ }
150 Rakefile
@@ -0,0 +1,150 @@
+require 'rubygems'
+require 'rake'
+require 'date'
+
+#############################################################################
+#
+# Helper functions
+#
+#############################################################################
+
+def name
+ @name ||= Dir['*.gemspec'].first.split('.').first
+end
+
+def version
+ line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
+ line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
+end
+
+def date
+ Date.today.to_s
+end
+
+def rubyforge_project
+ name
+end
+
+def gemspec_file
+ "#{name}.gemspec"
+end
+
+def gem_file
+ "#{name}-#{version}.gem"
+end
+
+def replace_header(head, header_name)
+ head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
+end
+
+#############################################################################
+#
+# Standard tasks
+#
+#############################################################################
+
+task :default => :test
+
+require 'rake/testtask'
+Rake::TestTask.new(:test) do |test|
+ test.libs << 'lib' << 'test'
+ test.pattern = 'test/**/test_*.rb'
+ test.verbose = true
+end
+
+desc "Generate RCov test coverage and open in your browser"
+task :coverage do
+ require 'rcov'
+ sh "rm -fr coverage"
+ sh "rcov test/test_*.rb"
+ sh "open coverage/index.html"
+end
+
+require 'rake/rdoctask'
+Rake::RDocTask.new do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = "#{name} #{version}"
+ rdoc.rdoc_files.include('README*')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
+
+desc "Open an irb session preloaded with this library"
+task :console do
+ sh "irb -rubygems -r ./lib/#{name}.rb"
+end
+
+#############################################################################
+#
+# Custom tasks (add your own tasks here)
+#
+#############################################################################
+
+
+
+#############################################################################
+#
+# Packaging tasks
+#
+#############################################################################
+
+desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
+task :release => :build do
+ unless `git branch` =~ /^\* master$/
+ puts "You must be on the master branch to release!"
+ exit!
+ end
+ sh "git commit --allow-empty -a -m 'Release #{version}'"
+ sh "git tag v#{version}"
+ sh "git push origin master"
+ sh "git push origin v#{version}"
+ sh "gem push pkg/#{name}-#{version}.gem"
+end
+
+desc "Build #{gem_file} into the pkg directory"
+task :build => :gemspec do
+ sh "mkdir -p pkg"
+ sh "gem build #{gemspec_file}"
+ sh "mv #{gem_file} pkg"
+end
+
+desc "Generate #{gemspec_file}"
+task :gemspec => :validate do
+ # read spec file and split out manifest section
+ spec = File.read(gemspec_file)
+ head, manifest, tail = spec.split(" # = MANIFEST =\n")
+
+ # replace name version and date
+ replace_header(head, :name)
+ replace_header(head, :version)
+ replace_header(head, :date)
+ #comment this out if your rubyforge_project has a different name
+ replace_header(head, :rubyforge_project)
+
+ # determine file list from git ls-files
+ files = `git ls-files`.
+ split("\n").
+ sort.
+ reject { |file| file =~ /^\./ }.
+ reject { |file| file =~ /^(rdoc|pkg)/ }.
+ map { |file| " #{file}" }.
+ join("\n")
+
+ # piece file back together and write
+ manifest = " s.files = %w[\n#{files}\n ]\n"
+ spec = [head, manifest, tail].join(" # = MANIFEST =\n")
+ File.open(gemspec_file, 'w') { |io| io.write(spec) }
+ puts "Updated #{gemspec_file}"
+end
+
+desc "Validate #{gemspec_file}"
+task :validate do
+ libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
+ unless libfiles.empty?
+ puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
+ exit!
+ end
+ unless Dir['VERSION*'].empty?
+ puts "A `VERSION` file at root level violates Gem best practices."
+ exit!
+ end
+end
31 lib/omniship.rb
@@ -0,0 +1,31 @@
+$:.unshift File.dirname(__FILE__)
+
+# stdlib
+require 'yaml'
+
+# 3rd Party
+require 'handsoap'
+require 'json'
+
+# Internal
+require 'omniship/ups'
+
+Handsoap::Service.logger = STDOUT
+
+module OmniShip
+ VERSION = '0.0.1'
+
+ # Load configuration information from a YAML file.
+ #
+ # file - The String filename of the YAML config file.
+ #
+ # Returns nothing.
+ def self.config(file)
+ data = YAML::load(File.open(file))
+ if ups = data['UPS']
+ UPS.username = ups['username']
+ UPS.password = ups['password']
+ UPS.token = ups['token']
+ end
+ end
+end
15 lib/omniship/ups.rb
@@ -0,0 +1,15 @@
+require 'omniship/ups/track'
+
+module OmniShip
+ module UPS
+ class << self
+ attr_accessor :username
+ attr_accessor :password
+ attr_accessor :token
+ end
+
+ def self.track(id)
+ TrackRequest.track(id)
+ end
+ end
+end
4 lib/omniship/ups/track.rb
@@ -0,0 +1,4 @@
+require 'omniship/ups/track/shipment'
+
+require 'omniship/ups/track_request'
+require 'omniship/ups/track_response'
61 lib/omniship/ups/track/shipment.rb
@@ -0,0 +1,61 @@
+module OmniShip
+ module UPS
+ module Track
+ class Shipment
+ # The Handsoap XML element representing the root response node.
+ attr_accessor :root
+
+ # Initialize a new Shipment.
+ #
+ # root - The root Handsoap XML node.
+ #
+ # Returns the newly initialized Shipment.
+ def initialize(root)
+ @root = root
+ end
+
+ # The scheduled delivery date. If a specific time of day is available
+ # then it will be set, otherwise the time will be set to noon. If no
+ # delivery date is available, the result will be nil.
+ #
+ # Returns the Time of the delivery, or nil if none is available.
+ def scheduled_delivery
+ if date = scheduled_delivery_date
+ year = date[0..3].to_i
+ month = date[4..5].to_i
+ day = date[6..7].to_i
+ if time = scheduled_delivery_time
+ hour = time[0..1].to_i
+ minute = time[2..3].to_i
+ second = time[4..5].to_i
+ Time.utc(year, month, day, hour, minute, second)
+ else
+ Time.utc(year, month, day, 12, 0, 0)
+ end
+ end
+ end
+
+ # The scheduled delivery date as a String in YYYYMMDD format.
+ #
+ # Returns the String delivery date or nil if none is available.
+ def scheduled_delivery_date
+ @root.xpath('./ns:Shipment/ns:ScheduledDelivery/ns:Date/text()').to_s
+ end
+
+ # The scheduled delivery time as a String in HHMMSS format.
+ #
+ # Returns the String delivery time or nil if none is available.
+ def scheduled_delivery_time
+ @root.xpath('./ns:Shipment/ns:ScheduledDelivery/ns:Time/text()').to_s
+ end
+
+ # Returns a Hash representation of this object.
+ def to_hash
+ {
+ "ScheduledDelivery" => scheduled_delivery.to_i
+ }
+ end
+ end
+ end
+ end
+end
44 lib/omniship/ups/track_request.rb
@@ -0,0 +1,44 @@
+module OmniShip
+ module UPS
+ class TrackRequest < Handsoap::Service
+ endpoint :uri => 'https://wwwcie.ups.com/webservices/Track', :version => 1
+
+ def on_create_document(doc)
+ doc.alias 'upss', 'http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0'
+ doc.alias 'track', 'http://www.ups.com/XMLSchema/XOLTWS/Track/v1.1'
+ doc.alias 'com', 'http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0'
+
+ doc.find("Header").add "upss:UPSSecurity" do |sec|
+ sec.add "upss:UsernameToken" do |ut|
+ ut.add "upss:Username", UPS.username
+ ut.add "upss:Password", UPS.password
+ end
+ sec.add "upss:ServiceAccessToken" do |tok|
+ tok.add "upss:AccessLicenseNumber", UPS.token
+ end
+ end
+ end
+
+ def on_response_document(doc)
+ doc.add_namespace 'trk', 'http://www.ups.com/XMLSchema/XOLTWS/Track/v1.1'
+ end
+
+ def track(tracking_number)
+ response = invoke('track:TrackRequest') do |body|
+ body.add('com:Request') do |request|
+ request.add('com:RequestOption', '1')
+ end
+ body.add('track:InquiryNumber', tracking_number)
+ end
+
+ root = response.document.xpath('//ns:TrackResponse', ns).first
+ OmniShip::UPS::TrackResponse.new(root)
+ # p xml_to_str(node, './ns:Shipment/ns:ScheduledDelivery/ns:Date/text()')
+ end
+
+ def ns
+ { 'ns' => 'http://www.ups.com/XMLSchema/XOLTWS/Track/v1.1' }
+ end
+ end
+ end
+end
25 lib/omniship/ups/track_response.rb
@@ -0,0 +1,25 @@
+module OmniShip
+ module UPS
+ class TrackResponse
+ attr_accessor :root
+
+ def initialize(root)
+ @root = root
+ end
+
+ def shipment
+ Track::Shipment.new(@root)
+ end
+
+ def to_hash
+ {
+ 'Shipment' => shipment.to_hash
+ }
+ end
+
+ def to_json
+ to_hash.to_json
+ end
+ end
+ end
+end
33 omniship.gemspec
@@ -0,0 +1,33 @@
+Gem::Specification.new do |s|
+ s.specification_version = 2 if s.respond_to? :specification_version=
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+ s.rubygems_version = '1.3.6'
+
+ s.name = ''
+ s.version = ''
+ s.date = ''
+ s.rubyforge_project = ''
+
+ s.summary = "OmniShip is a unified API for various shipping APIs."
+ s.description = "OmniShip is a unified API for various shipping APIs such as UPS, FedEx, and DHL."
+
+ s.authors = ["Tom Preston-Werner"]
+ s.email = 'tom@mojombo.com'
+ s.homepage = 'http://github.com/mojombo/omniship'
+
+ s.require_paths = %w[lib]
+
+ s.rdoc_options = ["--charset=UTF-8"]
+ s.extra_rdoc_files = %w[LICENSE README.md]
+
+ s.add_dependency('handsoap', [">= 1.1.7", "< 2.0.0"])
+
+ s.add_development_dependency('shoulda', [">= 2.11.3", "< 3.0.0"])
+
+ # = MANIFEST =
+ s.files = %w[
+ ]
+ # = MANIFEST =
+
+ s.test_files = s.files.select { |path| path =~ /^test\/test_.*\.rb/ }
+end
4 test/config.yml.example
@@ -0,0 +1,4 @@
+UPS:
+ username: johndoe
+ password: xk793Ab4G
+ token: DFFBF2984239A2C6
13 test/helper.rb
@@ -0,0 +1,13 @@
+require 'rubygems'
+require 'shoulda'
+
+require File.join(File.dirname(__FILE__), *%w[.. lib omniship])
+
+config_file = File.join(File.dirname(__FILE__), *%w[config.yml])
+
+unless File.exist?(config_file)
+ puts "You need a valid 'config.yml' to run the test."
+ exit 1
+end
+
+OmniShip.config(config_file)
14 test/test_ups_track.rb
@@ -0,0 +1,14 @@
+require File.dirname(__FILE__) + '/helper'
+
+class TestUPSTrack < Test::Unit::TestCase
+ context "stuff" do
+ setup do
+
+ end
+
+ should "work" do
+ tr = OmniShip::UPS.track('1z7900970327439305')
+ p tr.to_json
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.