weary 0.7.2 → 1.0.0.rc1
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 +4 -1
- data/.rspec +2 -0
- data/.travis.yml +10 -0
- data/Gemfile +11 -8
- data/Gemfile.lock +49 -53
- data/LICENSE +1 -1
- data/README.md +134 -208
- data/Rakefile +6 -47
- data/lib/weary.rb +4 -66
- data/lib/weary/adapter.rb +23 -0
- data/lib/weary/adapters/net_http.rb +68 -0
- data/lib/weary/client.rb +243 -0
- data/lib/weary/deferred.rb +35 -0
- data/lib/weary/env.rb +32 -0
- data/lib/weary/middleware.rb +9 -0
- data/lib/weary/middleware/basic_auth.rb +17 -0
- data/lib/weary/middleware/content_type.rb +28 -0
- data/lib/weary/middleware/oauth.rb +31 -0
- data/lib/weary/request.rb +137 -124
- data/lib/weary/resource.rb +152 -128
- data/lib/weary/response.rb +48 -99
- data/lib/weary/route.rb +53 -0
- data/lib/weary/version.rb +3 -0
- data/spec/spec_helper.rb +4 -56
- data/spec/support/shared_examples_for_a_rack_app.rb +70 -0
- data/spec/support/shared_examples_for_a_rack_env.rb +14 -0
- data/spec/support/shared_examples_for_a_uri.rb +9 -0
- data/spec/weary/adapter_spec.rb +26 -0
- data/spec/weary/adapters/nethttp_spec.rb +88 -0
- data/spec/weary/client_spec.rb +258 -0
- data/spec/weary/deferred_spec.rb +35 -0
- data/spec/weary/env_spec.rb +12 -0
- data/spec/weary/middleware/basic_auth_spec.rb +23 -0
- data/spec/weary/middleware/content_type_spec.rb +34 -0
- data/spec/weary/middleware/oauth_spec.rb +27 -0
- data/spec/weary/request_spec.rb +227 -315
- data/spec/weary/resource_spec.rb +233 -233
- data/spec/weary/response_spec.rb +82 -159
- data/spec/weary/route_spec.rb +72 -0
- data/spec/weary_spec.rb +3 -56
- data/weary.gemspec +16 -79
- metadata +138 -98
- data/VERSION +0 -1
- data/examples/batch.rb +0 -20
- data/examples/repo.rb +0 -16
- data/examples/status.rb +0 -26
- data/lib/weary/base.rb +0 -124
- data/lib/weary/batch.rb +0 -37
- data/lib/weary/exceptions.rb +0 -15
- data/lib/weary/httpverb.rb +0 -32
- data/spec/fixtures/github.yml +0 -11
- data/spec/fixtures/twitter.xml +0 -763
- data/spec/fixtures/vimeo.json +0 -1
- data/spec/weary/base_spec.rb +0 -320
- data/spec/weary/batch_spec.rb +0 -71
- data/spec/weary/httpverb_spec.rb +0 -25
data/Rakefile
CHANGED
@@ -1,51 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
rescue LoadError
|
5
|
-
# Fall back on doing an unlocked resolve at runtime.
|
6
|
-
require "rubygems"
|
7
|
-
require "bundler"
|
8
|
-
Bundler.setup
|
9
|
-
end
|
10
|
-
|
11
|
-
require 'spec/rake/spectask'
|
1
|
+
require 'rspec/core/rake_task'
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require 'yard'
|
12
4
|
|
13
5
|
task :default => :spec
|
14
6
|
|
15
|
-
|
16
|
-
Rake::RDocTask.new do |rdoc|
|
17
|
-
rdoc.rdoc_dir = 'doc'
|
18
|
-
rdoc.title = 'weary'
|
19
|
-
rdoc.main = 'README.md'
|
20
|
-
rdoc.rdoc_files.include('README.*', 'lib/**/*.rb', 'LICENSE')
|
21
|
-
rdoc.options << '--inline-source'
|
22
|
-
end
|
23
|
-
|
24
|
-
begin
|
25
|
-
require 'jeweler'
|
26
|
-
Jeweler::Tasks.new do |gemspec|
|
27
|
-
gemspec.name = "weary"
|
28
|
-
gemspec.rubyforge_project = "weary"
|
29
|
-
gemspec.summary = "A little DSL for consuming RESTful web services"
|
30
|
-
gemspec.email = "mark@markwunsch.com"
|
31
|
-
gemspec.homepage = "http://github.com/mwunsch/weary"
|
32
|
-
gemspec.description = "A tiny DSL that makes the consumption of RESTful web services simple."
|
33
|
-
gemspec.authors = "Mark Wunsch"
|
34
|
-
gemspec.add_dependency 'crack', '>= 0.1.7'
|
35
|
-
gemspec.add_dependency 'oauth', '>= 0.3.5'
|
36
|
-
gemspec.add_development_dependency 'bundler', ">= 0.9.7"
|
37
|
-
end
|
38
|
-
Jeweler::GemcutterTasks.new
|
39
|
-
rescue LoadError
|
40
|
-
puts "Jeweler not available. Install it with: gem install jeweler"
|
41
|
-
end
|
42
|
-
|
43
|
-
desc "Open an irb session preloaded with this library"
|
44
|
-
task :console do
|
45
|
-
sh "irb -rubygems -I lib -r weary.rb"
|
46
|
-
end
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
47
8
|
|
48
|
-
|
49
|
-
|
50
|
-
t.spec_opts = ['--color','--format nested']
|
51
|
-
end
|
9
|
+
# The state of yard-tomdoc isn't so great right now
|
10
|
+
YARD::Rake::YardocTask.new
|
data/lib/weary.rb
CHANGED
@@ -1,34 +1,8 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require 'net/https'
|
4
|
-
|
5
|
-
require 'crack'
|
6
|
-
require 'oauth'
|
7
|
-
|
8
|
-
autoload :Yaml, 'yaml'
|
9
|
-
|
10
|
-
require 'weary/request'
|
11
|
-
require 'weary/response'
|
12
|
-
require 'weary/resource'
|
13
|
-
require 'weary/batch'
|
14
|
-
require 'weary/exceptions'
|
15
|
-
require 'weary/httpverb'
|
16
|
-
require 'weary/base'
|
1
|
+
require 'weary/version'
|
2
|
+
require 'weary/client'
|
17
3
|
|
18
4
|
module Weary
|
19
|
-
|
20
|
-
# Supported HTTP Verbs
|
21
|
-
Methods = [:get, :post, :put, :delete, :head]
|
22
|
-
|
23
|
-
# Supported Content Types
|
24
|
-
ContentTypes = { :json => [:json, 'json', 'application/json', 'text/json', 'application/javascript', 'text/javascript'],
|
25
|
-
:xml => [:xml, 'xml', 'text/xml', 'application/xml'],
|
26
|
-
:html => [:html, 'html', 'text/html'],
|
27
|
-
:yaml => [:yaml, 'yaml', 'application/x-yaml', 'text/yaml'],
|
28
|
-
:plain => [:plain, 'plain', 'text/plain'] }
|
29
|
-
|
30
|
-
# A collection of User Agent strings that I stole from HURL (http://hurl.it)
|
31
|
-
UserAgents = {
|
5
|
+
USER_AGENTS = {
|
32
6
|
"Firefox 1.5.0.12 - Mac" => "Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.12) Gecko/20070508 Firefox/1.5.0.12",
|
33
7
|
"Firefox 1.5.0.12 - Windows" => "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.12) Gecko/20070508 Firefox/1.5.0.12",
|
34
8
|
"Firefox 2.0.0.12 - Mac" => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.12) Gecko/20080201 Firefox/2.0.0.12",
|
@@ -58,40 +32,4 @@ module Weary
|
|
58
32
|
"Safari 4.0.2 - Mac" => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19",
|
59
33
|
"Safari 4.0.2 - Windows" => "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1"
|
60
34
|
}
|
61
|
-
|
62
|
-
class << self
|
63
|
-
def get(url,&block)
|
64
|
-
request url, :get, &block
|
65
|
-
end
|
66
|
-
|
67
|
-
def post(url,&block)
|
68
|
-
request url, :post, &block
|
69
|
-
end
|
70
|
-
|
71
|
-
def put(url,&block)
|
72
|
-
request url, :put, &block
|
73
|
-
end
|
74
|
-
|
75
|
-
def delete(url,&block)
|
76
|
-
request url, :delete, &block
|
77
|
-
end
|
78
|
-
|
79
|
-
def head(url,&block)
|
80
|
-
request url, :head, &block
|
81
|
-
end
|
82
|
-
|
83
|
-
# Create a Request for the URL.
|
84
|
-
# Defaults to a GET Request. Use a block to further modify the Request
|
85
|
-
def request(url,via = :get, &block)
|
86
|
-
req = Request.new(url,via)
|
87
|
-
yield req if block_given?
|
88
|
-
req
|
89
|
-
end
|
90
|
-
|
91
|
-
# Create a Batch for a group of Requests
|
92
|
-
def batch(*requests)
|
93
|
-
Batch.new(requests)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
end
|
35
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'weary/response'
|
2
|
+
require 'weary/adapters/net_http'
|
3
|
+
module Weary
|
4
|
+
# An abstract interface. A subclass should be something that actually opens
|
5
|
+
# a socket to make the request, e.g. Net/HTTP, Curb, etc.
|
6
|
+
module Adapter
|
7
|
+
|
8
|
+
def initialize(app=nil)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
connect(Rack::Request.new(env)).finish
|
14
|
+
end
|
15
|
+
|
16
|
+
# request is a Rack::Request
|
17
|
+
# This computation is performed in a Promise/Future
|
18
|
+
# Returns a Rack::Response
|
19
|
+
def connect(request)
|
20
|
+
Rack::Response.new [""], 501, {"Content-Type" => "text/plain"}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
|
4
|
+
module Weary
|
5
|
+
module Adapter
|
6
|
+
class NetHttp
|
7
|
+
include Weary::Adapter
|
8
|
+
|
9
|
+
def self.call(env)
|
10
|
+
connect(Rack::Request.new(env)).finish
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.connect(request)
|
14
|
+
connection = socket(request)
|
15
|
+
response = connection.request prepare(request)
|
16
|
+
Rack::Response.new response.body, response.code, normalize_response(response.to_hash)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.prepare(request)
|
20
|
+
req_class = request_class(request.request_method)
|
21
|
+
req = req_class.new(request.fullpath, normalize_request_headers(request.env))
|
22
|
+
if req.request_body_permitted? # What's the best way of passing the body?
|
23
|
+
req.body = request.body.read
|
24
|
+
request.body.rewind
|
25
|
+
end
|
26
|
+
req.content_type = request.content_type if request.content_type
|
27
|
+
req.content_length = request.content_length if request.content_length
|
28
|
+
req
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.normalize_request_headers(env)
|
32
|
+
req_headers = env.reject {|k,v| !k.start_with? "HTTP_" }
|
33
|
+
normalized = req_headers.map do |k, v|
|
34
|
+
new_key = k.sub("HTTP_",'').split('_').map(&:capitalize).join('-')
|
35
|
+
[new_key, v] unless UNWANTED_REQUEST_HEADERS.include? new_key
|
36
|
+
end
|
37
|
+
Hash[normalized]
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.normalize_response(headers)
|
41
|
+
headers.reject {|k,v| k.downcase == 'status' }
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.socket(request)
|
45
|
+
host = request.env['HTTP_HOST'] || request.env['SERVER_NAME']
|
46
|
+
port = request.env['SERVER_PORT'].to_s
|
47
|
+
connection = Net::HTTP.new host, port
|
48
|
+
connection.use_ssl = request.scheme == 'https'
|
49
|
+
connection.verify_mode = OpenSSL::SSL::VERIFY_NONE if connection.use_ssl?
|
50
|
+
connection
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.request_class(method)
|
54
|
+
capitalized = method.capitalize
|
55
|
+
Net::HTTP.const_get capitalized
|
56
|
+
end
|
57
|
+
|
58
|
+
def call(env)
|
59
|
+
self.class.call(env)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
UNWANTED_REQUEST_HEADERS = []
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/weary/client.rb
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
require 'weary/resource'
|
2
|
+
|
3
|
+
module Weary
|
4
|
+
autoload :Route, 'weary/route'
|
5
|
+
|
6
|
+
# An abstract class used to construct client libraries and the primary
|
7
|
+
# entrance point to use the Weary framework. Client defines a DSL to describe
|
8
|
+
# and construct a set of Resources, which in turn can generate Requests that
|
9
|
+
# can perform HTTP actions.
|
10
|
+
#
|
11
|
+
# Resources are defined and stored by one of the class methods corresponding
|
12
|
+
# to an HTTP request method. Every Resource is declared with a name that
|
13
|
+
# acts as both a key to access the resource, and, when the class is
|
14
|
+
# instantiated, the name of a dynamically generated instance method.
|
15
|
+
#
|
16
|
+
# Examples
|
17
|
+
#
|
18
|
+
# class GiltSales < Weary::Client
|
19
|
+
# domain "https://api.gilt.com/v1"
|
20
|
+
#
|
21
|
+
# required :apikey
|
22
|
+
#
|
23
|
+
# optional :affid
|
24
|
+
#
|
25
|
+
# get :active, "/sales/active.json"
|
26
|
+
#
|
27
|
+
# get :active_in_store, "/sales/:store/active.json"
|
28
|
+
#
|
29
|
+
# get :upcoming, "/sales/upcoming.json"
|
30
|
+
#
|
31
|
+
# get :upcoming_in_store, "/sales/:store/upcoming.json"
|
32
|
+
#
|
33
|
+
# get :detail, "/sales/:store/:sale_key/detail.json"
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# sales = GiltSales.new
|
37
|
+
# sales.active_in_store :store => :women, :apikey => "my-key"
|
38
|
+
# # => A Weary::Request object
|
39
|
+
#
|
40
|
+
class Client
|
41
|
+
|
42
|
+
# Internal: HTTP Request verbs supported by Weary. These translate to class
|
43
|
+
# methods of Client.
|
44
|
+
REQUEST_METHODS = [
|
45
|
+
:copy, :delete, :get, :head, :lock, :mkcol, :move, :options,
|
46
|
+
:patch, :post, :propfind, :proppatch, :put, :trace, :unlock
|
47
|
+
]
|
48
|
+
|
49
|
+
class << self
|
50
|
+
|
51
|
+
REQUEST_METHODS.each do |request_method|
|
52
|
+
# Generate a resource of the specified REQUEST_METHOD. This
|
53
|
+
# method is defined for each of the REQUEST_METHODS.
|
54
|
+
#
|
55
|
+
# name - A Symbol name or descriptor of a resource to dynamically
|
56
|
+
# generate an instance method of the same name.
|
57
|
+
# path - A String path to the resource. If the class's domain is set it
|
58
|
+
# will be prepended to this path to form the resource's uri.
|
59
|
+
# block - An optional block to be used to further customize the resource.
|
60
|
+
#
|
61
|
+
# Returns a Response object describing an HTTP endpoint.
|
62
|
+
#
|
63
|
+
# Signature
|
64
|
+
#
|
65
|
+
# <method>(name, path, &block)
|
66
|
+
#
|
67
|
+
# method - An HTTP request method (and member of REQUEST_METHODS).
|
68
|
+
define_method request_method do |name, path, &block|
|
69
|
+
resource(name, request_method.to_s.upcase, path, &block)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# An accessor to set the domain where the client's resources are
|
74
|
+
# located. This is prepended to the resources' path to form the resource
|
75
|
+
# uri.
|
76
|
+
#
|
77
|
+
# host - An optional String to set the client domain.
|
78
|
+
#
|
79
|
+
# Returns the domain String.
|
80
|
+
def domain(host=nil)
|
81
|
+
@domain = host unless host.nil?
|
82
|
+
@domain ||= ""
|
83
|
+
end
|
84
|
+
|
85
|
+
# An accessor to set optional parameters permitted by all
|
86
|
+
# resources described by the client.
|
87
|
+
#
|
88
|
+
# params - Zero or more Symbol parameters expected by the resources.
|
89
|
+
#
|
90
|
+
# Returns an Array of parameters.
|
91
|
+
def optional(*params)
|
92
|
+
@optional = params unless params.empty?
|
93
|
+
@optional ||= []
|
94
|
+
end
|
95
|
+
|
96
|
+
# An accessor to set parameters required by the all of the
|
97
|
+
# resources of the client.
|
98
|
+
#
|
99
|
+
# params - Zero or more Symbol parameters required by the resources.
|
100
|
+
#
|
101
|
+
# Returns an Array of parameters.
|
102
|
+
def required(*params)
|
103
|
+
@required = params unless params.empty?
|
104
|
+
@required ||= []
|
105
|
+
end
|
106
|
+
|
107
|
+
# An accessor to set default parameters to be used by the all of
|
108
|
+
# the resources described by the client.
|
109
|
+
#
|
110
|
+
# hash - An optional Hash of key/value pairs describing the
|
111
|
+
# default parameters to be sent to the resources
|
112
|
+
#
|
113
|
+
# Returns a Hash of the default parameters sent to all resources.
|
114
|
+
def defaults(hash=nil)
|
115
|
+
@defaults = hash unless hash.nil?
|
116
|
+
@defaults ||= {}
|
117
|
+
end
|
118
|
+
|
119
|
+
# An accessor to set HTTP request headers for all of the client's
|
120
|
+
# resources.
|
121
|
+
#
|
122
|
+
# hash - An optional Hash of key/value pairs that are sent as HTTP
|
123
|
+
# request headers when a resource's request is performed.
|
124
|
+
#
|
125
|
+
# Returns a Hash of the headers.
|
126
|
+
def headers(hash=nil)
|
127
|
+
@headers = hash unless hash.nil?
|
128
|
+
@headers ||= {}
|
129
|
+
end
|
130
|
+
|
131
|
+
# Send a Rack middleware to all of the requests generated by
|
132
|
+
# the client.
|
133
|
+
#
|
134
|
+
# middleware - An object that implements the rack middleware interface.
|
135
|
+
# args - Zero or more optional arguments to send to the middleware.
|
136
|
+
# block - An optional block to send to the middleware.
|
137
|
+
#
|
138
|
+
# Returns an Array of middlewares.
|
139
|
+
def use(middleware, *args, &block)
|
140
|
+
@middlewares ||= []
|
141
|
+
@middlewares << [middleware, args, block]
|
142
|
+
end
|
143
|
+
|
144
|
+
# Internal: Create and build a resource description of a request. The
|
145
|
+
# resource is then stored in an internal hash, generating an instance
|
146
|
+
# method.
|
147
|
+
#
|
148
|
+
# name - A Symbol name/descriptor of the resource.
|
149
|
+
# method - A Symbol or String of the request method used in the request.
|
150
|
+
# path - A String path to the resource that is appended to the domain
|
151
|
+
# to form the uri.
|
152
|
+
#
|
153
|
+
# Yields the Resource for further construction.
|
154
|
+
#
|
155
|
+
# Returns the generated Resource.
|
156
|
+
def resource(name, method, path="")
|
157
|
+
resource = Weary::Resource.new method, "#{domain}#{path}"
|
158
|
+
resource.optional *optional
|
159
|
+
resource.required *required
|
160
|
+
resource.defaults defaults
|
161
|
+
resource.headers headers
|
162
|
+
if !@middlewares.nil? && !@middlewares.empty?
|
163
|
+
@middlewares.each {|middleware| resource.use *middleware }
|
164
|
+
end
|
165
|
+
yield resource if block_given?
|
166
|
+
self[name] = resource
|
167
|
+
end
|
168
|
+
|
169
|
+
# A getter for the stored table of Resources.
|
170
|
+
#
|
171
|
+
# Returns a Hash of Resources stored by their name keys.
|
172
|
+
def resources
|
173
|
+
@resources ||= {}
|
174
|
+
end
|
175
|
+
|
176
|
+
# Store a Resource at the given key. A method is built for the
|
177
|
+
# instances with the same name as the key that calls the request method
|
178
|
+
# of the Resource.
|
179
|
+
#
|
180
|
+
# name - A Symbol name of the Resource and the eventual name of the
|
181
|
+
# method that will build the request.
|
182
|
+
# resource - The Resource object to store. When the named method is
|
183
|
+
# called on the client instance, this resource's request
|
184
|
+
# method will be called.
|
185
|
+
#
|
186
|
+
# Returns the stored Resource.
|
187
|
+
def []=(name,resource)
|
188
|
+
store name, resource
|
189
|
+
end
|
190
|
+
|
191
|
+
# A quick getter to retrieve a Resource from the client's
|
192
|
+
# internal store.
|
193
|
+
#
|
194
|
+
# name - The Symbol name of the Resource.
|
195
|
+
#
|
196
|
+
# Returns the Resource stored at name.
|
197
|
+
def [](name)
|
198
|
+
resources[name]
|
199
|
+
end
|
200
|
+
|
201
|
+
# Internal: Build a Rack router for the client's resources.
|
202
|
+
#
|
203
|
+
# Returns a Route object of the resources at the domain.
|
204
|
+
def route
|
205
|
+
Weary::Route.new resources.values, domain
|
206
|
+
end
|
207
|
+
|
208
|
+
# A Rack middleware interface that uses the internal router to
|
209
|
+
# determine the best Resource available.
|
210
|
+
#
|
211
|
+
# Returns an Array resembling a Rack response tuple.
|
212
|
+
def call(env)
|
213
|
+
route.call(env)
|
214
|
+
end
|
215
|
+
|
216
|
+
private
|
217
|
+
|
218
|
+
# Private: Store the resource in a hash keyed by the name (as a Symbol).
|
219
|
+
#
|
220
|
+
# Returns the saved Resource.
|
221
|
+
# Raises ArgumentError if you try to store anything but a Resource.
|
222
|
+
def store(name, resource)
|
223
|
+
raise ArgumentError, "Expected a Weary::Resource but got #{resource.inspect}" \
|
224
|
+
unless resource.is_a? Weary::Resource
|
225
|
+
key = name.to_sym
|
226
|
+
build_method(key, resource)
|
227
|
+
resources[key] = resource
|
228
|
+
end
|
229
|
+
|
230
|
+
# Private: Do the metaprogramming necessary for the resource names to
|
231
|
+
# become instance methods.
|
232
|
+
def build_method(key, resource)
|
233
|
+
define_method(key) do |*parameters, &block|
|
234
|
+
parameters = parameters.first || {}
|
235
|
+
@defaults ||= {}
|
236
|
+
request = resource.request(@defaults.merge(parameters), &block)
|
237
|
+
request
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
243
|
+
end
|