blythedunham-base4r 0.2.0.6 → 0.2.0.7
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/LICENSE +339 -0
- data/README +19 -0
- data/cert/cacert.pem +10908 -0
- data/lib/attribute.rb +332 -0
- data/lib/base4r.rb +25 -0
- data/lib/base_client.rb +149 -0
- data/lib/client_login.rb +98 -0
- data/lib/http_logger.rb +32 -0
- data/lib/item.rb +290 -0
- data/test/attribute_test.rb +58 -0
- data/test/base_client_test.rb +93 -0
- data/test/client_login_test.rb +60 -0
- data/test/item_test.rb +51 -0
- metadata +15 -3
data/lib/attribute.rb
ADDED
@@ -0,0 +1,332 @@
|
|
1
|
+
# Base4R is a ruby interface to Google Base
|
2
|
+
# Copyright 2007, 2008 Dan Dukeson
|
3
|
+
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
|
14
|
+
# You should have received a copy of the GNU General Public License along
|
15
|
+
# with this program; if not, write to the Free Software Foundation, Inc.,
|
16
|
+
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
17
|
+
|
18
|
+
#
|
19
|
+
# following taken from http://code.google.com/apis/base/starting-out.html
|
20
|
+
#
|
21
|
+
# There are some limitations on how long your attribute names and values can be and how many you can use:
|
22
|
+
|
23
|
+
# * titles: between 3-1000 characters
|
24
|
+
# * attributes:
|
25
|
+
# o names: 30 characters
|
26
|
+
# o attribute text: 1000 characters, including spaces
|
27
|
+
# o total number permitted: 30 per item
|
28
|
+
# * labels:
|
29
|
+
# o names: 40 characters
|
30
|
+
# o total number permitted: 10 per item
|
31
|
+
# * URL length: 1000
|
32
|
+
|
33
|
+
module Base4R
|
34
|
+
|
35
|
+
module AdditionalXmlAttributeMethods
|
36
|
+
def self.included(base)
|
37
|
+
base.send :extend, ClassMethods
|
38
|
+
end
|
39
|
+
module ClassMethods
|
40
|
+
def define_xml_attribute(name, value)
|
41
|
+
@xml_attributes||={}
|
42
|
+
@xml_attributes[name.to_s] = value
|
43
|
+
end
|
44
|
+
|
45
|
+
def xml_attributes; @xml_attributes||={}; end
|
46
|
+
end
|
47
|
+
|
48
|
+
def additional_xml_attribute_map
|
49
|
+
self.class.xml_attributes.merge(xml_attributes||{}).inject({}) do |map, (k,v)|
|
50
|
+
map[k] = xml_attribute_value(v)
|
51
|
+
map
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def xml_attribute_value(v)
|
56
|
+
v.is_a?(Proc) ? v.call(self) : v.to_s
|
57
|
+
end
|
58
|
+
|
59
|
+
def xml_attributes; options[:xml_attributes]||{}; end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
module ChildAttributeMethods
|
64
|
+
def self.included(base)
|
65
|
+
base.send :extend, ClassMethods
|
66
|
+
end
|
67
|
+
|
68
|
+
module ClassMethods
|
69
|
+
def child_attribute(attr, options={})
|
70
|
+
default_child_options[attr.to_sym] = options
|
71
|
+
class_eval <<-EOS
|
72
|
+
def child_#{attr}; children[:#{attr}]; end
|
73
|
+
def child_#{attr}=(v); add_child(:#{attr}, v); end
|
74
|
+
def #{attr}_value; #{attr}.value; end
|
75
|
+
def #{attr}_value=(v); #{attr}.value = v; end
|
76
|
+
EOS
|
77
|
+
end
|
78
|
+
def default_children_names; default_child_options.keys; end
|
79
|
+
def default_child_options; @default_child_options||={}; end
|
80
|
+
end
|
81
|
+
|
82
|
+
protected
|
83
|
+
# Add default children (defined) if data specified
|
84
|
+
def add_default_children
|
85
|
+
child_data = options.merge(options[:children]||{})
|
86
|
+
self.class.default_children_names.concat((options[:children]||{}).keys).uniq.each do |child_name|
|
87
|
+
add_child(child_name, child_data[child_name.to_sym]) if child_data.has_key?(child_name.to_sym)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
public
|
92
|
+
# Add a custom prebuilt child
|
93
|
+
# +attribute+ - an item of class +Attribute+
|
94
|
+
# shipping_attribute.add_custom_child(BareAttribute.new(:region, 'WA'))
|
95
|
+
def add_child_attribute(attribute)
|
96
|
+
children_map[attribute.name.to_sym] = attribute
|
97
|
+
end
|
98
|
+
|
99
|
+
# add a child. The accepted arguments are the same as initializing a new +Attribute+
|
100
|
+
# shipping_attribute.add_child(:region, 'WA')
|
101
|
+
# shipping_attribute.add_child(:region, :value => 'WA', :namespace => :g)
|
102
|
+
def add_child(*args)
|
103
|
+
#parse out the options specified here into keys
|
104
|
+
parsed_options = options_from_args(args, :skip_defaults => true)
|
105
|
+
|
106
|
+
child_name = parsed_options[:name].to_sym
|
107
|
+
|
108
|
+
# merge the default options with the extra options with the namespace, name, type options
|
109
|
+
child_options = (self.class.default_child_options[child_name]||{}).merge(parsed_options)
|
110
|
+
klass = child_options.delete(:klass)||BareAttribute
|
111
|
+
|
112
|
+
children_map[child_name] = klass.new(child_options)
|
113
|
+
end
|
114
|
+
|
115
|
+
#get a specific child
|
116
|
+
def child(child_name); children_map[child_name.to_sym]; end
|
117
|
+
|
118
|
+
# a map of all the children names to Attribute class values
|
119
|
+
def children_map; @children_map||={}; end
|
120
|
+
# All the children Attribute objects
|
121
|
+
def children; children_map.values; end
|
122
|
+
# All the names of the children
|
123
|
+
def children_names; children_map.keys; end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
# Attributes are typed key-value pairs that describe content. Attributes describe the
|
128
|
+
# Base Item. Client code should use subclasses of Attribute such as TextAttribute,
|
129
|
+
# BareAttribute, DateTimeAttribute, IntAttribute, FloatUnitAttribute, UrlAttribute,
|
130
|
+
# LocationAttribute, BooleanAttribute.
|
131
|
+
# Each of these can represent themselves as required by the Atom format.
|
132
|
+
class Attribute
|
133
|
+
|
134
|
+
attr_accessor :name
|
135
|
+
attr_accessor :value
|
136
|
+
attr_accessor :namespace
|
137
|
+
attr_accessor :options
|
138
|
+
attr_accessor :private_attribute
|
139
|
+
|
140
|
+
include ChildAttributeMethods
|
141
|
+
include AdditionalXmlAttributeMethods
|
142
|
+
|
143
|
+
# Represent this Attribute as an XML element that is a child of _parent_.
|
144
|
+
def to_xml(parent, options={})
|
145
|
+
|
146
|
+
|
147
|
+
return if value.nil? && children.empty? && !options[:force].is_a?(TrueClass)
|
148
|
+
|
149
|
+
|
150
|
+
|
151
|
+
el = parent.add_element(calc_el_name)
|
152
|
+
|
153
|
+
el.add_attribute('type', type_name) if type_name && !type_name.empty?
|
154
|
+
el.add_attribute('access', 'private') if private_attribute?
|
155
|
+
|
156
|
+
|
157
|
+
|
158
|
+
|
159
|
+
#add additional attributes like href=http://meh.com
|
160
|
+
additional_xml_attribute_map.each { |k,v| el.add_attribute(k.to_s, v.to_s) if v }
|
161
|
+
|
162
|
+
el.text = value.to_s if value
|
163
|
+
|
164
|
+
children.each {|child| child.to_xml(el) }
|
165
|
+
el
|
166
|
+
end
|
167
|
+
|
168
|
+
def private_attribute?; private_attribute.is_a?(TrueClass); end
|
169
|
+
|
170
|
+
def type_name=(value); @type_name=value.to_s; end
|
171
|
+
|
172
|
+
# the type name for this attribute
|
173
|
+
def type_name; @type_name || self.class.type_name; end
|
174
|
+
|
175
|
+
class << self
|
176
|
+
# keep this pure ruby instead of using rails stuff
|
177
|
+
def type_name
|
178
|
+
self.to_s.gsub(/^Base4R::(\w)(\w*)Attribute$/) { "#{$1.downcase}#{$2}" }
|
179
|
+
end
|
180
|
+
# Create an xml object with this
|
181
|
+
# BareAttribute.new(parent, :myattr, 'love', :private_attribute => true)
|
182
|
+
def to_xml(parent, name, value, options={})
|
183
|
+
new( name, value, options).to_xml(parent)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
protected
|
188
|
+
|
189
|
+
# * +name+ - the name of the attribute
|
190
|
+
# * +value+ - the value of the attribute
|
191
|
+
# * +args+ - can be the namespace (default to :g) or the options
|
192
|
+
# ===Options
|
193
|
+
# <tt>:namespace</tt> - new way to specify namespace
|
194
|
+
# <tt>:private_attribute</tt> - default false. set true if this is private (set attribute access = private)
|
195
|
+
# <tt>:additional_attributes</tt - map of additional attribute decorations name/value pairs
|
196
|
+
def initialize(*args)
|
197
|
+
|
198
|
+
options_from_args(args, :set_instance => true)
|
199
|
+
|
200
|
+
type_name = options.delete(:type_name) if options.has_key?(:type_name)
|
201
|
+
|
202
|
+
@private_attribute = options.delete(:private_attribute).is_a?(TrueClass)
|
203
|
+
add_default_children #create the children attributes if any
|
204
|
+
end
|
205
|
+
|
206
|
+
# arguments can work like
|
207
|
+
# name, value, namespace =:g, options={}
|
208
|
+
# name, value, options={}
|
209
|
+
# name, options={}
|
210
|
+
def options_from_args(args, parse_options={})#:nodoc:
|
211
|
+
|
212
|
+
# if the last argument is a hash, use it for the options
|
213
|
+
option_map = args.last.is_a?(Hash) ? args.pop : {}
|
214
|
+
|
215
|
+
parsed_option_map = [:name, :value, :namespace].inject({}) do |map, v|
|
216
|
+
#first pop the value if any are left
|
217
|
+
val = if args.any?
|
218
|
+
args.shift
|
219
|
+
|
220
|
+
#check for the value in the option map
|
221
|
+
elsif option_map.has_key?(v)
|
222
|
+
option_map.delete(v)
|
223
|
+
|
224
|
+
#default namespace to :g
|
225
|
+
elsif v == :namespace && !parse_options[:skip_defaults].is_a?(TrueClass)
|
226
|
+
:g
|
227
|
+
else
|
228
|
+
:__SKIP_THIS_VAR__
|
229
|
+
end
|
230
|
+
|
231
|
+
map[v] = val unless val == :__SKIP_THIS_VAR__
|
232
|
+
map
|
233
|
+
end
|
234
|
+
|
235
|
+
if parse_options[:set_instance]
|
236
|
+
parsed_option_map.each { |name, val| instance_variable_set "@#{name}", val }
|
237
|
+
@options = option_map
|
238
|
+
end
|
239
|
+
|
240
|
+
option_map.merge(parsed_option_map)
|
241
|
+
|
242
|
+
end
|
243
|
+
def calc_el_name; namespace ? "#{namespace}:#{name}" : name.to_s; end
|
244
|
+
end
|
245
|
+
|
246
|
+
# TextAttribute is a simple string.x
|
247
|
+
class TextAttribute < Attribute; end
|
248
|
+
|
249
|
+
# BareAttribute is a string attribute but is in the google namespace
|
250
|
+
class BareAttribute < Attribute
|
251
|
+
def type_name; @type_name; end
|
252
|
+
end
|
253
|
+
|
254
|
+
# UrlAttribute represents a URL
|
255
|
+
class UrlAttribute < Attribute
|
256
|
+
define_xml_attribute :rel, :alternate
|
257
|
+
define_xml_attribute :type, 'text/html'
|
258
|
+
define_xml_attribute :href, Proc.new{|attr| attr.value }
|
259
|
+
|
260
|
+
def to_xml(parent, options={})
|
261
|
+
el = super(parent, options.merge(:force => true))
|
262
|
+
el.text = nil
|
263
|
+
el
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
# DateTimeAttribute represents a DateTime
|
268
|
+
class DateTimeAttribute < Attribute; end
|
269
|
+
|
270
|
+
# IntAttribute represents an Integer
|
271
|
+
class IntAttribute < Attribute; end
|
272
|
+
|
273
|
+
# BooleanAttribute represents a Boolean
|
274
|
+
class BooleanAttribute < Attribute; end
|
275
|
+
|
276
|
+
class SomethingUnits < Attribute
|
277
|
+
attr_accessor :units
|
278
|
+
attr_accessor :value_without_units
|
279
|
+
|
280
|
+
def initialize(*args)
|
281
|
+
super(*args)
|
282
|
+
@units = options.delete(:units)
|
283
|
+
@value_without_units = @value
|
284
|
+
end
|
285
|
+
|
286
|
+
def value; "#{@value_without_units} #{@units}".strip; end
|
287
|
+
def value=(v); @value_without_units, @units = v.split; end
|
288
|
+
end
|
289
|
+
|
290
|
+
class NumberUnits < SomethingUnits; end
|
291
|
+
|
292
|
+
# FloatUnitAttribute represents a floating-point property with units.
|
293
|
+
class FloatUnitAttribute < SomethingUnits
|
294
|
+
# Create a FloatUnitAttribute with _name_, a quantity of _number_, described in _units_ and in _namespace_
|
295
|
+
# old definition:
|
296
|
+
# FloatUnitAttribute.new(name, number, units, namespace)
|
297
|
+
# new definition:
|
298
|
+
# FloatUnitAttribute.new(name, number, options={})
|
299
|
+
# FloatUnitAttribute.new(:price, 50, :units => :USD, :namespace => :g)
|
300
|
+
# === Additional Options
|
301
|
+
# <tt>:units</tt> - the units used
|
302
|
+
def initialize(*args)
|
303
|
+
if args.length == 4 and !args.last.is_a?(Hash)
|
304
|
+
super(:name => args[0], :value => args[1], :units => args[2], :namespace => args.last)
|
305
|
+
else
|
306
|
+
super *args
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
class ReferenceAttribute < Attribute; end
|
312
|
+
|
313
|
+
# LocationAttribute represents an Item's location
|
314
|
+
class LocationAttribute < Attribute
|
315
|
+
child_attribute :longitude
|
316
|
+
child_attribute :latitude
|
317
|
+
|
318
|
+
end
|
319
|
+
|
320
|
+
class AuthorAttribute < Attribute
|
321
|
+
child_attribute :email, :namespace => nil
|
322
|
+
child_attribute :name, :namespace => nil
|
323
|
+
|
324
|
+
def initialize(*args)
|
325
|
+
super(*args)
|
326
|
+
@namespace = nil
|
327
|
+
end
|
328
|
+
|
329
|
+
def type_name; nil; end
|
330
|
+
|
331
|
+
end
|
332
|
+
end
|
data/lib/base4r.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Base4R is a ruby interface to Google Base
|
2
|
+
# Copyright 2007, 2008 Dan Dukeson
|
3
|
+
# This program is free software; you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation; either version 2 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
|
13
|
+
# You should have received a copy of the GNU General Public License along
|
14
|
+
# with this program; if not, write to the Free Software Foundation, Inc.,
|
15
|
+
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
16
|
+
|
17
|
+
#
|
18
|
+
# Require files we depend on
|
19
|
+
#
|
20
|
+
#
|
21
|
+
|
22
|
+
require 'http_logger'
|
23
|
+
require 'base_client'
|
24
|
+
require 'item'
|
25
|
+
require 'attribute'
|
data/lib/base_client.rb
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
# Base4R is a ruby interface to Google Base
|
2
|
+
# Copyright 2007, 2008 Dan Dukeson
|
3
|
+
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
|
14
|
+
# You should have received a copy of the GNU General Public License along
|
15
|
+
# with this program; if not, write to the Free Software Foundation, Inc.,
|
16
|
+
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
17
|
+
|
18
|
+
require 'net/http'
|
19
|
+
require 'client_login'
|
20
|
+
|
21
|
+
module Base4R
|
22
|
+
|
23
|
+
class BaseException < Exception; end
|
24
|
+
|
25
|
+
class ErrorResponse < BaseException
|
26
|
+
attr_reader :response
|
27
|
+
|
28
|
+
def initialize(msg, resp)
|
29
|
+
@response = resp
|
30
|
+
super msg
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class ItemNotFound < ErrorResponse; end
|
35
|
+
|
36
|
+
# BaseClient handles all communication with the Base API using HTTP
|
37
|
+
class BaseClient
|
38
|
+
|
39
|
+
include HTTPLogger
|
40
|
+
|
41
|
+
ITEMS_PATH = '/base/feeds/items/'
|
42
|
+
SNIPPETS_PATH = '/base/feeds/snippets/'
|
43
|
+
BASE_HOST = 'base.google.com'
|
44
|
+
|
45
|
+
attr_reader :auth_key #:nodoc:
|
46
|
+
attr_reader :feed_path #:nodoc:
|
47
|
+
attr_accessor :dry_run
|
48
|
+
|
49
|
+
# Construct a BaseClient, which will make API requiest for the Base account
|
50
|
+
# belonging to _username_, authenticating with _password_ and using _api_key_.
|
51
|
+
# Requests will be made against the public feed if _public_feed_ is true, which is the default.
|
52
|
+
# The BaseClient can be used for a number of Base API requests.
|
53
|
+
#
|
54
|
+
def initialize(username, password, api_key, public_feed=true, dry_run=false)
|
55
|
+
|
56
|
+
@auth_key = ClientLogin.new.authenticate(username, password)
|
57
|
+
@api_key = api_key
|
58
|
+
|
59
|
+
if public_feed then
|
60
|
+
@feed_path = SNIPPETS_PATH
|
61
|
+
else
|
62
|
+
@feed_path = ITEMS_PATH
|
63
|
+
end
|
64
|
+
@dry_run = dry_run
|
65
|
+
end
|
66
|
+
|
67
|
+
# Creates the supplied _item_ as a new Base Item.
|
68
|
+
# Throws an Exception if there is a problem creating _item_.
|
69
|
+
#
|
70
|
+
def create_item(item)
|
71
|
+
resp = do_request(item.to_xml.to_s, 'POST')
|
72
|
+
raise ErrorResponse.new("Error creating base item: #{resp.body}", resp) unless resp.kind_of? Net::HTTPSuccess
|
73
|
+
resp['location'] =~ /(\d+)$/
|
74
|
+
item.base_id= $1
|
75
|
+
end
|
76
|
+
|
77
|
+
# Update the supplied Base _item_. Returns true on success.
|
78
|
+
# Throws an Exception if there is a problem updating _item_.
|
79
|
+
#
|
80
|
+
def update_item(item)
|
81
|
+
base_id = item_base_id item
|
82
|
+
raise BaseException.new("base_id is required") if base_id.nil?
|
83
|
+
resp = do_request(item.to_xml.to_s, 'PUT', :base_id => base_id)
|
84
|
+
raise_response_error "Error updating base item", resp
|
85
|
+
true
|
86
|
+
end
|
87
|
+
|
88
|
+
# Delete the supplied Base _item_. Returns true on success.
|
89
|
+
# Throws an Exception if there is a problem deleting _item_
|
90
|
+
def delete_item(item)
|
91
|
+
base_id = item_base_id item
|
92
|
+
raise BaseException.new("base_id is required") if base_id.nil?
|
93
|
+
resp = do_request(nil, 'DELETE', :base_id => base_id)
|
94
|
+
raise_response_error "Error deleting base item", resp
|
95
|
+
raise BaseException.new("Error deleting base item:"+resp.body) unless resp.kind_of? Net::HTTPOK
|
96
|
+
true
|
97
|
+
end
|
98
|
+
|
99
|
+
def get_item(base_id)
|
100
|
+
resp = do_request '', 'GET', :base_id => nil, :url => "http://www.google.com/base/feeds/items/#{base_id}"
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
#raise the appropriate error based on the response
|
106
|
+
# +message+ - the error message
|
107
|
+
# +response+ - the Net::HTTPResponse object
|
108
|
+
def raise_response_error(message, response)
|
109
|
+
error_klass = if response.is_a?(Net::HTTPNotFound) && response.body =~ /Cannot find item/
|
110
|
+
ItemNotFound
|
111
|
+
elsif !response.kind_of?(Net::HTTPOK)
|
112
|
+
ErrorResponse
|
113
|
+
end
|
114
|
+
|
115
|
+
raise error_klass.new("#{message}: #{response.body}", response) if error_klass
|
116
|
+
end
|
117
|
+
|
118
|
+
# Return the base id of the item if it is a Base4r::Item
|
119
|
+
# otherwise assume it is the actual base id
|
120
|
+
def item_base_id(item)
|
121
|
+
item.respond_to?(:base_id) ? item.base_id : item
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
def do_request(data, http_method, options={})
|
126
|
+
|
127
|
+
|
128
|
+
url = options[:url]||"http://#{BASE_HOST}#{@feed_path}"
|
129
|
+
url << "#{options[:base_id]}" if options[:base_id]
|
130
|
+
url << "?dry-run=true" if dry_run
|
131
|
+
url = URI.parse(url)
|
132
|
+
|
133
|
+
headers = {'X-Google-Key' => "key=#{@api_key}",
|
134
|
+
'Authorization' => "GoogleLogin auth=#{@auth_key}",
|
135
|
+
'Content-Type' => 'application/atom+xml'}
|
136
|
+
|
137
|
+
result = Net::HTTP.start(url.host, url.port) { |http|
|
138
|
+
request = Net::HTTPGenericRequest.new(http_method,(data ? true : false),true, url.path, headers)
|
139
|
+
log_request request, :url => url, :method => http_method, :data => data
|
140
|
+
http.request request, data
|
141
|
+
}
|
142
|
+
|
143
|
+
log_response result
|
144
|
+
result
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|