woahdae-consumer 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/LICENSE +20 -0
- data/Manifest.txt +73 -0
- data/PostInstall.txt +8 -0
- data/README.rdoc +53 -0
- data/Rakefile +30 -0
- data/app_generators/consumer/USAGE +14 -0
- data/app_generators/consumer/consumer_generator.rb +89 -0
- data/app_generators/consumer/templates/LICENSE +20 -0
- data/app_generators/consumer/templates/README.rdoc +3 -0
- data/app_generators/consumer/templates/Rakefile +57 -0
- data/app_generators/consumer/templates/TODO +4 -0
- data/app_generators/consumer/templates/config/config.yml +2 -0
- data/app_generators/consumer/templates/config/config.yml.sample +1 -0
- data/app_generators/consumer/templates/lib/base.rb +6 -0
- data/app_generators/consumer/templates/rails/init.rb +1 -0
- data/app_generators/consumer/templates/script/destroy +14 -0
- data/app_generators/consumer/templates/script/generate +14 -0
- data/app_generators/consumer/templates/spec/spec_helper.rb +11 -0
- data/bin/consumer +17 -0
- data/config/website.yml.sample +2 -0
- data/consumer.gemspec +48 -0
- data/consumer_generators/request/USAGE +25 -0
- data/consumer_generators/request/request_generator.rb +94 -0
- data/consumer_generators/request/templates/lib/request.rb +55 -0
- data/consumer_generators/request/templates/lib/response.rb +12 -0
- data/consumer_generators/request/templates/spec/request_spec.rb +27 -0
- data/consumer_generators/request/templates/spec/response_spec.rb +10 -0
- data/consumer_generators/request/templates/spec/xml/response.xml +0 -0
- data/examples/active_record/README.txt +1 -0
- data/examples/active_record/ar_spec.rb +33 -0
- data/examples/active_record/database.sqlite +0 -0
- data/examples/active_record/environment.rb +15 -0
- data/examples/active_record/migration.rb +21 -0
- data/examples/active_record/models/book.rb +13 -0
- data/examples/active_record/models/contributor.rb +12 -0
- data/examples/active_record/xml/book.xml +6 -0
- data/examples/active_record/xml/book_with_contributors.xml +11 -0
- data/examples/active_record/xml/contributor.xml +3 -0
- data/examples/active_record/xml/contributor_with_books.xml +19 -0
- data/examples/shipping/environment.rb +3 -0
- data/examples/shipping/rate.rb +15 -0
- data/examples/shipping/shipping.yml.sample +8 -0
- data/examples/shipping/shipping_spec.rb +27 -0
- data/examples/shipping/ups_rate_request.rb +182 -0
- data/examples/shipping/ups_rate_response.xml +340 -0
- data/lib/consumer/helper.rb +111 -0
- data/lib/consumer/mapping.rb +184 -0
- data/lib/consumer/request.rb +280 -0
- data/lib/consumer.rb +28 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +71 -0
- data/spec/helper_spec.rb +136 -0
- data/spec/mapping_spec.rb +94 -0
- data/spec/request_spec.rb +75 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/xml/rate_response.xml +14 -0
- data/spec/xml/rate_response_error.xml +35 -0
- data/tasks/rspec.rake +21 -0
- data/test/test_consumer_generator.rb +68 -0
- data/test/test_generator_helper.rb +29 -0
- data/website/index.html +11 -0
- data/website/index.txt +81 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +159 -0
- data/website/template.html.erb +50 -0
- metadata +180 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
# === Vendor API Docs
|
2
|
+
# http://www.example.com/handy_docs
|
3
|
+
class <%= "#{appname.camelcase}::#{request_class}" %> < Consumer::Request
|
4
|
+
|
5
|
+
### Note: Everything except to_xml is optional (though handy) ###
|
6
|
+
|
7
|
+
response_class "<%= response_class %>"
|
8
|
+
yaml_defaults "<%= appname %>.yml", "<%= request_base.underscore %>"
|
9
|
+
|
10
|
+
# If root is found in a response, code + message will be raised
|
11
|
+
error_paths({
|
12
|
+
:root => "//Error",
|
13
|
+
:code => "//ErrorCode",
|
14
|
+
:message => "//ErrorDescription"
|
15
|
+
})
|
16
|
+
|
17
|
+
# Instance variables that must be set before xml sendoff
|
18
|
+
def required
|
19
|
+
return [
|
20
|
+
# :required_attrs_array
|
21
|
+
]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Lowest priority; overwritten by YAML, then params
|
25
|
+
def defaults
|
26
|
+
return {
|
27
|
+
# :sensible_default => "Value"
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def url
|
32
|
+
return "www.example.com/testing" if $TESTING
|
33
|
+
|
34
|
+
"www.example.com"
|
35
|
+
end
|
36
|
+
|
37
|
+
def headers
|
38
|
+
{"SoapAction" => ""}
|
39
|
+
end
|
40
|
+
|
41
|
+
# Called before required vars are checked, too
|
42
|
+
def before_to_xml; end
|
43
|
+
|
44
|
+
# All this has to do is return xml, and we have a Builder instance to help.
|
45
|
+
# Also, defaults, YAML, and the params are accessed via instance variables.
|
46
|
+
def to_xml
|
47
|
+
b.instruct!
|
48
|
+
|
49
|
+
# docs at http://builder.rubyforge.org/classes/Builder/XmlMarkup.html
|
50
|
+
# example:
|
51
|
+
b.<%= request_class %> {
|
52
|
+
b.Hello "World"
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class <%= "#{appname.camelcase}::#{response_class}" %>
|
2
|
+
include Consumer::Mapping
|
3
|
+
attr_accessor :attribute
|
4
|
+
|
5
|
+
# This is fairly dense; see documentation for full explanation
|
6
|
+
# map(:all or :first, "root xpath", registry, opts = {}, &postprocessing)
|
7
|
+
map(:all, "//FullyQualified/Xpath/ToRoot", {
|
8
|
+
:attribute => "RelativeOrFQxPathToValue",
|
9
|
+
},
|
10
|
+
:include => [:association1, :association2]
|
11
|
+
) {|<%= response_class.underscore %>| <%= response_class.underscore%>.attribute.strip! }
|
12
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/spec_helper"
|
2
|
+
|
3
|
+
describe <%= "#{appname.camelcase}::#{request_class}" %> do
|
4
|
+
|
5
|
+
it "creates xml" do
|
6
|
+
<%= request_class.underscore %> = <%= request_class %>.new({
|
7
|
+
# :attribute => value
|
8
|
+
})
|
9
|
+
xml = <%= request_class.underscore %>.to_xml_etc
|
10
|
+
xml.should =~ /\<\?xml/
|
11
|
+
end
|
12
|
+
|
13
|
+
# run "DO_IT_LIVE=true spec spec" to contact the api
|
14
|
+
if ENV['DO_IT_LIVE']
|
15
|
+
|
16
|
+
it "contacts the live api and returns <%= response_class %> instance(s)" do
|
17
|
+
$DEBUG = true # spit out xml for the request & response
|
18
|
+
|
19
|
+
<%= response_class.underscore %> = <%= request_class %>.new({
|
20
|
+
# :attribute => "value"
|
21
|
+
}).do
|
22
|
+
<%= response_class.underscore %>.should_not be_blank
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/spec_helper"
|
2
|
+
|
3
|
+
describe <%= "#{appname.camelcase}::#{response_class}" %> do
|
4
|
+
it "should make an instance of itself via from_xml" do
|
5
|
+
file = "spec/xml/<%= response_xml %>"
|
6
|
+
xml = File.read("#{file}")
|
7
|
+
|
8
|
+
<%= response_class %>.from_xml(xml).should_not be_blank
|
9
|
+
end
|
10
|
+
end
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
This is an example focusing on maps in an active_record environment. Requests are irrelevant here, as they're not intended to ever be used with ActiveRecord (althogh nothing prevents it).
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec'
|
2
|
+
require 'environment'
|
3
|
+
|
4
|
+
# Very broad tests to see ROXML working with Active Record
|
5
|
+
|
6
|
+
describe "ROXML in ActiveRecord" do
|
7
|
+
before(:all) do
|
8
|
+
@book_xml = File.read("xml/book.xml")
|
9
|
+
@book_with_contributors_xml = File.read("xml/book_with_contributors.xml")
|
10
|
+
@contributor_with_books_xml = File.read("xml/contributor_with_books.xml")
|
11
|
+
@contributor_xml = File.read("xml/contributor.xml")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "creates a valid AR object from xml" do
|
15
|
+
book = Book.from_xml(@book_xml)
|
16
|
+
book.isbn.should == "0974514055"
|
17
|
+
book.title.should == "Programming Ruby - 2nd Edition"
|
18
|
+
book.description.should == "Second edition of the great book out there"
|
19
|
+
book.save.should be_true
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "habtm for books-contributors" do
|
23
|
+
it "works from the book side of the relationship" do
|
24
|
+
book = Book.from_xml(@book_with_contributors_xml)
|
25
|
+
book.contributors.size.should == 3
|
26
|
+
end
|
27
|
+
|
28
|
+
it "works from the contributor side of the relationship" do
|
29
|
+
contributor = Contributor.from_xml(@contributor_with_books_xml)
|
30
|
+
contributor.books.size.should == 3
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
Binary file
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_record'
|
3
|
+
require 'sqlite3'
|
4
|
+
|
5
|
+
|
6
|
+
require File.dirname(__FILE__) + '/../../lib/consumer.rb'
|
7
|
+
|
8
|
+
$:.unshift(File.dirname(__FILE__))
|
9
|
+
require 'models/contributor'
|
10
|
+
require 'models/book'
|
11
|
+
|
12
|
+
ActiveRecord::Base.establish_connection(
|
13
|
+
:adapter => 'sqlite3',
|
14
|
+
:dbfile => 'database.sqlite'
|
15
|
+
)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/environment'
|
4
|
+
|
5
|
+
ActiveRecord::Schema.define do
|
6
|
+
create_table "books", :force => true do |t|
|
7
|
+
t.string :title
|
8
|
+
t.text :description
|
9
|
+
t.string :isbn
|
10
|
+
end
|
11
|
+
|
12
|
+
create_table "contributors", :force => true do |t|
|
13
|
+
t.string :name
|
14
|
+
t.string :role
|
15
|
+
end
|
16
|
+
|
17
|
+
create_table "books_contributors", :force => true do |t|
|
18
|
+
t.integer :book_id
|
19
|
+
t.integer :contributor_id
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Book < ActiveRecord::Base
|
2
|
+
include Consumer::Mapping
|
3
|
+
has_and_belongs_to_many :contributors
|
4
|
+
|
5
|
+
@habtm_registry = {
|
6
|
+
:isbn => "attribute::isbn",
|
7
|
+
:title => "title",
|
8
|
+
:description => "description"
|
9
|
+
}
|
10
|
+
|
11
|
+
map(:first, "//BookResponse/book", @habtm_registry, :include => :contributors)
|
12
|
+
map(:all, "//ContributorResponse/contributor/books/book", @habtm_registry)
|
13
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Contributor < ActiveRecord::Base
|
2
|
+
include Consumer::Mapping
|
3
|
+
has_and_belongs_to_many :books
|
4
|
+
|
5
|
+
@habtm_registry = {
|
6
|
+
:role => "attribute::role",
|
7
|
+
:name => "name"
|
8
|
+
}
|
9
|
+
|
10
|
+
map(:first, "//ContributorResponse/contributor", @habtm_registry, :include => :books)
|
11
|
+
map(:all, "//BookResponse/book/contributors/contributor", @habtm_registry)
|
12
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<BookResponse>
|
2
|
+
<book isbn="0974514055">
|
3
|
+
<title>Programming Ruby - 2nd Edition</title>
|
4
|
+
<description>Second edition of the great book out there</description>
|
5
|
+
<contributors>
|
6
|
+
<contributor role="author"><name>David Thomas</name></contributor>
|
7
|
+
<contributor role="supporting author"><name>Andrew Hunt</name></contributor>
|
8
|
+
<contributor role="supporting author"><name>Chad Fowler</name></contributor>
|
9
|
+
</contributors>
|
10
|
+
</book>
|
11
|
+
</BookResponse>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<ContributorResponse>
|
2
|
+
<contributor role="author">
|
3
|
+
<name>David Thomas</name>
|
4
|
+
<books>
|
5
|
+
<book isbn="0974514055">
|
6
|
+
<title>Programming Ruby - 2nd Edition</title>
|
7
|
+
<description>Second edition of the great book out there</description>
|
8
|
+
</book>
|
9
|
+
<book isbn="1112223334">
|
10
|
+
<title>Hitchhikers Guide to the Galaxy</title>
|
11
|
+
<description>42</description>
|
12
|
+
</book>
|
13
|
+
<book isbn="4556677889">
|
14
|
+
<title>Cats</title>
|
15
|
+
<description>lol</description>
|
16
|
+
</book>
|
17
|
+
</books>
|
18
|
+
</contributor>
|
19
|
+
</ContributorResponse>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/environment"
|
2
|
+
|
3
|
+
class Rate
|
4
|
+
include Consumer::Mapping
|
5
|
+
attr_accessor :service, :code, :price, :carrier
|
6
|
+
|
7
|
+
# UPS
|
8
|
+
map(:all, "//RatingServiceSelectionResponse/RatedShipment", {
|
9
|
+
:price => "TotalCharges/MonetaryValue",
|
10
|
+
# :name => "Service",
|
11
|
+
:code => "Service/Code"
|
12
|
+
}) {|instance| instance.carrier = "UPS" }
|
13
|
+
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
require File.dirname(__FILE__) + "/environment"
|
4
|
+
|
5
|
+
$TESTING = true
|
6
|
+
$DEBUG = true
|
7
|
+
describe "Shipping" do
|
8
|
+
if ENV['DO_IT_LIVE'] # http://www.youtube.com/watch?v=2tJjNVVwRCY&feature=related
|
9
|
+
describe "UPS" do
|
10
|
+
it "should work" do
|
11
|
+
rates = UPSRateRequest.new(
|
12
|
+
:zip => "98125",
|
13
|
+
:country => "US",
|
14
|
+
:weight => "5.00",
|
15
|
+
|
16
|
+
# optional
|
17
|
+
# :city => "Seattle",
|
18
|
+
# :state => "WA",
|
19
|
+
:request_type => "Shop" # take out shop, and it'll return one ground rate
|
20
|
+
).do
|
21
|
+
|
22
|
+
rates.should_not be_nil
|
23
|
+
rates.size.should > 1 # comment unless request_type => 'Shop'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/environment"
|
2
|
+
|
3
|
+
# === Vendor API Docs
|
4
|
+
#
|
5
|
+
# http://www.ups.com/gec/techdocs/pdf/dtk_RateXML_V1.zip
|
6
|
+
class UPSRateRequest < Consumer::Request
|
7
|
+
response_class "Rate"
|
8
|
+
error_paths({
|
9
|
+
:root => "//Error",
|
10
|
+
:code => "//ErrorCode",
|
11
|
+
:message => "//ErrorDescription"
|
12
|
+
})
|
13
|
+
yaml_defaults "shipping.yml", "ups"
|
14
|
+
required(
|
15
|
+
# these are in shipping.yml
|
16
|
+
:access_license_number,
|
17
|
+
:sender_zip,
|
18
|
+
:sender_country,
|
19
|
+
# these must be passed in
|
20
|
+
:zip,
|
21
|
+
:country,
|
22
|
+
# :city,
|
23
|
+
# :state,
|
24
|
+
:weight
|
25
|
+
)
|
26
|
+
defaults({
|
27
|
+
:customer_type => "wholesale",
|
28
|
+
:pickup_type => "daily_pickup",
|
29
|
+
:request_type => "Rate", # or Shop
|
30
|
+
:service_type => "ground",
|
31
|
+
:package_type => "your_packaging",
|
32
|
+
:weight_units => 'LBS', # or KGS
|
33
|
+
:measure_units => 'IN',
|
34
|
+
:measure_length => 0,
|
35
|
+
:measure_width => 0,
|
36
|
+
:measure_height => 0,
|
37
|
+
# optional, but all or none
|
38
|
+
# :currency_code => "US",
|
39
|
+
# :insured_value => 0
|
40
|
+
})
|
41
|
+
# optional
|
42
|
+
# * :city
|
43
|
+
# * :state
|
44
|
+
# * :sender_city
|
45
|
+
# * :sender_state
|
46
|
+
|
47
|
+
|
48
|
+
def url
|
49
|
+
return "https://wwwcie.ups.com/ups.app/xml/Rate" if $TESTING
|
50
|
+
|
51
|
+
"https://www.ups.com/ups.app/xml/Rate"
|
52
|
+
end
|
53
|
+
|
54
|
+
API_VERSION = "1.0001"
|
55
|
+
|
56
|
+
PackageTypes = {
|
57
|
+
"ups_envelope" => "01",
|
58
|
+
"your_packaging" => "02",
|
59
|
+
"ups_tube" => "03",
|
60
|
+
"ups_pak" => "04",
|
61
|
+
"ups_box" => "21",
|
62
|
+
"fedex_25_kg_box" => "24",
|
63
|
+
"fedex_10_kg_box" => "25"
|
64
|
+
}
|
65
|
+
|
66
|
+
ServiceTypes = {
|
67
|
+
"next_day" => "01",
|
68
|
+
"2day" => "02",
|
69
|
+
"ground" => "03",
|
70
|
+
"worldwide_express" => "07",
|
71
|
+
"worldwide_expedited" => "08",
|
72
|
+
"standard" => "11",
|
73
|
+
"3day" => "12",
|
74
|
+
"next_day_saver" => "13",
|
75
|
+
"next_day_early" => "14",
|
76
|
+
"worldwide_express_plus" => "54",
|
77
|
+
"2day_early" => "59"
|
78
|
+
}
|
79
|
+
|
80
|
+
PaymentTypes = {
|
81
|
+
'prepaid' => 'Prepaid',
|
82
|
+
'consignee' => 'Consignee',
|
83
|
+
'bill_third_party' => 'BillThirdParty',
|
84
|
+
'freight_collect' => 'FreightCollect'
|
85
|
+
}
|
86
|
+
|
87
|
+
# UPS-Specific types
|
88
|
+
|
89
|
+
PickupTypes = {
|
90
|
+
'daily_pickup' => '01',
|
91
|
+
'customer_counter' => '03',
|
92
|
+
'one_time_pickup' => '06',
|
93
|
+
'on_call' => '07',
|
94
|
+
'suggested_retail_rates' => '11',
|
95
|
+
'letter_center' => '19',
|
96
|
+
'air_service_center' => '20'
|
97
|
+
}
|
98
|
+
|
99
|
+
CustomerTypes = {
|
100
|
+
'wholesale' => '01',
|
101
|
+
'ocassional' => '02',
|
102
|
+
'retail' => '04'
|
103
|
+
}
|
104
|
+
|
105
|
+
def to_xml
|
106
|
+
b.instruct!
|
107
|
+
|
108
|
+
b.AccessRequest {
|
109
|
+
b.AccessLicenseNumber @access_license_number
|
110
|
+
b.UserId @user_id
|
111
|
+
b.Password @password
|
112
|
+
}
|
113
|
+
|
114
|
+
b.instruct!
|
115
|
+
|
116
|
+
b.RatingServiceSelectionRequest {
|
117
|
+
b.Request {
|
118
|
+
b.TransactionReference {
|
119
|
+
b.CustomerContext 'Rating and Service'
|
120
|
+
b.XpciVersion API_VERSION
|
121
|
+
}
|
122
|
+
b.RequestAction 'Rate'
|
123
|
+
b.RequestOption @request_type
|
124
|
+
}
|
125
|
+
b.CustomerClassification {
|
126
|
+
b.Code CustomerTypes[@customer_type]
|
127
|
+
}
|
128
|
+
b.PickupType {
|
129
|
+
b.Code PickupTypes[@pickup_type]
|
130
|
+
}
|
131
|
+
b.Shipment {
|
132
|
+
b.Shipper {
|
133
|
+
b.Address {
|
134
|
+
b.PostalCode @sender_zip
|
135
|
+
b.CountryCode @sender_country
|
136
|
+
b.City @sender_city
|
137
|
+
b.StateProvinceCode @sender_state
|
138
|
+
}
|
139
|
+
}
|
140
|
+
b.ShipTo {
|
141
|
+
b.Address {
|
142
|
+
b.PostalCode @zip
|
143
|
+
b.CountryCode @country
|
144
|
+
b.City @city
|
145
|
+
b.StateProvinceCode @state
|
146
|
+
}
|
147
|
+
}
|
148
|
+
b.Service {
|
149
|
+
b.Code ServiceTypes[@service_type]
|
150
|
+
}
|
151
|
+
b.Package {
|
152
|
+
b.PackagingType {
|
153
|
+
b.Code PackageTypes[@package_type]
|
154
|
+
b.Description 'Package'
|
155
|
+
}
|
156
|
+
b.Description 'Rate Shopping'
|
157
|
+
b.PackageWeight {
|
158
|
+
b.Weight @weight
|
159
|
+
b.UnitOfMeasurement {
|
160
|
+
b.Code @weight_units
|
161
|
+
}
|
162
|
+
}
|
163
|
+
b.Dimensions {
|
164
|
+
b.UnitOfMeasurement {
|
165
|
+
b.Code @measure_units
|
166
|
+
}
|
167
|
+
b.Length @measure_length
|
168
|
+
b.Width @measure_width
|
169
|
+
b.Height @measure_height
|
170
|
+
}
|
171
|
+
b.PackageServiceOptions {
|
172
|
+
b.InsuredValue {
|
173
|
+
b.CurrencyCode @currency_code
|
174
|
+
b.MonetaryValue @insured_value
|
175
|
+
}
|
176
|
+
}
|
177
|
+
}
|
178
|
+
}
|
179
|
+
}
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|