cxml 0.1.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.
- data/.gitignore +24 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +2 -0
- data/LICENSE +7 -0
- data/README.md +57 -0
- data/Rakefile +10 -0
- data/cxml.gemspec +24 -0
- data/lib/commerce.rb +90 -0
- data/lib/cxml.rb +29 -0
- data/lib/cxml/credential.rb +62 -0
- data/lib/cxml/credential_mac.rb +22 -0
- data/lib/cxml/document.rb +62 -0
- data/lib/cxml/documents/confirmation_request.rb +19 -0
- data/lib/cxml/documents/request_doc.rb +36 -0
- data/lib/cxml/documents/ship_notice_request.rb +12 -0
- data/lib/cxml/errors.rb +4 -0
- data/lib/cxml/header.rb +41 -0
- data/lib/cxml/money.rb +23 -0
- data/lib/cxml/parser.rb +10 -0
- data/lib/cxml/protocol.rb +55 -0
- data/lib/cxml/request.rb +24 -0
- data/lib/cxml/response.rb +24 -0
- data/lib/cxml/sender.rb +21 -0
- data/lib/cxml/status.rb +35 -0
- data/lib/cxml/version.rb +3 -0
- data/spec/cxml_spec.rb +5 -0
- data/spec/document_spec.rb +34 -0
- data/spec/fixtures/.gitkeep +0 -0
- data/spec/fixtures/envelope.xml +31 -0
- data/spec/fixtures/envelope2.xml +36 -0
- data/spec/fixtures/envelope3.xml +77 -0
- data/spec/fixtures/response_status_400.xml +5 -0
- data/spec/parser_spec.rb +4 -0
- data/spec/protocol_spec.rb +14 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/status_spec.rb +51 -0
- metadata +191 -0
data/.gitignore
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
*.gem
|
|
2
|
+
*.rbc
|
|
3
|
+
*.swp
|
|
4
|
+
*.tmproj
|
|
5
|
+
*~
|
|
6
|
+
.DS_Store
|
|
7
|
+
.\#*
|
|
8
|
+
.bundle
|
|
9
|
+
.config
|
|
10
|
+
.yardoc
|
|
11
|
+
Gemfile.lock
|
|
12
|
+
InstalledFiles
|
|
13
|
+
\#*
|
|
14
|
+
_yardoc
|
|
15
|
+
coverage
|
|
16
|
+
doc/
|
|
17
|
+
lib/bundler/man
|
|
18
|
+
pkg
|
|
19
|
+
rdoc
|
|
20
|
+
spec/reports
|
|
21
|
+
test/tmp
|
|
22
|
+
test/version_tmp
|
|
23
|
+
tmp
|
|
24
|
+
tmtags
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright (c) 2012 Dan Sosedoff.
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# cXML
|
|
2
|
+
|
|
3
|
+
Ruby implementation of cXML protocol.
|
|
4
|
+
|
|
5
|
+
## Documentation
|
|
6
|
+
|
|
7
|
+
Procotol specifications could be found here [http://xml.cxml.org/current/cXMLUsersGuide.pdf](http://xml.cxml.org/current/cXMLUsersGuide.pdf)
|
|
8
|
+
|
|
9
|
+
## Parsing cXML
|
|
10
|
+
|
|
11
|
+
To parse cXML, simply pass the raw text of the document to CXML.parse
|
|
12
|
+
|
|
13
|
+
CXML.parse("<xml namespace=...")
|
|
14
|
+
|
|
15
|
+
This will return a well-constructed hash based on the conents of the document.
|
|
16
|
+
|
|
17
|
+
## Commerce
|
|
18
|
+
|
|
19
|
+
cXML library can be used to handle inbound cXML requests via the Commerce.dispatch function. E.g.
|
|
20
|
+
|
|
21
|
+
class CommerceController < ApplicationController
|
|
22
|
+
|
|
23
|
+
def handle
|
|
24
|
+
Commerce.dispatch(request.raw_post) do
|
|
25
|
+
order_request do |order_request|
|
|
26
|
+
respond_to do |format|
|
|
27
|
+
format.xml { render xml: Commerce::Response.success }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
## Building documents
|
|
36
|
+
|
|
37
|
+
To build request documents, you can use the document builder. E.g.
|
|
38
|
+
|
|
39
|
+
ConfirmationRequest.new(type: 'reject').render
|
|
40
|
+
|
|
41
|
+
or send the response to a server (via RestClient)
|
|
42
|
+
|
|
43
|
+
ShipNoticeRequest.new.send("http://example.com/cxml")
|
|
44
|
+
|
|
45
|
+
## Running Tests
|
|
46
|
+
|
|
47
|
+
Install dependencies:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
bundle install
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Run suite:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
rake test
|
|
57
|
+
```
|
data/Rakefile
ADDED
data/cxml.gemspec
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require File.expand_path('../lib/cxml/version', __FILE__)
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |s|
|
|
4
|
+
s.name = "cxml"
|
|
5
|
+
s.version = CXML::VERSION
|
|
6
|
+
s.summary = "Ruby library to work with cXML protocol"
|
|
7
|
+
s.description = "Ruby library to work with cXML protocol"
|
|
8
|
+
s.homepage = "http://github.com/sosedoff/cxml"
|
|
9
|
+
s.authors = ["Dan Sosedoff","Geoff Hayes"]
|
|
10
|
+
s.email = ["dan.sosedoff@gmail.com", "hayesgm@gmail.com"]
|
|
11
|
+
|
|
12
|
+
s.add_development_dependency 'rake'
|
|
13
|
+
s.add_development_dependency 'rspec', '~> 2.11'
|
|
14
|
+
s.add_development_dependency 'simplecov', '~> 0.4'
|
|
15
|
+
|
|
16
|
+
s.add_dependency 'nokogiri'
|
|
17
|
+
s.add_dependency 'xml-simple'
|
|
18
|
+
s.add_dependency 'hashr'
|
|
19
|
+
|
|
20
|
+
s.files = `git ls-files`.split("\n")
|
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
|
|
23
|
+
s.require_paths = ["lib"]
|
|
24
|
+
end
|
data/lib/commerce.rb
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# The Commerce module is used for quickly building documents and dispatching inbound requests
|
|
2
|
+
module Commerce
|
|
3
|
+
|
|
4
|
+
# Define errors
|
|
5
|
+
module CommerceError
|
|
6
|
+
class CommerceError < StandardError; end
|
|
7
|
+
class UnableToProcessError < CommerceError; end
|
|
8
|
+
class InvalidRequestError < CommerceError; end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Block object used for binding in dispatch blocks
|
|
12
|
+
class BlockObject
|
|
13
|
+
def initialize
|
|
14
|
+
@procs = {}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def procs
|
|
18
|
+
@procs
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def method_missing(method, *args, &block)
|
|
22
|
+
raise CommerceError::CommerceError, "Missing dispatch block" unless block_given?
|
|
23
|
+
|
|
24
|
+
@procs[method.to_sym] = block
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Response used for status-responses
|
|
29
|
+
# TODO: Move to cxml/documents
|
|
30
|
+
class Response
|
|
31
|
+
def self.success
|
|
32
|
+
d = CXML::Document.new('Response' => { 'Status' => { 'code' => 200, 'text' => 'OK' } } )
|
|
33
|
+
d.setup
|
|
34
|
+
d.render
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.failure(message)
|
|
38
|
+
# TODO: Get failure up to specs
|
|
39
|
+
d = CXML::Document.new('Response' => { 'Status' => { 'code' => 400, 'text' => message } } )
|
|
40
|
+
d.setup
|
|
41
|
+
d.render
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Dispatch can be used to handle incoming cXML requests
|
|
46
|
+
#
|
|
47
|
+
# E.g.
|
|
48
|
+
# Commerce.dispatch(request.raw_post) do
|
|
49
|
+
# order_request do |order_request|
|
|
50
|
+
# # Process order request
|
|
51
|
+
# render xml: Commerce::Response.success
|
|
52
|
+
# end
|
|
53
|
+
# end
|
|
54
|
+
def self.dispatch(xml, &block)
|
|
55
|
+
raise CommerceError::CommerceError, "Missing xml" if xml.blank?
|
|
56
|
+
raise CommerceError::CommerceError, "Missing dispatch block" unless block_given?
|
|
57
|
+
|
|
58
|
+
cxml = CXML.parse(xml)
|
|
59
|
+
request = cxml['Request']
|
|
60
|
+
|
|
61
|
+
raise CommerceError::InvalidRequestError, "No request element" if request.nil?
|
|
62
|
+
|
|
63
|
+
deployment_mode = request.delete('deploymentMode')
|
|
64
|
+
id = request.delete('Id')
|
|
65
|
+
|
|
66
|
+
raise CommerceError::InvalidRequestError, "Invalid request object: #{request}" if request.keys.count != 1
|
|
67
|
+
|
|
68
|
+
request_type, request_item = request.first
|
|
69
|
+
|
|
70
|
+
Commerce.debug [ 'Commerce::Dispatch', 'Received request item', request_type, request_item ]
|
|
71
|
+
|
|
72
|
+
block_object = Commerce::BlockObject.new
|
|
73
|
+
block_object.instance_eval(&block)
|
|
74
|
+
|
|
75
|
+
processor = block_object.procs[request_type.underscore.to_sym]
|
|
76
|
+
|
|
77
|
+
raise CommerceError::CommerceError, "Missing handler for #{request_type.underscore}" unless processor
|
|
78
|
+
|
|
79
|
+
obj = block.binding.eval("self") # Grab self of caller
|
|
80
|
+
obj.instance_exec(request_item, &processor)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Send debug messages
|
|
84
|
+
def self.debug(*args)
|
|
85
|
+
if defined?(Rails)
|
|
86
|
+
Rails.logger.debug *args if Rails.logger
|
|
87
|
+
p *args if Rails.env && Rails.env.development?
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
data/lib/cxml.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'cxml/version'
|
|
2
|
+
require 'cxml/errors'
|
|
3
|
+
require 'time'
|
|
4
|
+
require 'nokogiri'
|
|
5
|
+
require 'commerce'
|
|
6
|
+
require 'cxml/documents/request_doc'
|
|
7
|
+
require 'cxml/documents/confirmation_request'
|
|
8
|
+
require 'cxml/documents/ship_notice_request'
|
|
9
|
+
|
|
10
|
+
module CXML
|
|
11
|
+
autoload :Protocol, 'cxml/protocol'
|
|
12
|
+
autoload :Document, 'cxml/document'
|
|
13
|
+
autoload :Header, 'cxml/header'
|
|
14
|
+
autoload :Credential, 'cxml/credential'
|
|
15
|
+
autoload :CredentialMac, 'cxml/credential_mac'
|
|
16
|
+
autoload :Sender, 'cxml/sender'
|
|
17
|
+
autoload :Status, 'cxml/status'
|
|
18
|
+
autoload :Request, 'cxml/request'
|
|
19
|
+
autoload :Response, 'cxml/response'
|
|
20
|
+
autoload :Parser, 'cxml/parser'
|
|
21
|
+
|
|
22
|
+
def self.parse(str)
|
|
23
|
+
CXML::Parser.new.parse(str)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.builder
|
|
27
|
+
Nokogiri::XML::Builder.new(:encoding => "UTF-8")
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# This element contains identification and authentication values.
|
|
2
|
+
# Credential contains an Identity element and optionally a SharedSecret or a CredentialMac
|
|
3
|
+
# element. The Identity element states who the Credential represents, while the optional
|
|
4
|
+
# authentication elements verify the identity of the party
|
|
5
|
+
#
|
|
6
|
+
# Credential has the following attributes:
|
|
7
|
+
#
|
|
8
|
+
# *domain*
|
|
9
|
+
# Specifies the type of credential. This attribute allows
|
|
10
|
+
# documents to contain multiple types of credentials for multiple
|
|
11
|
+
# authentication domains.
|
|
12
|
+
# For messages sent on the Ariba Supplier Network, for
|
|
13
|
+
# instance, the domain can be AribaNetworkUserId to indicate an
|
|
14
|
+
# email address, DUNS for a D-U-N-S number, or NetworkId for a
|
|
15
|
+
# preassigned ID.
|
|
16
|
+
#
|
|
17
|
+
# *type* - optional
|
|
18
|
+
# Requests to or from a marketplace identify both the
|
|
19
|
+
# marketplace and the member company in From or To Credential
|
|
20
|
+
# elements. In this case, the credential for the marketplace uses
|
|
21
|
+
# the type attribute, which is set to the value “marketplace”
|
|
22
|
+
#
|
|
23
|
+
# *SharedSecred*
|
|
24
|
+
# The SharedSecret element is used when the Sender has a password that the requester recognizes.
|
|
25
|
+
#
|
|
26
|
+
# *CredentialMac*
|
|
27
|
+
# The CredentialMac element is used for the Message Authentication Code (MAC)
|
|
28
|
+
# authentication method. This authentication method is used in situations where the
|
|
29
|
+
# sender must prove to the receiver that it has been authenticated by shared secret by a
|
|
30
|
+
# trusted third party. For example, a direct PunchOut request can travel directly from a
|
|
31
|
+
# buyer to a supplier without going through a network commerce hub, because it
|
|
32
|
+
# contains a MAC (generated by the network commerce hub) that allows the supplier to
|
|
33
|
+
# authenticate it.
|
|
34
|
+
|
|
35
|
+
module CXML
|
|
36
|
+
class Credential
|
|
37
|
+
attr_accessor :domain
|
|
38
|
+
attr_accessor :type
|
|
39
|
+
attr_accessor :shared_secret
|
|
40
|
+
attr_accessor :credential_mac
|
|
41
|
+
attr_accessor :identity
|
|
42
|
+
|
|
43
|
+
# Initialize a new Credential instance
|
|
44
|
+
# @param data [Hash] optional initial data
|
|
45
|
+
def initialize(data={})
|
|
46
|
+
if data.kind_of?(Hash) && !data.empty?
|
|
47
|
+
@domain = data['domain']
|
|
48
|
+
@type = data['type']
|
|
49
|
+
@identity = data['Identity']
|
|
50
|
+
@shared_secret = data['SharedSecret']
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def render(node)
|
|
55
|
+
node.Credential('domain' => domain) do |c|
|
|
56
|
+
c.Identity(@identity)
|
|
57
|
+
c.SharedSecret(@shared_secret) if @shared_secret
|
|
58
|
+
end
|
|
59
|
+
node
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# The CredentialMac element is used for the Message Authentication Code (MAC)
|
|
2
|
+
# authentication method. This authentication method is used in situations where the
|
|
3
|
+
# sender must prove to the receiver that it has been authenticated by shared secret by a
|
|
4
|
+
# trusted third party. For example, a direct PunchOut request can travel directly from a
|
|
5
|
+
# buyer to a supplier without going through a network commerce hub, because it
|
|
6
|
+
# contains a MAC (generated by the network commerce hub) that allows the supplier to
|
|
7
|
+
# authenticate it.
|
|
8
|
+
# The trusted third party computes the MAC and transfers it to the sender through the
|
|
9
|
+
# Profile transaction. The MAC is opaque to the sender (it is secure and non-reversible).
|
|
10
|
+
# To see how the MAC is transmitted from the trusted third party to the sender, see
|
|
11
|
+
# "ProfileResponse" on page 63.
|
|
12
|
+
#
|
|
13
|
+
# Page 45 cXML reference
|
|
14
|
+
|
|
15
|
+
module CXML
|
|
16
|
+
class CredentialMac
|
|
17
|
+
attr_accessor :type
|
|
18
|
+
attr_accessor :algorithm
|
|
19
|
+
attr_accessor :creation_date
|
|
20
|
+
attr_accessor :expiration_date
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module CXML
|
|
2
|
+
class Document
|
|
3
|
+
attr_accessor :version
|
|
4
|
+
attr_accessor :payload_id
|
|
5
|
+
attr_accessor :timestamp
|
|
6
|
+
|
|
7
|
+
attr_accessor :header
|
|
8
|
+
attr_accessor :request
|
|
9
|
+
attr_accessor :response
|
|
10
|
+
|
|
11
|
+
def initialize(data={})
|
|
12
|
+
if data.kind_of?(Hash) && !data.empty?
|
|
13
|
+
@version = data['version']
|
|
14
|
+
@payload_id = data['payloadID']
|
|
15
|
+
|
|
16
|
+
if data['timestamp']
|
|
17
|
+
@timestamp = Time.parse(data['timestamp'])
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
if data['Header']
|
|
21
|
+
@header = CXML::Header.new(data['Header'])
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
if data['Request']
|
|
25
|
+
@request = CXML::Request.new(data['Request'])
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
if data['Response']
|
|
29
|
+
@response = CXML::Response.new(data['Response'])
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def setup
|
|
35
|
+
@version = CXML::Protocol.version
|
|
36
|
+
@timestamp = Time.now.utc
|
|
37
|
+
@payload_id = "#{@timestamp.to_i}.process.#{Process.pid}@domain.com"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Check if document is request
|
|
41
|
+
# @return [Boolean]
|
|
42
|
+
def request?
|
|
43
|
+
!request.nil?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Check if document is a response
|
|
47
|
+
# @return [Boolean]
|
|
48
|
+
def response?
|
|
49
|
+
!response.nil?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def render
|
|
53
|
+
node = CXML.builder
|
|
54
|
+
node.cXML('version' => version, 'payloadID' => payload_id, 'timestamp' => timestamp.iso8601) do |doc|
|
|
55
|
+
doc.Header { |n| @header.render(n) } if @header
|
|
56
|
+
@request.render(node) if @request
|
|
57
|
+
@response.render(node) if @response
|
|
58
|
+
end
|
|
59
|
+
node
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Builder for ConfirmationRequest object
|
|
2
|
+
class ConfirmationRequest < RequestDoc
|
|
3
|
+
@@defaults = {
|
|
4
|
+
type: 'accept',
|
|
5
|
+
payload_id: nil,
|
|
6
|
+
confirm_id: nil,
|
|
7
|
+
invoice_id: nil
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
def features(node)
|
|
11
|
+
node.ConfirmationRequest({}.merge(@opts[:confirm_id] ? { confirmID: @opts[:confirm_id] } : {}).merge(@opts[:invoice_id] ? { invoice_id: @opts[:invoice_id] } : {})) {
|
|
12
|
+
node.ConfirmationHeader(type: @opts[:type], noticeDate: Time.now.iso8601)
|
|
13
|
+
node.OrderReference {
|
|
14
|
+
node.DocumentReference(@opts[:payload_id] ? { payloadID: @opts[:payload_id] } : {})
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Default parent for all Request builders
|
|
2
|
+
class RequestDoc
|
|
3
|
+
attr_accessor :opts
|
|
4
|
+
class_attribute :defaults
|
|
5
|
+
|
|
6
|
+
def initialize(opts={})
|
|
7
|
+
@opts = opts.with_indifferent_access.reverse_merge(self.class.class_variable_defined?("@@defaults") ? self.class.class_variable_get("@@defaults") : {})
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def [](key)
|
|
11
|
+
@opts[key]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def []=(key,val)
|
|
15
|
+
@opts[key] = val
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def render
|
|
19
|
+
doc = CXML::Document.new('Request' => { 'builder' => proc { |node|
|
|
20
|
+
features(node)
|
|
21
|
+
}})
|
|
22
|
+
|
|
23
|
+
doc.setup
|
|
24
|
+
doc.render.to_xml
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def features
|
|
28
|
+
raise NotImplementedError, "Missing features function for #{self.class.name}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def send(endpoint)
|
|
32
|
+
RestClient.post endpoint, self.render, content_type: :xml
|
|
33
|
+
|
|
34
|
+
# TODO: Abstract and verify response
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Builder for ShipNoticeRequest object
|
|
2
|
+
class ShipNoticeRequest < RequestDoc
|
|
3
|
+
@@defaults = {
|
|
4
|
+
shipment_id: nil
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
def features(node)
|
|
8
|
+
node.ShipNoticeRequest {
|
|
9
|
+
node.ShipNoticeHeader({noticeDate: Time.now.iso8601, shipmentDate: Time.now.iso8601}.merge(@opts[:shipment_id] ? { shipmentID: @opts[:shipment_id]} : {}))
|
|
10
|
+
}
|
|
11
|
+
end
|
|
12
|
+
end
|
data/lib/cxml/errors.rb
ADDED
data/lib/cxml/header.rb
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# The Header element contains addressing and authentication information. The Header
|
|
2
|
+
# element is the same regardless of the specific Request or Response within the body of
|
|
3
|
+
# the cXML message. Applications need the requestor's identity, but not validation that
|
|
4
|
+
# the information provided for identity is correct.
|
|
5
|
+
#
|
|
6
|
+
# The From and To elements are synonymous with From and To in SMTP mail
|
|
7
|
+
# messages; they are the logical source and destination of the messages. Sender is the
|
|
8
|
+
# party that opens the HTTP connection and sends the cXML document.
|
|
9
|
+
# Sender contains the Credential element, which allows the receiving party to authenticate
|
|
10
|
+
# the sending party. This credential allows strong authentication without requiring a
|
|
11
|
+
# public-key end-to-end digital certificate infrastructure. Only a user name and
|
|
12
|
+
# password need to be issued by the receiving party to allow the sending party to
|
|
13
|
+
# perform Requests.
|
|
14
|
+
#
|
|
15
|
+
# When the document is initially sent, Sender and From are the same, However, if the
|
|
16
|
+
# cXML document travels through e-commerce network hubs, the Sender element
|
|
17
|
+
# changes to indicate current sending party.
|
|
18
|
+
|
|
19
|
+
module CXML
|
|
20
|
+
class Header
|
|
21
|
+
attr_accessor :from
|
|
22
|
+
attr_accessor :to
|
|
23
|
+
attr_accessor :sender
|
|
24
|
+
|
|
25
|
+
def initialize(data={})
|
|
26
|
+
if data.kind_of?(Hash) && !data.empty?
|
|
27
|
+
@from = CXML::Credential.new(data['From']['Credential'])
|
|
28
|
+
@to = CXML::Credential.new(data['To']['Credential'])
|
|
29
|
+
@sender = CXML::Sender.new(data['Sender'])
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def render(node)
|
|
34
|
+
node.From { |n| @from.render(n) }
|
|
35
|
+
node.To { |n| @to.render(n) }
|
|
36
|
+
|
|
37
|
+
@sender.render(node)
|
|
38
|
+
node
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/lib/cxml/money.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# The Money element has three possible attributes: currency, alternateAmount,
|
|
2
|
+
# alternateCurrency. The attributes currency and alternateCurrecy must be a three-letter ISO
|
|
3
|
+
# 4217 currency code. The content of the Money element and of the aternateAmount
|
|
4
|
+
# attribute should be a numeric value. For example:
|
|
5
|
+
# <Money currency="USD">12.34</Money>
|
|
6
|
+
# The optional alternateCurrency and alternateAmount attributes are used together to specify
|
|
7
|
+
# an amount in an alternate currency. These can be used to support dual-currency
|
|
8
|
+
# requirements such as the euro. For example:
|
|
9
|
+
# <Money currency="USD" alternateCurrency=”EUR” alternateAmount=”14.28”>12.34
|
|
10
|
+
# </Money>
|
|
11
|
+
# Note: You can optionally use commas as thousands separators. Do not use
|
|
12
|
+
# commas as decimal separators.
|
|
13
|
+
#
|
|
14
|
+
# Page: 59
|
|
15
|
+
|
|
16
|
+
module CXML
|
|
17
|
+
class Money
|
|
18
|
+
attr_accessor :currency
|
|
19
|
+
attr_accessor :amount
|
|
20
|
+
attr_accessor :alternative_currency
|
|
21
|
+
attr_accessor :alternative_amount
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/cxml/parser.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module CXML
|
|
2
|
+
module Protocol
|
|
3
|
+
VERSION = '1.2.011'
|
|
4
|
+
|
|
5
|
+
REQUEST_ELEMENTS = [
|
|
6
|
+
'OrderRequest',
|
|
7
|
+
'ProfileRequest',
|
|
8
|
+
'PunchOutSetupRequest',
|
|
9
|
+
'StatusUpdateRequest',
|
|
10
|
+
'GetPendingRequest',
|
|
11
|
+
'ConfirmationRequest',
|
|
12
|
+
'ShipNoticeRequest',
|
|
13
|
+
'ProviderSetupRequest',
|
|
14
|
+
'PaymentRemittanceRequest',
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
RESPONSE_ELEMENTS = [
|
|
18
|
+
'ProfileResponse',
|
|
19
|
+
'PunchOutSetupResponse',
|
|
20
|
+
'GetPendingResponse',
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
STATUS_CODES = [
|
|
24
|
+
200, 201, 204, 280, 281,
|
|
25
|
+
400, 401, 402, 403, 406, 409, 412, 417, 450, 475, 476, 477,
|
|
26
|
+
500, 550, 551, 560
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
class << self
|
|
30
|
+
# Get current protocol version
|
|
31
|
+
# @return [String]
|
|
32
|
+
def version
|
|
33
|
+
VERSION
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Get available request elements
|
|
37
|
+
# @return [Array<String>]
|
|
38
|
+
def request_elements
|
|
39
|
+
REQUEST_ELEMENTS
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Get available response elements
|
|
43
|
+
# @return [Array<String>]
|
|
44
|
+
def response_elements
|
|
45
|
+
RESPONSE_ELEMENTS
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Get available status codes
|
|
49
|
+
# @return [Array<Fixnum>]
|
|
50
|
+
def status_codes
|
|
51
|
+
STATUS_CODES
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
data/lib/cxml/request.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Clients send requests for operations. Only one Request element is allowed for each
|
|
2
|
+
# cXML envelope element, which simplifies the server implementations, because no
|
|
3
|
+
# demultiplexing needs to occur when reading cXML documents. The Request element
|
|
4
|
+
# can contain virtually any type of XML data.
|
|
5
|
+
|
|
6
|
+
module CXML
|
|
7
|
+
class Request
|
|
8
|
+
attr_accessor :id
|
|
9
|
+
attr_accessor :deployment_mode
|
|
10
|
+
attr_accessor :builder
|
|
11
|
+
|
|
12
|
+
def initialize(data={})
|
|
13
|
+
if data.kind_of?(Hash) && !data.empty?
|
|
14
|
+
@id = data['id']
|
|
15
|
+
@deployment_mode = data['deploymentMode']
|
|
16
|
+
@builder = data['builder']
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def render(node)
|
|
21
|
+
@builder.yield(node) if @builder
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Servers send responses to inform clients of the results of operations. Because the
|
|
2
|
+
# result of some requests might not have any data, the Response element can optionally
|
|
3
|
+
# contain nothing but a Status element. A Response element can also contain any
|
|
4
|
+
# application-level data. During PunchOut for example, the application-level data is
|
|
5
|
+
# contained in a PunchOutSetupResponse element.
|
|
6
|
+
|
|
7
|
+
module CXML
|
|
8
|
+
class Response
|
|
9
|
+
attr_accessor :id
|
|
10
|
+
attr_accessor :status
|
|
11
|
+
|
|
12
|
+
def initialize(data={})
|
|
13
|
+
if data.kind_of?(Hash) && !data.empty?
|
|
14
|
+
@status = CXML::Status.new(data['Status'])
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def render(node)
|
|
19
|
+
options = {:id => @id}
|
|
20
|
+
options.keep_if { |k,v| !v.nil? }
|
|
21
|
+
node.Response(options) { |n| @status.render(n) }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/cxml/sender.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module CXML
|
|
2
|
+
class Sender
|
|
3
|
+
attr_accessor :credential
|
|
4
|
+
attr_accessor :user_agent
|
|
5
|
+
|
|
6
|
+
def initialize(data={})
|
|
7
|
+
if data.kind_of?(Hash) && !data.empty?
|
|
8
|
+
@credential = CXML::Credential.new(data['Credential'])
|
|
9
|
+
@user_agent = data['UserAgent']
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def render(node)
|
|
14
|
+
node.Sender do |n|
|
|
15
|
+
n.UserAgent(@user_agent)
|
|
16
|
+
@credential.render(n)
|
|
17
|
+
end
|
|
18
|
+
node
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/cxml/status.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module CXML
|
|
2
|
+
class Status
|
|
3
|
+
attr_accessor :code
|
|
4
|
+
attr_accessor :text
|
|
5
|
+
attr_accessor :xml_lang
|
|
6
|
+
|
|
7
|
+
# Initialize a new Status instance
|
|
8
|
+
# @params data [Hash] optional hash with attributes
|
|
9
|
+
def initialize(data={})
|
|
10
|
+
data = CXML.parse(data) if data.kind_of?(String)
|
|
11
|
+
|
|
12
|
+
if data.kind_of?(Hash) && !data.empty?
|
|
13
|
+
@code = data['code'].to_i
|
|
14
|
+
@text = data['text']
|
|
15
|
+
@xml_lang = data['xml:lang']
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Check if status is success
|
|
20
|
+
# @return [Boolean]
|
|
21
|
+
def success?
|
|
22
|
+
[200, 201, 204, 280, 281].include?(code)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Check if status is failure
|
|
26
|
+
# @return [Boolean]
|
|
27
|
+
def failure?
|
|
28
|
+
!success?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def render(node)
|
|
32
|
+
node.Status(:code => @code, :text => @text)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/cxml/version.rb
ADDED
data/spec/cxml_spec.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe CXML::Document do
|
|
4
|
+
let(:parser) { CXML::Parser.new }
|
|
5
|
+
|
|
6
|
+
it { should respond_to :version }
|
|
7
|
+
it { should respond_to :payload_id }
|
|
8
|
+
it { should respond_to :timestamp }
|
|
9
|
+
|
|
10
|
+
describe '#parse' do
|
|
11
|
+
it 'sets document attributes' do
|
|
12
|
+
data = parser.parse(fixture('envelope3.xml'))
|
|
13
|
+
doc = nil
|
|
14
|
+
|
|
15
|
+
expect { doc = CXML::Document.new(data) }.not_to raise_error
|
|
16
|
+
|
|
17
|
+
doc.version.should eq(CXML::Protocol::VERSION)
|
|
18
|
+
doc.payload_id.should_not be_nil
|
|
19
|
+
doc.timestamp.should be_a Time
|
|
20
|
+
doc.timestamp.should eq(Time.parse('2012-09-04T02:37:49-05:00'))
|
|
21
|
+
|
|
22
|
+
doc.header.should be_a CXML::Header
|
|
23
|
+
doc.request.should be_a CXML::Request
|
|
24
|
+
doc.response.should be_nil
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe '#render' do
|
|
29
|
+
it 'returns and xml result' do
|
|
30
|
+
doc = CXML::Document.new(parser.parse(fixture('envelope3.xml')))
|
|
31
|
+
expect { doc.render }.not_to raise_error
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE cXML SYSTEM "http://xml.cxml.org/schemas/cXML/1.2.014/cXML.dtd">
|
|
3
|
+
<cXML timestamp="2000-12-28T16:56:03-08:00" payloadID="12345666@10.10.83.39">
|
|
4
|
+
<Header>
|
|
5
|
+
<From>
|
|
6
|
+
<Credential domain="DUNS">
|
|
7
|
+
<Identity>123456789</Identity>
|
|
8
|
+
</Credential>
|
|
9
|
+
</From>
|
|
10
|
+
<To>
|
|
11
|
+
<Credential domain="NetworkID">
|
|
12
|
+
<Identity>AN01000000001</Identity>
|
|
13
|
+
</Credential>
|
|
14
|
+
</To>
|
|
15
|
+
<Sender>
|
|
16
|
+
<Credential domain="DUNS">
|
|
17
|
+
<Identity>123456789</Identity>
|
|
18
|
+
<SharedSecret>abracadabra</SharedSecret>
|
|
19
|
+
</Credential>
|
|
20
|
+
</Sender>
|
|
21
|
+
</Header>
|
|
22
|
+
<Request>
|
|
23
|
+
<CatalogUploadRequest operation="new">
|
|
24
|
+
<CatalogName xml:lang="en">Winter Prices</CatalogName>
|
|
25
|
+
<Description xml:lang="en">premiere-level prices</Description>
|
|
26
|
+
<Attachment>
|
|
27
|
+
<URL>cid:part2.PCO28.975@saturn.workchairs.com</URL>
|
|
28
|
+
</Attachment>
|
|
29
|
+
</CatalogUploadRequest>
|
|
30
|
+
</Request>
|
|
31
|
+
</cXML>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE cXML SYSTEM "http://xml.cxml.org/schemas/cXML/1.2.014/cXML.dtd">
|
|
3
|
+
<cXML payloadID="123@sendercompany.com"
|
|
4
|
+
timestamp="2003-11-20T23:59:45-07:00">
|
|
5
|
+
<Header>
|
|
6
|
+
<From>
|
|
7
|
+
<!-- Sender -->
|
|
8
|
+
<Credential domain="AribaNetworkUserId">
|
|
9
|
+
<Identity>sender@sendercompany.com</Identity>
|
|
10
|
+
</Credential>
|
|
11
|
+
</From>
|
|
12
|
+
<To>
|
|
13
|
+
<!-- Recipient -->
|
|
14
|
+
<Credential domain="AribaNetworkUserId">
|
|
15
|
+
<Identity>recipient@recipientcompany.com</Identity>
|
|
16
|
+
</Credential>
|
|
17
|
+
</To>
|
|
18
|
+
<Sender>
|
|
19
|
+
<!-- Sender -->
|
|
20
|
+
<Credential domain="AribaNetworkUserId">
|
|
21
|
+
<Identity>sender@sendercompany.com</Identity>
|
|
22
|
+
<SharedSecret>abracadabra</SharedSecret>
|
|
23
|
+
</Credential>
|
|
24
|
+
<UserAgent>Sender Application 1.0</UserAgent>
|
|
25
|
+
</Sender>
|
|
26
|
+
</Header>
|
|
27
|
+
<Request deploymentMode="production">
|
|
28
|
+
<CopyRequest>
|
|
29
|
+
<cXMLAttachment>
|
|
30
|
+
<Attachment>
|
|
31
|
+
<URL>cid:222@sendercompany.com</URL>
|
|
32
|
+
</Attachment>
|
|
33
|
+
</cXMLAttachment>
|
|
34
|
+
</CopyRequest>
|
|
35
|
+
</Request>
|
|
36
|
+
</cXML>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<?xml version="1.0"?>
|
|
2
|
+
<cXML version="1.2.011" payloadID="1346769469000.process.162998590@officedepot.com" timestamp="2012-09-04T02:37:49-05:00">
|
|
3
|
+
<Header>
|
|
4
|
+
<From>
|
|
5
|
+
<Credential domain="NetworkID">
|
|
6
|
+
<Identity>AN01000000147</Identity>
|
|
7
|
+
</Credential>
|
|
8
|
+
</From>
|
|
9
|
+
<To>
|
|
10
|
+
<Credential domain="NetworkId">
|
|
11
|
+
<Identity>customerID</Identity>
|
|
12
|
+
</Credential>
|
|
13
|
+
</To>
|
|
14
|
+
<Sender>
|
|
15
|
+
<Credential domain="NetworkID">
|
|
16
|
+
<Identity>AN01000000147</Identity>
|
|
17
|
+
<SharedSecret>welcome</SharedSecret>
|
|
18
|
+
</Credential>
|
|
19
|
+
<UserAgent>BSD 8.12</UserAgent>
|
|
20
|
+
</Sender>
|
|
21
|
+
</Header>
|
|
22
|
+
<Request deploymentMode="production">
|
|
23
|
+
<ConfirmationRequest>
|
|
24
|
+
<ConfirmationHeader confirmID="" operation="new" type="detail" noticeDate="2012-09-04T02:37:49-05:00" invoiceID="623612976001">
|
|
25
|
+
<Contact role="shipTo" addressID="0170-74629-00">
|
|
26
|
+
<Name xml:lang="en-US">HOSPITAL BUILDING</Name>
|
|
27
|
+
<PostalAddress>
|
|
28
|
+
<DeliverTo>Yvonne Smith</DeliverTo>
|
|
29
|
+
<DeliverTo>541813031</DeliverTo>
|
|
30
|
+
<Street>3601 Mile Road</Street>
|
|
31
|
+
<Street>BUILDING: 0170-74629-00</Street>
|
|
32
|
+
<City>Royal Oak</City>
|
|
33
|
+
<State>MI</State>
|
|
34
|
+
<PostalCode>48073</PostalCode>
|
|
35
|
+
<Country isoCountryCode="US">United States</Country>
|
|
36
|
+
</PostalAddress>
|
|
37
|
+
</Contact>
|
|
38
|
+
</ConfirmationHeader>
|
|
39
|
+
<OrderReference orderID="400-2591453-0">
|
|
40
|
+
<DocumentReference payloadID="20120904023747.cXMLPurchaseOrder.984556224@eprosvcs.com"/>
|
|
41
|
+
</OrderReference>
|
|
42
|
+
<ConfirmationItem quantity="20" lineNumber="1">
|
|
43
|
+
<UnitOfMeasure>RM</UnitOfMeasure>
|
|
44
|
+
<ConfirmationStatus quantity="20" type="accept" shipmentDate="2012-09-05T12:00:00-05:00" deliveryDate="2012-09-05T12:00:00-05:00">
|
|
45
|
+
<UnitOfMeasure>RM</UnitOfMeasure>
|
|
46
|
+
<UnitPrice>
|
|
47
|
+
<Money currency="USD">4.44</Money>
|
|
48
|
+
</UnitPrice>
|
|
49
|
+
<Comments></Comments>
|
|
50
|
+
<Extrinsic name="SupplierPartID">751441</Extrinsic>
|
|
51
|
+
</ConfirmationStatus>
|
|
52
|
+
</ConfirmationItem>
|
|
53
|
+
<ConfirmationItem quantity="12" lineNumber="2">
|
|
54
|
+
<UnitOfMeasure>BX</UnitOfMeasure>
|
|
55
|
+
<ConfirmationStatus quantity="12" type="accept" shipmentDate="2012-09-05T12:00:00-05:00" deliveryDate="2012-09-05T12:00:00-05:00">
|
|
56
|
+
<UnitOfMeasure>BX</UnitOfMeasure>
|
|
57
|
+
<UnitPrice>
|
|
58
|
+
<Money currency="USD">1.52</Money>
|
|
59
|
+
</UnitPrice>
|
|
60
|
+
<Comments></Comments>
|
|
61
|
+
<Extrinsic name="SupplierPartID">429175</Extrinsic>
|
|
62
|
+
</ConfirmationStatus>
|
|
63
|
+
</ConfirmationItem>
|
|
64
|
+
<ConfirmationItem quantity="2" lineNumber="3">
|
|
65
|
+
<UnitOfMeasure>EA</UnitOfMeasure>
|
|
66
|
+
<ConfirmationStatus quantity="2" type="accept" shipmentDate="2012-09-05T12:00:00-05:00" deliveryDate="2012-09-05T12:00:00-05:00">
|
|
67
|
+
<UnitOfMeasure>EA</UnitOfMeasure>
|
|
68
|
+
<UnitPrice>
|
|
69
|
+
<Money currency="USD">4.14</Money>
|
|
70
|
+
</UnitPrice>
|
|
71
|
+
<Comments></Comments>
|
|
72
|
+
<Extrinsic name="SupplierPartID">908194</Extrinsic>
|
|
73
|
+
</ConfirmationStatus>
|
|
74
|
+
</ConfirmationItem>
|
|
75
|
+
</ConfirmationRequest>
|
|
76
|
+
</Request>
|
|
77
|
+
</cXML>
|
data/spec/parser_spec.rb
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe CXML::Protocol do
|
|
4
|
+
it { should respond_to :version }
|
|
5
|
+
it { should respond_to :request_elements }
|
|
6
|
+
it { should respond_to :response_elements }
|
|
7
|
+
it { should respond_to :status_codes }
|
|
8
|
+
|
|
9
|
+
context '#version' do
|
|
10
|
+
it 'returns current protocol version' do
|
|
11
|
+
subject.version.should eq('1.2.011')
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
$:.unshift File.expand_path("../..", __FILE__)
|
|
2
|
+
|
|
3
|
+
require 'cxml'
|
|
4
|
+
|
|
5
|
+
RSpec.configure do |conf|
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def fixture_path(filename=nil)
|
|
9
|
+
path = File.expand_path("../fixtures", __FILE__)
|
|
10
|
+
filename.nil? ? path : File.join(path, filename)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def fixture(file)
|
|
14
|
+
File.read(File.join(fixture_path, file))
|
|
15
|
+
end
|
data/spec/status_spec.rb
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe CXML::Status do
|
|
4
|
+
describe 'instance' do
|
|
5
|
+
it { should respond_to :code }
|
|
6
|
+
it { should respond_to :text }
|
|
7
|
+
it { should respond_to :xml_lang }
|
|
8
|
+
it { should respond_to :success? }
|
|
9
|
+
it { should respond_to :failure? }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe '#initialize' do
|
|
13
|
+
it 'assigns attributes from string' do
|
|
14
|
+
str = '<Status xml:lang="en-US" code="200" text="OK"></Status>'
|
|
15
|
+
status = CXML::Status.new(str)
|
|
16
|
+
|
|
17
|
+
status.code.should eq(200)
|
|
18
|
+
status.xml_lang.should eq('en-US')
|
|
19
|
+
status.text.should eq('OK')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'assigns attributes from hash' do
|
|
23
|
+
hash = {'xml:lang' => 'en-US', 'code' => "200", 'text' => 'OK'}
|
|
24
|
+
status = CXML::Status.new(hash)
|
|
25
|
+
|
|
26
|
+
status.code.should eq(200)
|
|
27
|
+
status.xml_lang.should eq('en-US')
|
|
28
|
+
status.text.should eq('OK')
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe '#success?' do
|
|
33
|
+
it 'returns true on 2xx codes' do
|
|
34
|
+
CXML::Status.new('code' => '200').success?.should be_true
|
|
35
|
+
CXML::Status.new('code' => '201').success?.should be_true
|
|
36
|
+
CXML::Status.new('code' => '281').success?.should be_true
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'returns false on non 2xx codes' do
|
|
40
|
+
CXML::Status.new('code' => '400').success?.should be_false
|
|
41
|
+
CXML::Status.new('code' => '475').success?.should be_false
|
|
42
|
+
CXML::Status.new('code' => '500').success?.should be_false
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe '#failure?' do
|
|
47
|
+
it 'returns false on 2xx codes' do
|
|
48
|
+
CXML::Status.new('code' => '200').failure?.should be_false
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: cxml
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Dan Sosedoff
|
|
9
|
+
- Geoff Hayes
|
|
10
|
+
autorequire:
|
|
11
|
+
bindir: bin
|
|
12
|
+
cert_chain: []
|
|
13
|
+
date: 2013-07-06 00:00:00.000000000 Z
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: rake
|
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
|
18
|
+
none: false
|
|
19
|
+
requirements:
|
|
20
|
+
- - ! '>='
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '0'
|
|
23
|
+
type: :development
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
none: false
|
|
27
|
+
requirements:
|
|
28
|
+
- - ! '>='
|
|
29
|
+
- !ruby/object:Gem::Version
|
|
30
|
+
version: '0'
|
|
31
|
+
- !ruby/object:Gem::Dependency
|
|
32
|
+
name: rspec
|
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
|
34
|
+
none: false
|
|
35
|
+
requirements:
|
|
36
|
+
- - ~>
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '2.11'
|
|
39
|
+
type: :development
|
|
40
|
+
prerelease: false
|
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
42
|
+
none: false
|
|
43
|
+
requirements:
|
|
44
|
+
- - ~>
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '2.11'
|
|
47
|
+
- !ruby/object:Gem::Dependency
|
|
48
|
+
name: simplecov
|
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
|
50
|
+
none: false
|
|
51
|
+
requirements:
|
|
52
|
+
- - ~>
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0.4'
|
|
55
|
+
type: :development
|
|
56
|
+
prerelease: false
|
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
58
|
+
none: false
|
|
59
|
+
requirements:
|
|
60
|
+
- - ~>
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '0.4'
|
|
63
|
+
- !ruby/object:Gem::Dependency
|
|
64
|
+
name: nokogiri
|
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
|
66
|
+
none: false
|
|
67
|
+
requirements:
|
|
68
|
+
- - ! '>='
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '0'
|
|
71
|
+
type: :runtime
|
|
72
|
+
prerelease: false
|
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
74
|
+
none: false
|
|
75
|
+
requirements:
|
|
76
|
+
- - ! '>='
|
|
77
|
+
- !ruby/object:Gem::Version
|
|
78
|
+
version: '0'
|
|
79
|
+
- !ruby/object:Gem::Dependency
|
|
80
|
+
name: xml-simple
|
|
81
|
+
requirement: !ruby/object:Gem::Requirement
|
|
82
|
+
none: false
|
|
83
|
+
requirements:
|
|
84
|
+
- - ! '>='
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: '0'
|
|
87
|
+
type: :runtime
|
|
88
|
+
prerelease: false
|
|
89
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
90
|
+
none: false
|
|
91
|
+
requirements:
|
|
92
|
+
- - ! '>='
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '0'
|
|
95
|
+
- !ruby/object:Gem::Dependency
|
|
96
|
+
name: hashr
|
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
|
98
|
+
none: false
|
|
99
|
+
requirements:
|
|
100
|
+
- - ! '>='
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '0'
|
|
103
|
+
type: :runtime
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
none: false
|
|
107
|
+
requirements:
|
|
108
|
+
- - ! '>='
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
description: Ruby library to work with cXML protocol
|
|
112
|
+
email:
|
|
113
|
+
- dan.sosedoff@gmail.com
|
|
114
|
+
- hayesgm@gmail.com
|
|
115
|
+
executables: []
|
|
116
|
+
extensions: []
|
|
117
|
+
extra_rdoc_files: []
|
|
118
|
+
files:
|
|
119
|
+
- .gitignore
|
|
120
|
+
- .rspec
|
|
121
|
+
- .travis.yml
|
|
122
|
+
- Gemfile
|
|
123
|
+
- LICENSE
|
|
124
|
+
- README.md
|
|
125
|
+
- Rakefile
|
|
126
|
+
- cxml.gemspec
|
|
127
|
+
- lib/commerce.rb
|
|
128
|
+
- lib/cxml.rb
|
|
129
|
+
- lib/cxml/credential.rb
|
|
130
|
+
- lib/cxml/credential_mac.rb
|
|
131
|
+
- lib/cxml/document.rb
|
|
132
|
+
- lib/cxml/documents/confirmation_request.rb
|
|
133
|
+
- lib/cxml/documents/request_doc.rb
|
|
134
|
+
- lib/cxml/documents/ship_notice_request.rb
|
|
135
|
+
- lib/cxml/errors.rb
|
|
136
|
+
- lib/cxml/header.rb
|
|
137
|
+
- lib/cxml/money.rb
|
|
138
|
+
- lib/cxml/parser.rb
|
|
139
|
+
- lib/cxml/protocol.rb
|
|
140
|
+
- lib/cxml/request.rb
|
|
141
|
+
- lib/cxml/response.rb
|
|
142
|
+
- lib/cxml/sender.rb
|
|
143
|
+
- lib/cxml/status.rb
|
|
144
|
+
- lib/cxml/version.rb
|
|
145
|
+
- spec/cxml_spec.rb
|
|
146
|
+
- spec/document_spec.rb
|
|
147
|
+
- spec/fixtures/.gitkeep
|
|
148
|
+
- spec/fixtures/envelope.xml
|
|
149
|
+
- spec/fixtures/envelope2.xml
|
|
150
|
+
- spec/fixtures/envelope3.xml
|
|
151
|
+
- spec/fixtures/response_status_400.xml
|
|
152
|
+
- spec/parser_spec.rb
|
|
153
|
+
- spec/protocol_spec.rb
|
|
154
|
+
- spec/spec_helper.rb
|
|
155
|
+
- spec/status_spec.rb
|
|
156
|
+
homepage: http://github.com/sosedoff/cxml
|
|
157
|
+
licenses: []
|
|
158
|
+
post_install_message:
|
|
159
|
+
rdoc_options: []
|
|
160
|
+
require_paths:
|
|
161
|
+
- lib
|
|
162
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
163
|
+
none: false
|
|
164
|
+
requirements:
|
|
165
|
+
- - ! '>='
|
|
166
|
+
- !ruby/object:Gem::Version
|
|
167
|
+
version: '0'
|
|
168
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
169
|
+
none: false
|
|
170
|
+
requirements:
|
|
171
|
+
- - ! '>='
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: '0'
|
|
174
|
+
requirements: []
|
|
175
|
+
rubyforge_project:
|
|
176
|
+
rubygems_version: 1.8.25
|
|
177
|
+
signing_key:
|
|
178
|
+
specification_version: 3
|
|
179
|
+
summary: Ruby library to work with cXML protocol
|
|
180
|
+
test_files:
|
|
181
|
+
- spec/cxml_spec.rb
|
|
182
|
+
- spec/document_spec.rb
|
|
183
|
+
- spec/fixtures/.gitkeep
|
|
184
|
+
- spec/fixtures/envelope.xml
|
|
185
|
+
- spec/fixtures/envelope2.xml
|
|
186
|
+
- spec/fixtures/envelope3.xml
|
|
187
|
+
- spec/fixtures/response_status_400.xml
|
|
188
|
+
- spec/parser_spec.rb
|
|
189
|
+
- spec/protocol_spec.rb
|
|
190
|
+
- spec/spec_helper.rb
|
|
191
|
+
- spec/status_spec.rb
|