shippinglogic 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/.document +5 -0
  2. data/CHANGELOG.rdoc +55 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +175 -0
  5. data/Rakefile +37 -0
  6. data/VERSION.yml +5 -0
  7. data/init.rb +1 -0
  8. data/lib/shippinglogic.rb +3 -0
  9. data/lib/shippinglogic/attributes.rb +121 -0
  10. data/lib/shippinglogic/error.rb +22 -0
  11. data/lib/shippinglogic/fedex.rb +84 -0
  12. data/lib/shippinglogic/fedex/cancel.rb +47 -0
  13. data/lib/shippinglogic/fedex/enumerations.rb +348 -0
  14. data/lib/shippinglogic/fedex/error.rb +47 -0
  15. data/lib/shippinglogic/fedex/rate.rb +229 -0
  16. data/lib/shippinglogic/fedex/request.rb +134 -0
  17. data/lib/shippinglogic/fedex/response.rb +72 -0
  18. data/lib/shippinglogic/fedex/service.rb +11 -0
  19. data/lib/shippinglogic/fedex/ship.rb +238 -0
  20. data/lib/shippinglogic/fedex/signature.rb +68 -0
  21. data/lib/shippinglogic/fedex/track.rb +124 -0
  22. data/lib/shippinglogic/proxy.rb +23 -0
  23. data/lib/shippinglogic/service.rb +42 -0
  24. data/lib/shippinglogic/ups.rb +83 -0
  25. data/lib/shippinglogic/ups/cancel.rb +52 -0
  26. data/lib/shippinglogic/ups/enumerations.rb +56 -0
  27. data/lib/shippinglogic/ups/error.rb +42 -0
  28. data/lib/shippinglogic/ups/label.rb +50 -0
  29. data/lib/shippinglogic/ups/rate.rb +228 -0
  30. data/lib/shippinglogic/ups/request.rb +49 -0
  31. data/lib/shippinglogic/ups/response.rb +58 -0
  32. data/lib/shippinglogic/ups/service.rb +11 -0
  33. data/lib/shippinglogic/ups/ship_accept.rb +53 -0
  34. data/lib/shippinglogic/ups/ship_confirm.rb +170 -0
  35. data/lib/shippinglogic/ups/track.rb +118 -0
  36. data/lib/shippinglogic/validation.rb +32 -0
  37. data/shippinglogic.gemspec +120 -0
  38. data/spec/attributes_spec.rb +67 -0
  39. data/spec/config/fedex_credentials.example.yml +4 -0
  40. data/spec/config/ups_credentials.example.yml +3 -0
  41. data/spec/error_spec.rb +43 -0
  42. data/spec/fedex/cancel_spec.rb +10 -0
  43. data/spec/fedex/error_spec.rb +26 -0
  44. data/spec/fedex/rate_spec.rb +87 -0
  45. data/spec/fedex/request_spec.rb +15 -0
  46. data/spec/fedex/responses/blank.xml +0 -0
  47. data/spec/fedex/responses/cancel_not_found.xml +7 -0
  48. data/spec/fedex/responses/failed_authentication.xml +7 -0
  49. data/spec/fedex/responses/malformed.xml +8 -0
  50. data/spec/fedex/responses/rate_defaults.xml +7 -0
  51. data/spec/fedex/responses/rate_insurance.xml +9 -0
  52. data/spec/fedex/responses/rate_no_services.xml +7 -0
  53. data/spec/fedex/responses/rate_non_custom_packaging.xml +7 -0
  54. data/spec/fedex/responses/ship_defaults.xml +7 -0
  55. data/spec/fedex/responses/ship_with_no_signature.xml +7 -0
  56. data/spec/fedex/responses/signature_defaults.xml +7 -0
  57. data/spec/fedex/responses/track_defaults.xml +7 -0
  58. data/spec/fedex/responses/unexpected.xml +1 -0
  59. data/spec/fedex/service_spec.rb +19 -0
  60. data/spec/fedex/ship_spec.rb +37 -0
  61. data/spec/fedex/signature_spec.rb +11 -0
  62. data/spec/fedex/spec_helper.rb +84 -0
  63. data/spec/fedex/track_spec.rb +37 -0
  64. data/spec/fedex_spec.rb +16 -0
  65. data/spec/lib/interceptor.rb +17 -0
  66. data/spec/proxy_spec.rb +42 -0
  67. data/spec/service_spec.rb +23 -0
  68. data/spec/spec_helper.rb +12 -0
  69. data/spec/ups/responses/blank.xml +0 -0
  70. data/spec/ups/responses/track_defaults.xml +2 -0
  71. data/spec/ups/spec_helper.rb +43 -0
  72. data/spec/ups_spec.rb +16 -0
  73. data/spec/validation_spec.rb +49 -0
  74. metadata +163 -0
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,55 @@
1
+ == 1.1.3 released 2009-07-17
2
+
3
+ * Apparently department is not an option, even though FedEx docs say it is and the test services allow it. Production services say its not valid.
4
+
5
+ == 1.1.2 released 2009-07-17
6
+
7
+ * Added :department for shipper and recipient options.
8
+
9
+ == 1.1.1 released 2009-07-17
10
+
11
+ * Added :just_validate options for creating shipments, that leverages FedExs validation option without creating a shipment.
12
+ * Added more shipper and recipient options, also standardized those options between rating and shipping to create consistency.
13
+
14
+ == 1.1.0 released 2009-07-16
15
+
16
+ * Added another layer to tracking. You dont get an array of events anymore, you get a Shippinglogic::Fedex::Track::Details object that has information the single shipment. Such as delivery date, signature name, etc. To access the events you just call the events method on this object.
17
+ * Added speed to rates.
18
+
19
+ == 1.0.8 released 2009-07-09
20
+
21
+ * Fix insurance request format.
22
+
23
+ == 1.0.7 released 2009-07-09
24
+
25
+ * Fix decimal conversion bug, cant convert a raw float to a decimal.
26
+
27
+ == 1.0.6 released 2009-07-09
28
+
29
+ * Reset the cached response if an attribute changes.
30
+
31
+ == 1.0.5 released 2009-07-08
32
+
33
+ * Added request to error objects so you can see both the raw request and response.
34
+
35
+ == 1.0.4 released 2009-07-08
36
+
37
+ * Added state code mapping so that we can pass full state names and have it convert it to a FedEx friendly code behind the scenes.
38
+
39
+ == 1.0.3 released 2009-07-08
40
+
41
+ * Rails uses United States not United State of America
42
+
43
+ == 1.0.2 released 2009-07-08
44
+
45
+ * Remove notifications with a successful severity in the errors message.
46
+ * Added country code mapping so that we can pass full country names and have it convert it to a FedEx friendly code behind the scenes.
47
+
48
+ == 1.0.1 released 2009-07-08
49
+
50
+ * Fixed delivery deadline option for rate.
51
+ * Ship package dimensions if we arent using our own packaging.
52
+
53
+ == 1.0.0 released 2009-07-07
54
+
55
+ * Initial lease
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Ben Johnson of Binary Logic
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,175 @@
1
+ = Shippinglogic
2
+
3
+ == <b>Please note, this library is no longer supported and is being deprecated. A replacement is being worked on that will allow for a seamless transition should use choose to do so.</b>
4
+
5
+ The goal of this library is to provide simple and straight forward interfaces to the various shipping web services: FedEx, UPS, USPS, etc. (Only FedEx is supported at this time)
6
+
7
+ == Helpful links
8
+
9
+ * <b>Documentation:</b> http://rdoc.info/projects/binarylogic/shippinglogic
10
+ * <b>Repository:</b> http://github.com/binarylogic/shippinglogic/tree/master
11
+ * <b>Issues:</b> http://github.com/binarylogic/shippinglogic/issues
12
+ * <b>Google group:</b> http://groups.google.com/group/shippinglogic
13
+
14
+ <b>Before contacting me directly, please read:</b>
15
+
16
+ If you find a bug or a problem please post it in the issues section. If you need help with something, please use google groups. I check both regularly and get emails when anything happens, so that is the best place to get help. This also benefits other people in the future with the same questions / problems. Thank you.
17
+
18
+ == Install & use
19
+
20
+ Install the gem from rubyforge:
21
+
22
+ sudo gem install shippinglogic
23
+
24
+ Now just include it in your project and you are ready to go.
25
+
26
+ You can also install this as a plugin:
27
+
28
+ script/plugin install git://github.com/binarylogic/shippinglogic.git
29
+
30
+ See below for usage examples.
31
+
32
+ == Simple tracking example
33
+
34
+ What I think is unique about this library is it's usage / syntax:
35
+
36
+ fedex = Shippinglogic::FedEx.new(key, password, account, meter)
37
+ tracking = fedex.track(:tracking_number => "XXXXXXXXXXXXXXXXX")
38
+ # => A proxy object that delegates calls to an array of Shippinglogic::FedEx::Track::Event objects
39
+
40
+ tracking.tracking_number
41
+ # => "XXXXXXXXXXXXXXXXX"
42
+
43
+ tracking.tracking_number = "YYYYYYYYYYYYYYYYYY"
44
+ # => "YYYYYYYYYYYYYYYYYY"
45
+
46
+ tracking.status
47
+ # => "Delivered"
48
+
49
+ tracking.signature_name
50
+ # => "KKING"
51
+
52
+ tracking.events.first
53
+ # => #<Shippinglogic::FedEx::Track::Event @postal_code="95817", @name="Delivered", @state="CA", @residential=false,
54
+ # @city="Sacramento", @type="DL", @country="US", @occured_at=Mon Dec 08 10:43:37 -0500 2008>
55
+
56
+ tracking.events.first.name
57
+ # => "Delivered"
58
+
59
+ == Calls to the web services are lazy
60
+
61
+ In our above example, you will notice we are able to access attributes, while at the same time able to treat the object as an array. That's because the object is not actually an array, it acts as a proxy for the underlying array.
62
+
63
+ That being said, a request is not sent to FedEx until we need to deal with the underlying array. Meaning it's lazy, which is more efficient. Most would think the request to FedEx was sent when we initialized the object. This is not the case. The request to FedEx was sent when we executed "tracking.first".
64
+
65
+ This is similar to how ActiveRecord's association proxies work. When you call "user.orders" no database activity occurs until you actually use the object (Ex: user.orders.each).
66
+
67
+ == Flexibility
68
+
69
+ You will notice above we assign the result of the 'track' method to a variable called 'tracking'. That object has more to it:
70
+
71
+ # Initializing
72
+ tracking = fedex.track(:tracking_number => "XXXXXXXXXXXXX")
73
+ tracking.tracking_number
74
+ # => "XXXXXXXXXXXXX"
75
+
76
+ # Attribute accessors
77
+ tracking.tracking_number = "YYYYYYYYYYYYYYY"
78
+ tracking.tracking_number
79
+ # => "YYYYYYYYYYYYYYY"
80
+
81
+ # Mass attribute setting
82
+ tracking.attributes = {:tracking_number => "ZZZZZZZZZZZZZZZZ"}
83
+ tracking.tracking_number
84
+ # => "ZZZZZZZZZZZZZZZZ"
85
+
86
+ tracking.attributes
87
+ # => {:tracking_number => "ZZZZZZZZZZZZZZZZ"}
88
+
89
+ == Available services and their features
90
+
91
+ This library is still very new, as a result only FedEx is supported at this time. More will come.
92
+
93
+ I spent a lot of time on the documentation, for examples of how to use each service please see the docs for their respective classes.
94
+
95
+ === FedEx
96
+
97
+ 1. <b>Tracking</b> - See Shippinglogic::Fedex::Track
98
+ 2. <b>Signature proof of delivery</b> - See Shippinglogic::Fedex::Signature
99
+ 3. <b>Getting service rates</b> - See Shippinglogic::Fedex::Rate
100
+ 4. <b>Creating shipments w/ labels</b> - See Shippinglogic::Fedex::Ship
101
+ 5. <b>Canceling shipments</b> - See Shippinglogic::Fedex::Cancel
102
+
103
+ === Add your own services
104
+
105
+ Simply fork the project and make your changes. If you want to add support for a new service it is very straight forward. Checkout the code in Shippinglogic::Fedex::Track, it very simple and easy to follow. It's a great place to start because its the simplest of services.
106
+
107
+ == Interface integration
108
+
109
+ What's nice about having an object is that you can pass it around. Let's say you wanted to add simple FedEx tracking functionality to your app:
110
+
111
+ class TrackingController < ApplicationController
112
+ def new
113
+ @tracking_details = fedex.track(params[:tracking])
114
+ end
115
+
116
+ def create
117
+ @tracking_details = fedex.track(params[:tracking])
118
+ render :action => :new if !@tracking.successful?
119
+ end
120
+
121
+ private
122
+ def fedex
123
+ @fedex ||= Shippinglogic::FedEx.new(key, password, account, meter)
124
+ end
125
+ end
126
+
127
+ That's pretty simple. Now check out your form:
128
+
129
+ # new.html.haml
130
+ - form_for @tracking_details do |f|
131
+ = f.error_messages
132
+ = f.text_field :tracking_number
133
+ = f.submit "Track"
134
+
135
+ Then your results:
136
+
137
+ # create.html.haml
138
+ .signature_name= @tracking_details.signature_name
139
+ - @tracking_details.events.each do |event|
140
+ .event
141
+ .name= event.name
142
+ .occured_at= event.occured_at.to_s(:long)
143
+ .location== #{event.city}, #{event.state} #{event.postal_code}, #{event.country}
144
+ .residential= event.residential ? "Yes" : "No"
145
+
146
+ == Leave abstraction to your application
147
+
148
+ Here is what I did in an application of mine and it worked out great. I also have complete control of what I'm doing and the library is not limiting me:
149
+
150
+ class Shipment < ActiveRecord::Base
151
+ class Service
152
+ attr_accessor :carrier, :name, "delivered_by, :rate
153
+ end
154
+
155
+ def services
156
+ @services ||= fedex_services # + some_other_services
157
+ end
158
+
159
+ private
160
+ def fedex_services
161
+ rate_options = {} # replace me with your own options accepted by Shippinglogic::FedEx::Rate
162
+ fedex.rate(rate_options).collect do |rate|
163
+ service = Service.new
164
+ service.carrier = :fedex
165
+ serivce.name = rate.name
166
+ service.rate = rate.rate
167
+ service.delivered_by = rate.delivered_by
168
+ service
169
+ end
170
+ end
171
+ end
172
+
173
+ == Copyright
174
+
175
+ Copyright (c) 2009 {Ben Johnson of Binary Logic}[http://www.binarylogic.com], released under the MIT license
@@ -0,0 +1,37 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "shippinglogic"
8
+ gem.summary = "A simple and clean library to interface with shipping carriers"
9
+ gem.description = "Easily use FedEx, UPS, USPS web services with an elegant and simple syntax."
10
+ gem.email = "bjohnson@binarylogic.com"
11
+ gem.homepage = "http://github.com/binarylogic/shippinglogic"
12
+ gem.authors = ["Ben Johnson of Binary Logic"]
13
+ gem.add_development_dependency "rspec"
14
+ gem.add_development_dependency "fakeweb"
15
+ gem.add_dependency "httparty", ">= 0.4.4"
16
+ gem.add_dependency "builder", ">= 2.1.2"
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
21
+ end
22
+
23
+ require 'spec/rake/spectask'
24
+ Spec::Rake::SpecTask.new(:spec) do |spec|
25
+ spec.libs << 'lib' << 'spec'
26
+ spec.spec_files = FileList['spec/**/*_spec.rb']
27
+ end
28
+
29
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.pattern = 'spec/**/*_spec.rb'
32
+ spec.rcov = true
33
+ end
34
+
35
+ task :spec => :check_dependencies
36
+
37
+ task :default => :spec
@@ -0,0 +1,5 @@
1
+ ---
2
+ :major: 1
3
+ :minor: 2
4
+ :patch: 3
5
+ :build:
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), "lib", "shippinglogic")
@@ -0,0 +1,3 @@
1
+ require "shippinglogic/service"
2
+ require "shippinglogic/fedex"
3
+ require "shippinglogic/ups"
@@ -0,0 +1,121 @@
1
+ module Shippinglogic
2
+ # Adds in all of the reading / writing for the various serivce options.
3
+ module Attributes
4
+ def self.included(klass)
5
+ klass.class_eval do
6
+ alias_method(:real_class, :class) unless method_defined? :real_class
7
+
8
+ extend ClassMethods
9
+ include InstanceMethods
10
+ end
11
+ end
12
+
13
+ module ClassMethods
14
+ # Define an attribute for a class, makes adding options / attributes to the class
15
+ # much cleaner. See the Rates class for an example.
16
+ def attribute(name, type, options = {})
17
+ name = name.to_sym
18
+ options[:type] = type.to_sym
19
+ attributes[name] = options
20
+
21
+ define_method(name) { read_attribute(name) }
22
+ define_method("#{name}=") { |value| write_attribute(name, value) }
23
+ end
24
+
25
+ # A hash of all the attributes and their options
26
+ def attributes
27
+ @attributes ||= {}
28
+ end
29
+
30
+ # An array of the attribute names
31
+ def attribute_names
32
+ attributes.keys
33
+ end
34
+
35
+ # Returns the options specified when defining a specific attribute
36
+ def attribute_options(name)
37
+ attributes[name.to_sym]
38
+ end
39
+ end
40
+
41
+ module InstanceMethods
42
+ # A convenience so that you can set attributes while initializing an object
43
+ def initialize(*args)
44
+ attributes = args.last.is_a?(Hash) ? args.last : {}
45
+ @attributes = {}
46
+ self.attributes = attributes
47
+ end
48
+
49
+ # Returns a hash of the various attribute values
50
+ def attributes
51
+ attributes = {}
52
+ attribute_names.each do |name|
53
+ attributes[name] = send(name)
54
+ end
55
+ attributes
56
+ end
57
+
58
+ # Accepts a hash of attribute values and sets each attribute to those values
59
+ def attributes=(value)
60
+ return if value.blank?
61
+ value.each do |key, value|
62
+ next if !attribute_names.include?(key.to_sym)
63
+ send("#{key}=", value)
64
+ end
65
+ end
66
+
67
+ private
68
+ def attribute_names
69
+ real_class.attribute_names
70
+ end
71
+
72
+ def attribute_options(name)
73
+ real_class.attribute_options(name)
74
+ end
75
+
76
+ def attribute_type(name)
77
+ attribute_options(name)[:type]
78
+ end
79
+
80
+ def attribute_default(name)
81
+ default = attribute_options(name)[:default]
82
+ case default
83
+ when Proc
84
+ default.call(self)
85
+ else
86
+ default
87
+ end
88
+ end
89
+
90
+ def write_attribute(name, value)
91
+ @attributes[name.to_sym] = value
92
+ end
93
+
94
+ def read_attribute(name)
95
+ name = name.to_sym
96
+ value = @attributes[name].nil? ? attribute_default(name) : @attributes[name]
97
+ type = attribute_type(name)
98
+ return nil if value.nil? && type != :array
99
+
100
+ case type
101
+ when :array
102
+ value.is_a?(Array) ? value : [value].compact
103
+ when :integer
104
+ value.to_i
105
+ when :float
106
+ value.to_f
107
+ when :decimal
108
+ BigDecimal.new(value.to_s)
109
+ when :boolean
110
+ ["true", "1"].include?(value.to_s)
111
+ when :string, :text
112
+ value.to_s
113
+ when :datetime
114
+ Time.parse(value.to_s)
115
+ else
116
+ value
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,22 @@
1
+ module Shippinglogic
2
+ class Error < StandardError
3
+ attr_accessor :errors, :request, :response
4
+
5
+ def initialize(request, response)
6
+ self.request = request
7
+ self.response = response
8
+ end
9
+
10
+ def add_error(error, code = nil)
11
+ errors << {:message => error, :code => code}
12
+ end
13
+
14
+ def errors
15
+ @errors ||= []
16
+ end
17
+
18
+ def message
19
+ errors.collect { |e| e[:message] }.join(", ")
20
+ end
21
+ end
22
+ end