ebay-trader 0.9.5

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.
@@ -0,0 +1,151 @@
1
+ #require 'active_support/core_ext/hash/indifferent_access'
2
+ require 'active_support/core_ext/string'
3
+ require 'hash_with_indifferent_access'
4
+
5
+ module EbayTrader
6
+ class SaxHandler
7
+
8
+ attr_accessor :stack
9
+ attr_accessor :path
10
+ attr_reader :skip_type_casting
11
+ attr_reader :known_arrays
12
+
13
+ def initialize(args = {})
14
+ @stack = []
15
+ @stack.push(HashWithIndifferentAccess.new)
16
+ @path = []
17
+ @hash = nil
18
+ @attributes = {}
19
+
20
+ @skip_type_casting = args[:skip_type_casting] || []
21
+ @skip_type_casting = [@skip_type_casting] unless @skip_type_casting.is_a?(Array)
22
+ @skip_type_casting.map! { |key| format(key.to_s) }
23
+
24
+ @known_arrays = args[:known_arrays] || []
25
+ @known_arrays = [@known_arrays] unless @known_arrays.is_a?(Array)
26
+ @known_arrays.map! { |key| format(key.to_s) }
27
+ end
28
+
29
+ def to_hash
30
+ stack[0]
31
+ end
32
+
33
+ def start_element(name)
34
+ @attributes.clear
35
+ name = name.to_s
36
+ path.push(name)
37
+
38
+ hash = HashWithIndifferentAccess.new
39
+ append(format_key(name), hash)
40
+ stack.push(hash)
41
+ end
42
+
43
+ def end_element(_)
44
+ @stack.pop
45
+ @path.pop
46
+ end
47
+
48
+ def text(value)
49
+ key = format_key(path.last)
50
+ auto_cast = !(skip_type_casting.include?(path.last) || skip_type_casting.include?(key.to_s))
51
+ parent = @stack[-2]
52
+
53
+ # If 'CurrencyID' is a defined attribute we are dealing with money type
54
+ if @attributes.key?('CurrencyID')
55
+ currency = @attributes.delete('CurrencyID')
56
+ value = BigDecimal.new(value)
57
+ if EbayTrader.configuration.price_type == :money && EbayTrader.is_money_gem_installed?
58
+ value = Money.new((value * 100).round.to_i, currency)
59
+ else
60
+ @attributes['Currency'] = currency
61
+ value = value.to_f if EbayTrader.configuration.price_type == :float
62
+ value = (value * 100).round.to_i if EbayTrader.configuration.price_type == :fixnum
63
+ end
64
+ end
65
+
66
+ if auto_cast && value.is_a?(String)
67
+ case
68
+ when value.downcase == 'false' then value = false
69
+ when value.downcase == 'true' then value = true
70
+ when value.match(/^[0-9]+$/) then value = value.to_i
71
+ when value.match(/^[0-9]+[.][0-9]+$/) then value = value.to_f
72
+ when value.match(/^[0-9]{4}-[0-9]{2}-[0-9]{2}[T ][0-9]{2}:[0-9]{2}:[0-9]{2}(.[0-9a-z]+)?$/i)
73
+ value = Time.parse(value)
74
+ end
75
+ end
76
+
77
+ if parent[key].is_a?(Array)
78
+ parent[key].pop if parent[key].last.is_a?(Hash) && parent[key].last.empty?
79
+ parent[key] << value
80
+ else
81
+ parent[key] = value
82
+ end
83
+
84
+ unless @attributes.empty?
85
+ @attributes.each_pair do |attr_key, attr_value|
86
+ attr_key_element_name = format_key("#{path.last}#{attr_key}")
87
+ parent[attr_key_element_name] = attr_value
88
+ end
89
+ end
90
+ end
91
+
92
+ def cdata(value)
93
+ key = format_key(path.last)
94
+ parent = @stack[-2]
95
+ parent[key] = value
96
+ end
97
+
98
+ def attr(name, value)
99
+ return if name.to_s.downcase == 'xmlns'
100
+ last = path.last
101
+ return if last.nil?
102
+
103
+ name = name[0].upcase + name[1...name.length]
104
+ @attributes[name] = value
105
+ end
106
+
107
+ def error(message, line, column)
108
+ raise Exception.new("#{message} at #{line}:#{column}")
109
+ end
110
+
111
+ def append(key, value)
112
+ key = key.to_s
113
+ h = @stack.last
114
+ if h.key?(key)
115
+ v = h[key]
116
+ if v.is_a?(Array)
117
+ v << value
118
+ else
119
+ h[key] = [v, value]
120
+ end
121
+ else
122
+ if known_arrays.include?(key)
123
+ h[key] = [value]
124
+ else
125
+ h[key] = value
126
+ end
127
+ end
128
+ end
129
+
130
+ #-------------------------------------------------------------------------
131
+ private
132
+
133
+ # Ensure the key is an underscored Symbol.
134
+ #
135
+ # Examples:
136
+ #
137
+ # 'ApplyBuyerProtection' -> :apply_buyer_protection
138
+ # 'PayPalEmailAddress' -> :paypal_email_address
139
+ # 'SoldOffeBay' -> :sold_off_ebay
140
+ #
141
+ # @return [Symbol] the reformatted key.
142
+ #
143
+ def format_key(key)
144
+ key = key.to_s
145
+ key = key.gsub('PayPal', 'Paypal')
146
+ key = key.gsub('eBay', 'Ebay')
147
+ key = key.gsub('EBay', 'Ebay')
148
+ key.underscore.to_sym
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,71 @@
1
+ require 'cgi'
2
+ require 'ebay_trader/request'
3
+
4
+ module EbayTrader
5
+
6
+ # Request a session ID from the eBay API.
7
+ #
8
+ # @see http://developer.ebay.com/DevZone/XML/docs/Reference/eBay/GetSessionID.html
9
+ # @see http://developer.ebay.com/DevZone/XML/docs/HowTo/Tokens/GettingTokens.html
10
+ # @see http://developer.ebay.com/DevZone/guides/ebayfeatures/Basics/Tokens-MultipleUsers.html
11
+ #
12
+ class SessionID < Request
13
+
14
+ CALL_NAME = 'GetSessionID'
15
+
16
+ # The application RuName defined in {Configuration#ru_name}, unless over-ridden in {#initialize} args.
17
+ # @return [String] the RuName for this call.
18
+ # @see https://developer.ebay.com/DevZone/account/appsettings/Consent/
19
+ #
20
+ attr_reader :ru_name
21
+
22
+ # Construct a GetSessionID eBay API call.
23
+ # @param [Hash] args a hash of optional arguments.
24
+ # @option args [String] :ru_name Override the default RuName,
25
+ # which should be defined in {Configuration#ru_name}.
26
+ #
27
+ def initialize(args = {})
28
+ @ru_name = (args[:ru_name] || EbayTrader.configuration.ru_name).freeze
29
+
30
+ super(CALL_NAME, args) do
31
+ RuName ru_name
32
+ end
33
+ end
34
+
35
+ # Get the session ID returned by the API call.
36
+ # @return [String] the session ID.
37
+ #
38
+ def id
39
+ response_hash[:session_id]
40
+ end
41
+
42
+ # Get the URL through which a user must sign in using this session ID.
43
+ # @param [Hash] ruparams eBay appends this data to the AcceptURL and RejectURL.
44
+ # In a typical rails app this might include the user's model primary key.
45
+ # @return [String] the sign-in URL.
46
+ #
47
+ def sign_in_url(ruparams = {})
48
+ url = EbayTrader.configuration.production? ? 'https://signin.ebay.com' : 'https://signin.sandbox.ebay.com'
49
+ url << '/ws/eBayISAPI.dll?SignIn'
50
+ url << "&runame=#{url_encode ru_name}"
51
+ url << "&SessID=#{url_encode id}"
52
+ if ruparams && ruparams.is_a?(Hash) && !ruparams.empty?
53
+ params = []
54
+ ruparams.each_pair { |key, value| params << "#{key}=#{value}" }
55
+ url << "&ruparams=#{url_encode(params.join('&'))}"
56
+ end
57
+ url
58
+ end
59
+
60
+ #---------------------------------------------------------------
61
+ private
62
+
63
+ def url_encode(string)
64
+ CGI.escape string
65
+ end
66
+
67
+ def url_decode(string)
68
+ CGI.unescape string
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,3 @@
1
+ module EbayTrader
2
+ VERSION = '0.9.5'
3
+ end
@@ -0,0 +1,113 @@
1
+ module EbayTrader
2
+ class XMLBuilder
3
+
4
+ attr_reader :context, :xml, :depth, :tab_width
5
+
6
+ def initialize(args = {})
7
+ @xml = ''
8
+ @depth = 0
9
+ @tab_width = (args[:tab_width] || 0).to_i
10
+ end
11
+
12
+ # Catch-all method to avoid having to create individual methods for each XML tag name.
13
+ def method_missing(method_name, *args, &block)
14
+ if @context && @context.respond_to?(method_name)
15
+ @context.send(method_name, *args, &block)
16
+ else
17
+ node(method_name, args, &block)
18
+ end
19
+ end
20
+
21
+ # Only respond to method names with only numbers and letters.
22
+ # Do not respond to names with underscores.
23
+ def respond_to_missing?(method_name, include_private = false)
24
+ super || method_name.to_s =~ /^[a-z0-9]+$/i
25
+ end
26
+
27
+ # Begin creating an XML string by specifying the root node.
28
+ # This also set the context scope, allowing methods and variable
29
+ # outside the block to be accessed.
30
+ # @param [String] name the name of the root node element.
31
+ # @param [Array] args the data for this element.
32
+ # @param [Block] block an optional block of sub-elements to be nested
33
+ # within the root node.
34
+ def root(name, *args, &block)
35
+ set_context(&block)
36
+ node(name, args, &block)
37
+ end
38
+
39
+ #---------------------------------------------------------------------------
40
+ private
41
+
42
+ # @see https://github.com/sparklemotion/nokogiri/blob/master/lib/nokogiri/xml/builder.rb
43
+ def set_context(&block)
44
+ @context = block_given? ? eval('self', block.binding) : nil
45
+ @context = nil if @context.is_a?(XMLBuilder)
46
+ end
47
+
48
+ # Create an XML node
49
+ # @param [String|Symbol] name the name of the XML element (ul, li, strong, etc...)
50
+ # @param [Array] args Can contain a String of text or a Hash of attributes
51
+ # @param [Block] block An optional block which will further nest XML
52
+ def node(name, args, &block)
53
+ content = get_node_content(args)
54
+ options = format_node_attributes(get_node_attributes(args))
55
+
56
+ @xml << "#{indent_new_line}<#{name}#{options}>#{content}"
57
+ if block_given?
58
+ @depth += 1
59
+ instance_eval(&block)
60
+ @depth -= 1
61
+ @xml << indent_new_line
62
+ end
63
+ @xml << "</#{name}>"
64
+ @xml.strip
65
+ end
66
+
67
+ # Return the first Hash in the list of arguments to #node
68
+ # as this defines the attributes for the XML node.
69
+ # @return [Hash] the hash of attributes for this node.
70
+ #
71
+ def get_node_attributes(args)
72
+ args.detect { |arg| arg.is_a? Hash } || {}
73
+ end
74
+
75
+ # Return the node content as a String, unless a block is given.
76
+ # @return [String] the node data.
77
+ #
78
+ def get_node_content(args)
79
+ return nil if block_given?
80
+ content = nil
81
+ args.each do |arg|
82
+ case arg
83
+ when Hash
84
+ next
85
+ when Time
86
+ # eBay official TimeStamp format YYYY-MM-DDTHH:MM:SS.SSSZ
87
+ content = arg.strftime('%Y-%m-%dT%H:%M:%S.%Z')
88
+ when DateTime
89
+ content = arg.strftime('%Y-%m-%dT%H:%M:%S.%Z')
90
+ break
91
+ else
92
+ content = arg.to_s
93
+ break
94
+ end
95
+ end
96
+ content
97
+ end
98
+
99
+ # Convert the given Hash of options into a string of XML element attributes.
100
+ #
101
+ def format_node_attributes(options)
102
+ options.collect { |key, value|
103
+ value = value.to_s.gsub('"', '\"')
104
+ " #{key}=\"#{value}\""
105
+ }.join('')
106
+ end
107
+
108
+ # Add a new line to the XML and indent with the appropriate number of spaces.
109
+ def indent_new_line
110
+ tab_width > 0 ? ("\n" + (' ' * tab_width * depth)) : ''
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,23 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
+
3
+ class HashWithIndifferentAccess
4
+
5
+ # Perform a depth first search of this hash and return the first element
6
+ # matching +path+, or +default+ if nothing found.
7
+ # @param [Array[String | Symbol] | String | Symbol] path to the element of interest.
8
+ # @param [Object] default the object to be returned if there is no result for +path+.
9
+ # @return [Object] the first object found on +path+ or +default+.
10
+ #
11
+ def deep_find(path, default = nil)
12
+ return default unless path
13
+ path = [path] if path.is_a?(String) || path.is_a?(Symbol)
14
+ return default unless path.is_a?(Array) && !path.empty?
15
+
16
+ location = self
17
+ path.each do |key|
18
+ return default if location.nil? || !location.key?(key)
19
+ location = location[key]
20
+ end
21
+ return location
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ebay-trader
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.5
5
+ platform: ruby
6
+ authors:
7
+ - Rob Graham
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-09-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: ox
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.2'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.2'
83
+ description: |2
84
+ EbayTrader is a lightweight easy to use Ruby gem for interacting with eBay's Trading API.
85
+ Using its simple DSL you can quickly and intuitively post XML requests to eBay and rapidly interpret the responses.
86
+ email:
87
+ - rob@altabyte.com
88
+ executables: []
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - ".gitignore"
93
+ - ".rspec"
94
+ - ".travis.yml"
95
+ - Gemfile
96
+ - LICENSE.txt
97
+ - README.md
98
+ - Rakefile
99
+ - bin/console
100
+ - bin/setup
101
+ - ebay_trader.gemspec
102
+ - examples/get_categories.rb
103
+ - examples/more_examples.rb
104
+ - lib/ebay_trader.rb
105
+ - lib/ebay_trader/configuration.rb
106
+ - lib/ebay_trader/fetch_token.rb
107
+ - lib/ebay_trader/request.rb
108
+ - lib/ebay_trader/sax_handler.rb
109
+ - lib/ebay_trader/session_id.rb
110
+ - lib/ebay_trader/version.rb
111
+ - lib/ebay_trader/xml_builder.rb
112
+ - lib/hash_with_indifferent_access.rb
113
+ homepage: https://github.com/altabyte/ebay_trader
114
+ licenses:
115
+ - MIT
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.4.6
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: A DSL to interact with eBay's Trading API using Ruby
137
+ test_files: []
138
+ has_rdoc: