simple_shipping 0.4.6

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 (101) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.metrics +6 -0
  4. data/.rspec +4 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.simplecov +43 -0
  8. data/Gemfile +26 -0
  9. data/Gemfile.lock +201 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.markdown +207 -0
  12. data/Rakefile +68 -0
  13. data/VERSION +1 -0
  14. data/coverage/.resultset.json +1579 -0
  15. data/lib/colorized_text.rb +34 -0
  16. data/lib/simple_shipping.rb +27 -0
  17. data/lib/simple_shipping/abstract.rb +11 -0
  18. data/lib/simple_shipping/abstract/builder.rb +47 -0
  19. data/lib/simple_shipping/abstract/client.rb +111 -0
  20. data/lib/simple_shipping/abstract/model.rb +40 -0
  21. data/lib/simple_shipping/abstract/request.rb +27 -0
  22. data/lib/simple_shipping/abstract/response.rb +26 -0
  23. data/lib/simple_shipping/address.rb +22 -0
  24. data/lib/simple_shipping/contact.rb +24 -0
  25. data/lib/simple_shipping/demo.rb +9 -0
  26. data/lib/simple_shipping/demo/base.rb +71 -0
  27. data/lib/simple_shipping/demo/fedex.rb +46 -0
  28. data/lib/simple_shipping/demo/ups.rb +68 -0
  29. data/lib/simple_shipping/exceptions.rb +40 -0
  30. data/lib/simple_shipping/fedex.rb +14 -0
  31. data/lib/simple_shipping/fedex/client.rb +41 -0
  32. data/lib/simple_shipping/fedex/package_builder.rb +24 -0
  33. data/lib/simple_shipping/fedex/party_builder.rb +50 -0
  34. data/lib/simple_shipping/fedex/request.rb +54 -0
  35. data/lib/simple_shipping/fedex/response.rb +5 -0
  36. data/lib/simple_shipping/fedex/shipment_builder.rb +123 -0
  37. data/lib/simple_shipping/fedex/shipment_request.rb +14 -0
  38. data/lib/simple_shipping/fedex/shipment_response.rb +12 -0
  39. data/lib/simple_shipping/package.rb +43 -0
  40. data/lib/simple_shipping/party.rb +21 -0
  41. data/lib/simple_shipping/shipment.rb +42 -0
  42. data/lib/simple_shipping/ups.rb +24 -0
  43. data/lib/simple_shipping/ups/client.rb +46 -0
  44. data/lib/simple_shipping/ups/package_builder.rb +101 -0
  45. data/lib/simple_shipping/ups/party_builder.rb +38 -0
  46. data/lib/simple_shipping/ups/request.rb +27 -0
  47. data/lib/simple_shipping/ups/response.rb +63 -0
  48. data/lib/simple_shipping/ups/ship_accept_request.rb +21 -0
  49. data/lib/simple_shipping/ups/ship_accept_response.rb +6 -0
  50. data/lib/simple_shipping/ups/ship_client.rb +70 -0
  51. data/lib/simple_shipping/ups/ship_confirm_request.rb +22 -0
  52. data/lib/simple_shipping/ups/ship_confirm_response.rb +6 -0
  53. data/lib/simple_shipping/ups/shipment_builder.rb +66 -0
  54. data/lib/simple_shipping/ups/shipment_request.rb +22 -0
  55. data/lib/simple_shipping/ups/shipment_response.rb +6 -0
  56. data/lib/simple_shipping/ups/void_client.rb +50 -0
  57. data/lib/simple_shipping/ups/void_request.rb +42 -0
  58. data/lib/simple_shipping/ups/void_response.rb +6 -0
  59. data/lib/tasks/demo.rake +58 -0
  60. data/script/ups_certification.rb +140 -0
  61. data/simple_shipping.gemspec +168 -0
  62. data/spec/fixtures/fedex_shipment_request.soap.xml.erb +85 -0
  63. data/spec/fixtures/fedex_shipment_response.soap.xml.erb +182 -0
  64. data/spec/fixtures/ups_shipment_request.soap.xml.erb +88 -0
  65. data/spec/fixtures/ups_shipment_response.soap.xml.erb +58 -0
  66. data/spec/fixtures/ups_shipment_response_with_faked_label_data.soap.xml.erb +54 -0
  67. data/spec/fixtures/ups_void_request.soap.xml.erb +29 -0
  68. data/spec/fixtures/ups_void_response.soap.xml.erb +21 -0
  69. data/spec/lib/simple_shipping/address_spec.rb +19 -0
  70. data/spec/lib/simple_shipping/contact_spec.rb +28 -0
  71. data/spec/lib/simple_shipping/exceptions_spec.rb +58 -0
  72. data/spec/lib/simple_shipping/fedex/package_builder_spec.rb +5 -0
  73. data/spec/lib/simple_shipping/fedex/party_builder_spec.rb +5 -0
  74. data/spec/lib/simple_shipping/fedex/response/shipment_reponse_spec.rb +5 -0
  75. data/spec/lib/simple_shipping/fedex/response_spec.rb +5 -0
  76. data/spec/lib/simple_shipping/fedex/shipment_builder_spec.rb +23 -0
  77. data/spec/lib/simple_shipping/package_spec.rb +32 -0
  78. data/spec/lib/simple_shipping/party_spec.rb +18 -0
  79. data/spec/lib/simple_shipping/shipment_spec.rb +35 -0
  80. data/spec/lib/simple_shipping/ups/package_builder_spec.rb +26 -0
  81. data/spec/lib/simple_shipping/ups/party_builder_spec.rb +47 -0
  82. data/spec/lib/simple_shipping/ups/response/shipment_response_spec.rb +5 -0
  83. data/spec/lib/simple_shipping/ups/response_spec.rb +33 -0
  84. data/spec/lib/simple_shipping/ups/shipment_builder_spec.rb +19 -0
  85. data/spec/requests/fedex_spec.rb +47 -0
  86. data/spec/requests/ups_spec.rb +75 -0
  87. data/spec/spec_helper.rb +47 -0
  88. data/spec/support/custom_matchers/basic_matcher.rb +13 -0
  89. data/spec/support/custom_matchers/have_attribute_matcher.rb +22 -0
  90. data/spec/support/custom_matchers/have_default_value_matcher.rb +26 -0
  91. data/spec/support/custom_matchers/have_errors_on_matcher.rb +23 -0
  92. data/spec/support/custom_matchers/validate_inclusion_of_matcher.rb +37 -0
  93. data/spec/support/custom_matchers/validate_presence_of_matcher.rb +24 -0
  94. data/spec/support/custom_matchers/validate_submodel_matcher.rb +44 -0
  95. data/spec/support/shared_behaviours/builders_behaviour.rb +9 -0
  96. data/spec/support/shared_behaviours/responses_behaviour.rb +10 -0
  97. data/tmp/metric_fu/_data/20131210.yml +9964 -0
  98. data/wsdl/fedex/ship_service_v10.wsdl +5566 -0
  99. data/wsdl/ups/Ship.wsdl +120 -0
  100. data/wsdl/ups/Void.wsdl +58 -0
  101. metadata +308 -0
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ module SimpleShipping::Ups
4
+ describe PartyBuilder do
5
+ it_should_behave_like "builders"
6
+
7
+ it 'validate account number if party is shipper' do
8
+ shipper = SimpleShipping::Party.new
9
+
10
+ shipper.stub!(:valid? => true)
11
+ lambda {
12
+ PartyBuilder.build(shipper, :shipper => true)
13
+ }.should raise_error SimpleShipping::ValidationError
14
+
15
+ shipper.stub!(:account_number => '123')
16
+ lambda {
17
+ PartyBuilder.build(shipper, :shipper => true)
18
+ }.should_not raise_error SimpleShipping::ValidationError
19
+ end
20
+ end
21
+
22
+ describe "build" do
23
+ it "should include all addresses" do
24
+ contact = SimpleShipping::Contact.new(:phone_number => "5555555555", :person_name => "Frank")
25
+ address = SimpleShipping::Address.new(:country_code => "US", :state_code => "IL", :city => "Chicago", :postal_code => "60622")
26
+ address.street_line = "foo"
27
+ address.street_line_2 = "bar"
28
+ address.street_line_3 = "baz"
29
+
30
+ shipper = SimpleShipping::Party.new(:contact => contact, :address => address)
31
+ party = PartyBuilder.build(shipper)
32
+ party['Address']['AddressLine'].should have(3).addresses
33
+ end
34
+
35
+ it "should exclude nil addresses" do
36
+ contact = SimpleShipping::Contact.new(:phone_number => "5555555555", :person_name => "Frank")
37
+ address = SimpleShipping::Address.new(:country_code => "US", :state_code => "IL", :city => "Chicago", :postal_code => "60622")
38
+ address.street_line = "foo"
39
+ address.street_line_2 = nil
40
+ address.street_line_3 = "baz"
41
+
42
+ shipper = SimpleShipping::Party.new(:contact => contact, :address => address)
43
+ party = PartyBuilder.build(shipper)
44
+ party['Address']['AddressLine'].should have(2).addresses
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe SimpleShipping::Ups::ShipmentResponse do
4
+ it_should_behave_like "responses_with_labels"
5
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe SimpleShipping::Ups::Response do
4
+ it_should_behave_like "responses"
5
+
6
+ context "when success response is received" do
7
+ subject { response }
8
+
9
+ let(:response_fixture) { fixture("ups_shipment_response") }
10
+ let(:http_response) { ::HTTPI::Response.new(200, {}, response_fixture) }
11
+ let(:savon_response) do
12
+ ::Savon::Response.new(
13
+ http_response,
14
+ {
15
+ strip_namespaces: true,
16
+ convert_response_tags_to: ->(tag) { tag.snakecase.to_sym }
17
+ },
18
+ {}
19
+ )
20
+ end
21
+ let(:response) { ::SimpleShipping::Ups::ShipmentResponse.new(savon_response) }
22
+
23
+ its(:shipment_identification_number) { should eq("1Z35679R0294268838") }
24
+ its(:tracking_number) { should eq("1Z35679R0294268838") }
25
+
26
+ describe "label image" do
27
+ let(:response_fixture) { fixture("ups_shipment_response_with_faked_label_data") }
28
+
29
+ # HTML_IMAGE_DATA is the Base64 decoded version of SFRNTF9JTUFHRV9EQVRB
30
+ its(:label_html) { should eq("HTML_IMAGE_DATA") }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe SimpleShipping::Ups::ShipmentBuilder do
4
+ it_should_behave_like "builders"
5
+
6
+ describe 'validation' do
7
+ it 'validates inclusion of service_types' do
8
+ shipment = SimpleShipping::Shipment.new
9
+ shipment.stub!(:valid? => true)
10
+
11
+ lambda {
12
+ SimpleShipping::Ups::ShipmentBuilder.build(shipment, :service_type => :ground)
13
+ }.should_not raise_error SimpleShipping::ValidationError
14
+ lambda {
15
+ SimpleShipping::Ups::ShipmentBuilder.build(shipment, :service_type => :bad)
16
+ }.should raise_error SimpleShipping::ValidationError
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe "FedEx integration test" do
4
+ before { Timecop.freeze }
5
+ after { Timecop.return }
6
+
7
+ #let(:credentials) do
8
+ # YAML.load_file(SimpleShipping::DEMO_CREDENTIALS_FILE)['fedex'].symbolize_keys!
9
+ #end
10
+
11
+ let(:credentials) {{
12
+ :key => 'fedex key',
13
+ :password => 'secret word',
14
+ :account_number => '101010101',
15
+ :meter_number => '202020202'
16
+ }}
17
+
18
+ let(:demo) do
19
+ options = credentials.merge(:live => false)
20
+ SimpleShipping::Demo::Fedex.new(options)
21
+ end
22
+
23
+ context "#shipment_request" do
24
+ it "builds correct SOAP request envelope" do
25
+ req_matcher = lambda do |req|
26
+ expected = Nokogiri::XML(fixture(:fedex_shipment_request, credentials))
27
+ actual = Nokogiri::XML(req.body)
28
+
29
+ actual.root.should be_equivalent_to(expected.root).respecting_element_order
30
+
31
+ req.headers['Soapaction'].should == %{"processShipment"}
32
+
33
+ true
34
+ end
35
+
36
+ req = stub_http_request(:post, demo.fedex_client.class.testing_address).
37
+ with(&req_matcher).
38
+ to_return(:body => fixture(:fedex_shipment_response), :status => 200, :headers => {})
39
+
40
+ resp = demo.shipment_request
41
+
42
+ resp.label_image_base64.should be_present
43
+
44
+ req.should have_been_made
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe "UPS test" do
4
+ let(:credentials) {{
5
+ :username => "Joe13",
6
+ :password => "JoesSecretWord",
7
+ :account_number => "1010101",
8
+ :access_license_number => "Joe's license"
9
+ }}
10
+
11
+ let(:demo) do
12
+ options = credentials.merge!(:live => false)
13
+ SimpleShipping::Demo::Ups.new(options)
14
+ end
15
+
16
+ let(:erb_request_vars) do
17
+ credentials.merge(:shipment_identification_number => demo.shipment_identification_number)
18
+ end
19
+
20
+ describe "ship_client" do
21
+ describe "#shipment_request" do
22
+ it "builds correct SOAP request envelope" do
23
+ req_matcher = lambda do |req|
24
+ expected = Nokogiri::XML(fixture(:ups_shipment_request, erb_request_vars))
25
+ actual = Nokogiri::XML(req.body)
26
+
27
+ actual.root.should be_equivalent_to(expected.root)
28
+ #actual.root.to_s.should == expected.root.to_s
29
+
30
+ true
31
+ end
32
+
33
+ req = stub_http_request(:post, demo.ship_client.class.testing_address).
34
+ with(&req_matcher).
35
+ to_return(:body => fixture(:ups_shipment_response), :status => 200, :headers => {})
36
+
37
+ resp = demo.shipment_request(
38
+
39
+ )
40
+
41
+
42
+ resp.label_image_base64.should be_present
43
+
44
+ req.should have_been_made
45
+ end
46
+ end
47
+ end
48
+
49
+ describe "void client" do
50
+ describe "#void_request" do
51
+ it "builds correct SOAP request envelope" do
52
+ req_matcher = lambda do |req|
53
+ expected = Nokogiri::XML(fixture(:ups_void_request, erb_request_vars))
54
+ actual = Nokogiri::XML(req.body)
55
+
56
+ actual.root.should be_equivalent_to(expected.root)
57
+ #actual.root.to_s.should == expected.root.to_s
58
+
59
+ true
60
+ end
61
+
62
+ req = stub_http_request(:post, demo.void_client.class.testing_address).
63
+ with(&req_matcher).
64
+ to_return(:body => fixture(:ups_void_response), :status => 200, :headers => {})
65
+
66
+ expect{ demo.void_request }.to(
67
+ # NOTE: Exception message is important! This is how we can find out whether SOAP request was made correct -- aignatyev 20130204
68
+ raise_exception(SimpleShipping::RequestError, /No shipment found within the allowed void period/)
69
+ )
70
+
71
+ req.should have_been_made
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,47 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'simple_shipping'
5
+
6
+ require "simplecov"
7
+ require "simplecov-rcov-text"
8
+
9
+ SimpleCov.formatter = SimpleCov::Formatter::RcovTextFormatter
10
+ SimpleCov.start do
11
+ add_filter "/spec/"
12
+ end
13
+
14
+
15
+ require "savon/mock/spec_helper"
16
+ require 'webmock/rspec'
17
+ require "timecop"
18
+ require "erubis"
19
+ require 'equivalent-xml'
20
+
21
+ # Requires supporting files with custom matchers and macros, etc,
22
+ # in ./support/ and its subdirectories.
23
+ require File.expand_path("../support/custom_matchers/basic_matcher", __FILE__)
24
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
25
+
26
+ RSpec.configure do |config|
27
+ config.include SimpleShipping::CustomMatchers
28
+ config.include Savon::SpecHelper
29
+ config.after(:each) { savon.unmock! }
30
+
31
+ config.before(:each) do
32
+ # We are going to enable webmock in some spec on-demand.
33
+ # This is the reason why this config directive is placed in before(:each) hook -- aignatyev 20130204
34
+ WebMock.disable_net_connect!(:allow_localhost => true)
35
+ end
36
+
37
+ def fixtures_dir
38
+ Pathname.new(File.expand_path('../fixtures', __FILE__))
39
+ end
40
+
41
+ def fixture(name, vars = {})
42
+ path = fixtures_dir.join(name.to_s + ".soap.xml.erb")
43
+ template = File.open(path).read
44
+
45
+ Erubis::Eruby.new(template).result(vars)
46
+ end
47
+ end
@@ -0,0 +1,13 @@
1
+ module SimpleShipping
2
+ module CustomMatchers
3
+ class BasicMatcher
4
+ def failure_message
5
+ "expected to #{description}"
6
+ end
7
+
8
+ def negative_failure_message
9
+ "expected to not #{description}"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,22 @@
1
+ module SimpleShipping
2
+ module CustomMatchers
3
+ class HaveAttributeMatcher < BasicMatcher
4
+ def initialize(attribute)
5
+ @attribute = attribute.to_sym
6
+ end
7
+
8
+ def matches?(model)
9
+ model.respond_to?(@attribute) &&
10
+ model.respond_to?("#{@attribute}=")
11
+ end
12
+
13
+ def description
14
+ "have attribute #{@attribute.inspect}"
15
+ end
16
+ end
17
+
18
+ def have_attribute(attr_name)
19
+ HaveAttributeMatcher.new(attr_name)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ module SimpleShipping
2
+ module CustomMatchers
3
+ class HaveDefaultValueMatcher < BasicMatcher
4
+ def initialize(value)
5
+ @value = value
6
+ end
7
+
8
+ def matches?(model)
9
+ model.send(@attribute) == @value
10
+ end
11
+
12
+ def for_attribute(attribute)
13
+ @attribute = attribute.to_sym
14
+ self
15
+ end
16
+
17
+ def description
18
+ "have default value #{@value.inspect} for attribute #{@attribute.inspect}"
19
+ end
20
+ end
21
+
22
+ def have_default_value(value)
23
+ HaveDefaultValueMatcher.new(value)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ module SimpleShipping
2
+ module CustomMatchers
3
+ class HaveErrorsOnMatcher < BasicMatcher
4
+
5
+ def initialize(attribute)
6
+ @attribute = attribute.to_sym
7
+ end
8
+
9
+ def matches?(model)
10
+ model.valid?
11
+ !!model.errors.messages[@attribute]
12
+ end
13
+
14
+ def description
15
+ "have errors on #{@attribute.inspect}"
16
+ end
17
+ end
18
+
19
+ def have_errors_on(attr_name)
20
+ HaveErrorsOnMatcher.new(attr_name)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ module SimpleShipping
2
+ module CustomMatchers
3
+ class ValidateInclusionOfMatcher < BasicMatcher
4
+ def initialize(attribute)
5
+ @attribute = attribute.to_sym
6
+ end
7
+
8
+ def matches?(model)
9
+ @enumeration.each do |value|
10
+ model.send("#{@attribute}=", value)
11
+ return false if has_error?(model)
12
+ end
13
+ model.send("#{@attribute}=", "ANOTHER_VALUE_#{rand}")
14
+ has_error?(model)
15
+ end
16
+
17
+ def in(*enumeration)
18
+ @enumeration = enumeration
19
+ self
20
+ end
21
+
22
+ def description
23
+ "validate inclusion of #{@attribute.inspect} in #{@enumeration.inspect}"
24
+ end
25
+
26
+ def has_error?(model)
27
+ model.valid?
28
+ model.errors.messages[@attribute] and
29
+ model.errors.messages[@attribute].include?("is not included in the list")
30
+ end
31
+ end
32
+
33
+ def validate_inclusion_of(attr_name)
34
+ ValidateInclusionOfMatcher.new(attr_name)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,24 @@
1
+ module SimpleShipping
2
+ module CustomMatchers
3
+ class ValidatePresenceOfMatcher < BasicMatcher
4
+ def initialize(attribute)
5
+ @attribute = attribute.to_sym
6
+ end
7
+
8
+ def matches?(model)
9
+ model.send("#{@attribute}=", nil)
10
+ model.valid?
11
+ model.errors.messages[@attribute] and
12
+ model.errors.messages[@attribute].include? "can't be blank"
13
+ end
14
+
15
+ def description
16
+ "validate presence of #{@attribute.inspect}"
17
+ end
18
+ end
19
+
20
+ def validate_presence_of(attr_name)
21
+ ValidatePresenceOfMatcher.new(attr_name)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,44 @@
1
+ module SimpleShipping
2
+ module CustomMatchers
3
+ class ValidateSubmodelMatcher < BasicMatcher
4
+ def initialize(attribute)
5
+ @attribute = attribute.to_sym
6
+ end
7
+
8
+ def matches?(model)
9
+ @model = model
10
+
11
+ @model.send("#{@attribute}=", Class.new.new)
12
+ return false unless has_error?
13
+
14
+ submodel = @class.new
15
+ submodel.stub!(:valid? => false)
16
+ @model.send("#{@attribute}=", submodel)
17
+ return false unless has_error?
18
+
19
+ submodel.stub!(:valid? => true)
20
+ @model.send("#{@attribute}=", submodel)
21
+ !has_error?
22
+ end
23
+
24
+ def as(klass)
25
+ @class = klass
26
+ self
27
+ end
28
+
29
+ def description
30
+ "validate submodel #{@attribute.inspect} as instance of #{@class}"
31
+ end
32
+
33
+ def has_error?
34
+ @model.valid?
35
+ !!@model.errors.messages[@attribute]
36
+ end
37
+ private :has_error?
38
+ end
39
+
40
+ def validate_submodel(attr_name)
41
+ ValidateSubmodelMatcher.new(attr_name)
42
+ end
43
+ end
44
+ end