Module: ClientApiBuilder::Router::ClassMethods

Defined in:
lib/client_api_builder/router.rb

Constant Summary collapse

REQUIRED_BODY_HTTP_METHODS =
[
  :post,
  :put,
  :patch
]
@@namespaces =
[]

Instance Method Summary collapse

Instance Method Details

#add_response_proc(method_name, proc) ⇒ Object

tracks the proc used to handle responses



38
39
40
41
42
# File 'lib/client_api_builder/router.rb', line 38

def add_response_proc(method_name, proc)
  response_procs = default_options[:response_procs].dup
  response_procs[method_name] = proc
  add_value_to_class_method(:default_options, response_procs: response_procs)
end

#auto_detect_http_method(method_name) ⇒ Object



144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/client_api_builder/router.rb', line 144

def auto_detect_http_method(method_name)
  case method_name.to_s
  when /^(?:post|create|add|insert)/i
    :post
  when /^(?:put|update|modify|change)/i
    :put
  when /^(?:patch)/i
    :patch
  when /^(?:delete|remove)/i
    :delete
  else
    :get
  end
end

#base_url(url = nil) ⇒ Object

set/get base url



50
51
52
53
54
# File 'lib/client_api_builder/router.rb', line 50

def base_url(url = nil)
  return default_options[:base_url] unless url

  add_value_to_class_method(:default_options, base_url: url)
end

#body_builder(builder = nil, &block) ⇒ Object

set the builder to :to_json, :to_query, :query_params or specify a proc to handle building the request body payload or get the body builder



58
59
60
61
62
# File 'lib/client_api_builder/router.rb', line 58

def body_builder(builder = nil, &block)
  return default_options[:body_builder] if builder.nil? && block.nil?

  add_value_to_class_method(:default_options, body_builder: builder || block)
end

#build_body(router, body) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/client_api_builder/router.rb', line 116

def build_body(router, body)
  case body_builder
  when :to_json
    body.to_json
  when :to_query
    body.to_query
  when :query_params
    ClientApiBuilder::QueryParams.new.to_query(body)
  when Symbol
    router.send(body_builder, body)
  else
    router.instance_exec(body, &body_builder)
  end
end

#build_query(router, query) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/client_api_builder/router.rb', line 131

def build_query(router, query)
  case query_builder
  when :to_query
    query.to_query
  when :query_params
    ClientApiBuilder::QueryParams.new.to_query(query)
  when Symbol
    router.send(query_builder, query)
  else
    router.instance_exec(query, &query_builder)
  end
end

#configure_retries(max_retries, sleep_time_between_retries_in_seconds = 0.05) ⇒ Object



86
87
88
89
90
91
92
# File 'lib/client_api_builder/router.rb', line 86

def configure_retries(max_retries, sleep_time_between_retries_in_seconds = 0.05)
  add_value_to_class_method(
    :default_options,
    max_retries: max_retries,
    sleep: sleep_time_between_retries_in_seconds
  )
end

#connection_option(name, value) ⇒ Object

set a connection_option, specific to Net::HTTP



80
81
82
83
84
# File 'lib/client_api_builder/router.rb', line 80

def connection_option(name, value)
  connection_options = default_options[:connection_options].dup
  connection_options[name] = value
  add_value_to_class_method(:default_options, connection_options: connection_options)
end

#default_connection_optionsObject

get configured connection_options



107
108
109
# File 'lib/client_api_builder/router.rb', line 107

def default_connection_options
  default_options[:connection_options]
end

#default_headersObject

get default headers



102
103
104
# File 'lib/client_api_builder/router.rb', line 102

def default_headers
  default_options[:headers]
end

#default_optionsObject



23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/client_api_builder/router.rb', line 23

def default_options
  {
    base_url: nil,
    body_builder: :to_json,
    connection_options: {},
    headers: {},
    query_builder: Hash.method_defined?(:to_query) ? :to_query : :query_params,
    query_params: {},
    response_procs: {},
    max_retries: 1,
    sleep: 0.05
  }.freeze
end

#default_query_paramsObject

