6
\$\begingroup\$

I have two very similar methods, which make HTTP requests, the difference is that one makes PUT and another GET. Is there any proper Ruby way not to repeat the setup code and not to pass the flag parameter?

def notify_client(url, params)
 uri = URI.parse(url)
 https = Net::HTTP.new(uri.host, uri.port)
 https.use_ssl = !Rails.env.development?
 req = Net::HTTP::Patch.new(uri.path)
 req.body = {data: {attributes: params}}.to_json
 res = https.request(req)
 puts "Response #{res.code} #{res.message}: #{res.body}"
end
def notify_vendor(url, params)
 uri = URI.parse(url)
 https = Net::HTTP.new(uri.host, uri.port)
 https.use_ssl = !Rails.env.development?
 req = Net::HTTP::Get.new(uri.path)
 req.body = {data: {attributes: params}}.to_json
 res = https.request(req)
 puts "Response #{res.code} #{res.message}: #{res.body}"
end
200_success
145k22 gold badges190 silver badges478 bronze badges
asked May 26, 2016 at 12:48
\$\endgroup\$
5
  • \$\begingroup\$ Do you mean PUT or PATCH? What flag parameter are you talking about? \$\endgroup\$ Commented May 26, 2016 at 14:13
  • \$\begingroup\$ One method makes GET and other PUT request the rest of the code is similar and I wanted to differentiate then somehow \$\endgroup\$ Commented May 26, 2016 at 14:22
  • \$\begingroup\$ Then why does your code say Net::HTTP::Patch? \$\endgroup\$ Commented May 26, 2016 at 14:24
  • \$\begingroup\$ My mistake but that's not relevant to what I was asking \$\endgroup\$ Commented May 26, 2016 at 14:36
  • \$\begingroup\$ Every detail is relevant in a code review, because every detail matters when running code. \$\endgroup\$ Commented May 26, 2016 at 14:38

4 Answers 4

6
\$\begingroup\$

In such cases I try to move repeated code to separate method that accepts block.

def notify_client(url, params)
 notify(url, params) { |path| Net::HTTP::Patch.new(path) }
end
def notify_vendor(url, params)
 notify(url, params) { |path| Net::HTTP::Get.new(path) }
end
def notify(url, params)
 uri = URI.parse(url)
 https = Net::HTTP.new(uri.host, uri.port)
 https.use_ssl = !Rails.env.development?
 req = yield uri.path
 req.body = {data: {attributes: params}}.to_json
 res = https.request(req)
 puts "Response #{res.code} #{res.message}: #{res.body}"
end

That code refactoring quite simple. If you need more details - let me know.

answered May 26, 2016 at 13:09
\$\endgroup\$
0
2
\$\begingroup\$

I like Sergii's solution a lot, but just as an extra idea, this is also possible:

%w(vendor client).each do |subject|
 define_method("notify_#{subject}") do |url, params|
 uri = URI.parse(url)
 https = Net::HTTP.new(uri.host, uri.port)
 https.use_ssl = !Rails.env.development?
 req = (subject == 'client' ? Net::HTTP::Patch.new(uri.path) : Net::HTTP::Get.new(uri.path))
 req.body = {data: {attributes: params}}.to_json
 res = https.request(req)
 puts "Response #{res.code} #{res.message}: #{res.body}"
 end
end

You create two methods dynamically. Again, not as elegant as Sergii's but this will still work.

answered Jun 2, 2016 at 10:29
\$\endgroup\$
0
1
\$\begingroup\$

One way is a block, already shown, the other sending a string/symbol to select the method:

def request(url, params, method)
 uri = URI.parse(url)
 https = Net::HTTP.new(uri.host, uri.port)
 https.use_ssl = !Rails.env.development?
 class_obj = Net::HTTP.const_get(method.to_s.capitalize)
 req = class_obj.new(uri.path)
 req.body = {data: {attributes: params}}.to_json
 res = https.request(req)
 puts "Response #{res.code} #{res.message}: #{res.body}"
end

In any case, using net/http is slightly masochistic, there are more friendly libraries, for example rest-client:

response = RestClient::Request.execute(method: method, url: url, params: params)
answered May 26, 2016 at 20:51
\$\endgroup\$
0
\$\begingroup\$

You could inject the class for constructing the request, but this would break the abstraction, as the caller would have to know that we're using Net::HTTP. You could pass the HTTP verb as a symbol instead and look up the corresponding class like this:

METHOD_CLASS = {get: Net::HTTP::Get, patch: Net::HTTP::Patch}
def notify(url, params, method)
 uri = URI.parse(url)
 https = Net::HTTP.new(uri.host, uri.port)
 https.use_ssl = !Rails.env.development?
 req = METHOD_CLASS.fetch(method).new(uri.path)
 req.body = {data: {attributes: params}}.to_json
 res = https.request(req)
 puts "Response #{res.code} #{res.message}: #{res.body}"
end

I'd use a block instead for evaluating the response, printing the response shouldn't be a responsibility of this method. Depending on your use case you might want to yield the response to the block only if the request was successful or if it failed.

answered May 26, 2016 at 19:03
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.