get default query_params to add to all requests



112
113
114
# File 'lib/client_api_builder/router.rb', line 112

def default_query_params
  default_options[:query_params]
end

#generate_route_code(method_name, path, options = {}) ⇒ Object



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/client_api_builder/router.rb', line 230

def generate_route_code(method_name, path, options = {})
  http_method = options[:method] || auto_detect_http_method(method_name)

  path = namespaces.join + path

  # instance method
  path.gsub!(/\{([a-z0-9_]+)\}/i) do |_|
    get_instance_method($1)
  end

  path_arguments = []
  path.gsub!(/:([a-z0-9_]+)/i) do |_|
    path_arguments << $1
    "#\{escape_path(#{$1})\}"
  end

  has_body_param = options[:body].nil? && requires_body?(http_method, options)

  query =
    if options[:query]
      query_arguments = get_arguments(options[:query])
      str = options[:query].inspect
      str.gsub!(/"__\|\|(.+?)\|\|__"/) { $1 }
      str
    else
      query_arguments = []
      'nil'
    end

  body =
    if options[:body]
      has_body_param = false
      body_arguments = get_arguments(options[:body])
      str = options[:body].inspect
      str.gsub!(/"__\|\|(.+?)\|\|__"/) { $1 }
      str
    else
      body_arguments = []
      has_body_param ? 'body' : 'nil'
    end

  query_arguments.map!(&:to_s)
  body_arguments.map!(&:to_s)
  named_arguments = path_arguments + query_arguments + body_arguments
  named_arguments.uniq!

  expected_response_codes =
    if options[:expected_response_codes]
      options[:expected_response_codes]
    elsif options[:expected_response_code]
      [options[:expected_response_code]]
    else
      []
    end
  expected_response_codes.map!(&:to_s)

  stream_param =
    case options[:stream]
    when true,
         :file
      :file
    when :io
      :io
    end

  method_args = named_arguments.map { |arg_name| "#{arg_name}:" }
  method_args += ['body:'] if has_body_param
  method_args += ["#{stream_param}:"] if stream_param
  method_args += ['**__options__', '&block']

  code = "def #{method_name}_raw_response(" + method_args.join(', ') + ")\n"
  code += "  __path__ = \"#{path}\"\n"
  code += "  __query__ = #{query}\n"
  code += "  __body__ = #{body}\n"
  code += "  __uri__ = build_uri(__path__, __query__, __options__)\n"
  code += "  __body__ = build_body(__body__, __options__)\n"
  code += "  __headers__ = build_headers(__options__)\n"
  code += "  __connection_options__ = build_connection_options(__options__)\n"
  code += "  @request_options = {method: #{http_method.inspect}, uri: __uri__, body: __body__, headers: __headers__, connection_options: __connection_options__}\n"
  code += "  @request_options[:#{stream_param}] = #{stream_param}\n" if stream_param

  case options[:stream]
  when true,
       :file
    code += "  @response = stream_to_file(**@request_options)\n"
  when :io
    code += "  @response = stream_to_io(**@request_options)\n"
  when :block
    code += "  @response = stream(**@request_options, &block)\n"
  else
    code += "  @response = request(**@request_options)\n"
  end
  code += "end\n"
  code += "\n"

  code += "def #{method_name}(" + method_args.join(', ') + ")\n"
  code += "  request_wrapper(__options__) do\n"
  code += "    block ||= self.class.get_response_proc(#{method_name.inspect})\n"
  code += "    __expected_response_codes__ = #{expected_response_codes.inspect}\n"
  code += "    #{method_name}_raw_response(" + method_args.map { |a| a =~ /:$/ ? "#{a} #{a.sub(':', '')}" : a }.join(', ') + ")\n"
  code += "    expected_response_code!(@response, __expected_response_codes__, __options__)\n"

  if options[:stream] || options[:return] == :response
    code += "    @response\n"
  elsif options[:return] == :body
    code += "    @response.body\n"
  else
    code += "    handle_response(@response, __options__, &block)\n"
  end

  code += "  end\n"
  code += "end\n"
  code
end

#get_arguments(value) ⇒ Object

returns a list of arguments to add to the route method



203
204
205
206
207
208
209
210
211
212
# File 'lib/client_api_builder/router.rb', line 203

def get_arguments(value)
  case value
  when Hash
    get_hash_arguments(value)
  when Array
    get_array_arguments(value)
  else
    []
  end
end

#get_array_arguments(list) ⇒ Object



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/client_api_builder/router.rb', line 184

def get_array_arguments(list)
  arguments = []
  list.each_with_index do |v, idx|
    case v
    when Symbol
      list[idx] = "__||#{v}||__"
      arguments << v
    when Hash
      arguments += get_hash_arguments(v)
    when Array
      arguments += get_array_arguments(v)
    when String
      list[idx] = "__||#{$1}||__" if v =~ /\{([a-z0-9_]+)\}/i
    end
  end
  arguments
end

#get_hash_arguments(hsh) ⇒ Object



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/client_api_builder/router.rb', line 166

def get_hash_arguments(hsh)
  arguments = []
  hsh.each do |k, v|
    case v
    when Symbol
      hsh[k] = "__||#{v}||__"
      arguments << v
    when Hash
      arguments += get_hash_arguments(v)
    when Array
      arguments += get_array_arguments(v)
    when String
      hsh[k] = "__||#{$1}||__" if v =~ /\{([a-z0-9_]+)\}/i
    end
  end
  arguments
end

#get_instance_method(var) ⇒ Object



214
215
216
# File 'lib/client_api_builder/router.rb', line 214

def get_instance_method(var)
   "#\{escape_path(#{var})\}"
end

#get_response_proc(method_name) ⇒ Object

retrieves the proc used to handle the response



45
46
47
# File 'lib/client_api_builder/router.rb', line 45

def get_response_proc(method_name)
  default_options[:response_procs][method_name]
end

#header(name, value = nil, &block) ⇒ Object

add a request header



73
74
75
76
77
# File 'lib/client_api_builder/router.rb', line 73

def header(name, value = nil, &block)
  headers = default_options[:headers].dup
  headers[name] = value || block
  add_value_to_class_method(:default_options, headers: headers)
end

#namespace(name) ⇒ Object

a namespace is a top level path to apply to all routes within the namespace block



224
225
226
227
228
# File 'lib/client_api_builder/router.rb', line 224

def namespace(name)
  namespaces << name
  yield
  namespaces.pop
end

#namespacesObject



219
220
221
# File 'lib/client_api_builder/router.rb', line 219

def namespaces
  @@namespaces
end

#query_builder(builder = nil, &block) ⇒ Object

set the builder to :to_query, :query_params or specify a proc to handle building the request query params or get the query builder



66
67
68
69
70
# File 'lib/client_api_builder/router.rb', line 66

def query_builder(builder = nil, &block)
  return default_options[:query_builder] if builder.nil? && block.nil?

  add_value_to_class_method(:default_options, query_builder: builder || block)
end

#query_param(name, value = nil, &block) ⇒ Object

add a query param to all requests



95
96
97
98
99
# File 'lib/client_api_builder/router.rb', line 95

def query_param(name, value = nil, &block)
  query_params = default_options[:query_params].dup
  query_params[name] = value || block
  add_value_to_class_method(:default_options, query_params: query_params)
end

#requires_body?(http_method, options) ⇒ Boolean

Returns:

  • (Boolean)


159
160
161
162
163
164
# File 'lib/client_api_builder/router.rb', line 159

def requires_body?(http_method, options)
  return !options[:no_body] if options.key?(:no_body)
  return options[:has_body] if options.key?(:has_body)

  REQUIRED_BODY_HTTP_METHODS.include?(http_method)
end

#route(method_name, path, options = {}, &block) ⇒ Object



345
346
347
348
349
# File 'lib/client_api_builder/router.rb', line 345

def route(method_name, path, options = {}, &block)
  add_response_proc(method_name, block) if block

  self.class_eval generate_route_code(method_name, path, options), __FILE__, __LINE__
end