I have a little sinatra api that I'm trying to beautify. Most of my routes are simple db operations but a few involve calling an external service before doing db operations. In all cases most of the code is the same except for how I respond to the service response. Is there any slick functional programming approach?
Here's an example of one of these routes:
get '/update_x' do
validateParams(params,:x)
xid = params[:x]
xName = getNameFromId(xid)
if xName
# Make request to proxy service
rid = generateRandomHexNumber(16) # generate requestId
params['m'] = 'set'
params['rid'] = rid
json = "{}"
begin
response = #resource["/"+"?rid=#{rid}&id=#{xid}&json=#{json}"].get
status = response.code
body = response.body
parsed_json = JSON(body)
if parsed_json['response'] and parsed_json['response']['success'] and parsed_json['response']['success']=='false'
msg = {:success => "false", :response => "unknown error"}
if parsed_json['response']['response']
msg = {:success => "false", :response => parsed_json['response']['response']}
end
content_type :json
msg.to_json
else
#### Here is stuff specific to this api call
updateDBHelper(xid,buildUpdateOptions(params))
params['ss_status'] = status
content_type :json
params.to_json
#### End specific to api call
end
rescue Exception=>e
params['ss_status'] = status
params['exception'] = e
content_type :json
params.to_json
end
else
msg = {:success => "false", :response => "Not found"}
content_type :json
msg.to_json
end
end
In general if you have a general pattern with some arbitrary code that changes every time then the simplest thing would be accepting a block with those customizations.
def make_api_request(some, params)
# do what you need to do
yield(variables, that, your_custom_code, needs)
# do some more, maybe cleanup
end
get '/some_route' do
make_api_request do |variables, that, your_custom_code, needs|
# do custom stuff here
end
end
Related
My code creates a github gist using the github API. After creating the gist, the API returns a status code to me. If the code is "201" the gist was created. But if it is "400" there was an error. In my code the variable that saves that state is response_status
This is my code:
require 'net/http'
require 'json'
require 'uri'
class Gist
attr_reader :response_status
attr_reader :try_again
def initialize(filename,description,state,content)
#filename = filename
#description = description
#state = state
#content = content
end
def post(uri, request)
request.basic_auth("my_username", "my_token")
request.body = JSON.dump({
"description" => #description,
"public" => #state,
"files" => {
#filename => {
"content" => #content
}
}
})
req_options = { use_ssl: uri.scheme == "https" }
begin
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
http.request(request)
end
json = response.body
parsed = JSON.parse(json)
#response_status = "#{ response.code }"
if #response_status == "201"
puts "Tu gist se ha creado con exito. La URL de su gist es: "+ parsed["url"]
end
rescue SocketError => se
puts "Ha ocurrido un error de conexión. Quiere intentar nuevamente?"
#try_again = gets.chomp.capitalize
end
end
end
loop do
filename= gets.chomp
if File.exist?("#{filename}")
description = gets.chomp
state = gets.chomp.capitalize
if state == "Si"
state = true;
elsif state == "No"
state = false;
end
open(filename, "r") { |file| #contenido = file.read() }
content = #contenido
uri = URI.parse("https://api.github.com/gists")
request = Net::HTTP::Post.new(uri)
gist = Gist.new(filename,description,state,content)
gist.post(uri, request)
break if gist.response_status == "201"
break if gist.try_again == "No"
else
puts "The file does not exist"
continue = gets.chomp.capitalize
break if continue == "No"
end
end
I want to test test cases using rspec, but I did not understand.
It occurred to me as a test case that a Gist be created. For example, I thought about checking if the variable that returns the state of the Gist is equal to "201", but that didn't work for me.
This is my rspec file:
require './git_clases.rb'
RSpec.describe Gist do
describe "#Gist" do
it "#Gist created" do
expect(response_status)==("201")
end
end
end
response_status is a method on your Gist object. You need to create a Gist object, call post on it, and check response_status on your object.
RSpec.describe Gist do
# Generally organize by method
describe "#post" do
# Example descriptions read grammatically correct
# "Gist #post creates a gist"
it "creates a gist" do
filename = "test.txt"
description = "testing gist"
state = "dunno what goes here"
content = "some test content"
gist = described_class.new(filename, description, state, content)
uri = URI.parse("https://api.github.com/gists")
request = Net::HTTP::Post.new(uri)
gist.post(uri, request)
expect(gist.response_status).to eq 201
end
end
end
described_class is Gist from RSpec.describe Gist. It's preferable to hard coding the class name into the example in case that example becomes shared or the class name changes.
expect(gist.response_status).to eq 201 is really expect(gist.response_status).to( eq(201) ). This compares gist.response_status using the matcher eq(201) which just checks that it equals 201. This might seem a bit much for a simple equality check, but it allows rspec to provide a wide variety of complex matchers as well as custom ones.
Your post method is a bit odd in that the user is expected to initialize and pass in the URL and Request object. That should probably be done by Gist.
There is no need to turn the response_status into a string. It's preferable to leave it as an Integer. If you do need it as a string, use response.code.to_s.
I am able to parse a JSON using the following code
$httpresult = #params['body']
$jsonresult = Rho::JSON.parse($httpresult)
But I don't know how to create a model from $jsonresult.
First, using app_info you can print the result coming from the server to check if the response is valid JSON string.
Second, i think you must decode the url in order to parse it by using:
Rho::JSON.parse(Rho::RhoSupport.url_decode(#params['body']))
Once you've the data in json_result, you can put them in a pre-existing Model.
Supposing that you've already created a model with the name "Product", you can use transactions to speed up the process.
At the beginning of your module you've to require the model name:
require_source 'Product'
Then you can do this callback:
def get_callback
if #params['status'] == "ok"
json_result = Rho::JSON.parse(#params['body'])
db = ::Rho::RHO.get_src_db('Product')
db.start_transaction
Product.delete_all
begin
json_result.each do |item|
Product.create({:Brand => item["B rand"], :Name => item["Name"], :SKU => d["SKU"]})
end
db.commit
rescue Exception => e
trace_msg = e.backtrace.join("\n")
puts 'Application initialize failed: ' + e.inspect + ";Trace: #{trace_msg}"
db.rollback
end
WebView.navigate Rho::RhoConfig.start_path
else
WebView.navigate url_for :action => :show_error
end
end
I need to post using three parameters and a body which consists of 512 bytes. I can get the body right but I can't seem to get the parameters to take:
require 'net/http'
#ip_address = Array['cueserver.dnsalias.com']
#cueserver = 0
#playback = 'p1'
def send_cuescript(data)
params = {'id' => '1', 'type' => "20",'dst' => 'RES' }
begin
url = URI.parse('http://'+ #ip_address[#cueserver] + '/set.cgi')
http = Net::HTTP.new(url.host, url.port)
response, body = http.post(url.path, params, data)
rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
end
response_array = []
puts 'got this value: ' + response.to_s
response.body.each_byte { |e| response_array.push(e.to_s(16))}
end
data_array = Array.new(512, "\x80")
send_cuescript(data_array.join)
I am getting an error from the initialize_http_header. I know there must be a way to set the parameters and the body separately but I can't seem to find any reference to this.
Why do you have to send part of the params in the url and part of it in the body?
If you have to do this, try
url = URI.parse('http://'+ #ip_address[#cueserver] + '/set.cgi?' + params.to_param)
PS: to_param is from active support. You need to write your own if you are not using active support.
I'm having trouble getting parameters passed in an HTTP Put call, using ruby. Take a look at the "put_data" variable.
When I leave it as a hash, ruby says:
undefined method `bytesize' for #<Hash:0x007fbf41a109e8>
if I convert to a string, I get:
can't convert Net::HTTPUnauthorized into String
I've also tried doing just - '?token=wBsB16NSrfVDpZPoEpM'
def process_activation
uri = URI("http://localhost:3000/api/v1/activation/" + self.member_card_num)
Net::HTTP.start(uri.host, uri.port) do |http|
headers = {'Content-Type' => 'text/plain; charset=utf-8'}
put_data = {:token => "wBsB16NSrfVDpZPoEpM"}
response = http.send_request('PUT', uri.request_uri, put_data, headers)
result = JSON.parse(response)
end
if result['card']['state']['state'] == "active"
return true
else
return false
end
end
I've searched all around, including rubydocs, but can't find an example of how to encode parameters. Any help would be appreciated.
Don't waste your time with NET::HTTP. I used 'rest-client' and had this thing done in minutes...
def process_activation
response = RestClient.put 'http://localhost:3000/api/v1/card_activation/'+ self.member_card_num, :token => "wBsB1pjJNNfiK6NSrfVDpZPoEpM"
result = JSON.parse(response)
return result['card']['state']['state'] == "active"
end
How can I send HTTP GET request with parameters via ruby?
I have tried a lot of examples but all of those failed.
I know this post is old but for the sake of those brought here by google, there is an easier way to encode your parameters in a URL safe manner. I'm not sure why I haven't seen this elsewhere as the method is documented on the Net::HTTP page. I have seen the method described by Arsen7 as the accepted answer on several other questions also.
Mentioned in the Net::HTTP documentation is URI.encode_www_form(params):
# Lets say we have a path and params that look like this:
path = "/search"
params = {q: => "answer"}
# Example 1: Replacing the #path_with_params method from Arsen7
def path_with_params(path, params)
encoded_params = URI.encode_www_form(params)
[path, encoded_params].join("?")
end
# Example 2: A shortcut for the entire example by Arsen7
uri = URI.parse("http://localhost.com" + path)
uri.query = URI.encode_www_form(params)
response = Net::HTTP.get_response(uri)
Which example you choose is very much dependent on your use case. In my current project I am using a method similar to the one recommended by Arsen7 along with the simpler #path_with_params method and without the block format.
# Simplified example implementation without response
# decoding or error handling.
require "net/http"
require "uri"
class Connection
VERB_MAP = {
:get => Net::HTTP::Get,
:post => Net::HTTP::Post,
:put => Net::HTTP::Put,
:delete => Net::HTTP::Delete
}
API_ENDPOINT = "http://dev.random.com"
attr_reader :http
def initialize(endpoint = API_ENDPOINT)
uri = URI.parse(endpoint)
#http = Net::HTTP.new(uri.host, uri.port)
end
def request(method, path, params)
case method
when :get
full_path = path_with_params(path, params)
request = VERB_MAP[method].new(full_path)
else
request = VERB_MAP[method].new(path)
request.set_form_data(params)
end
http.request(request)
end
private
def path_with_params(path, params)
encoded_params = URI.encode_www_form(params)
[path, encoded_params].join("?")
end
end
con = Connection.new
con.request(:post, "/account", {:email => "test#test.com"})
=> #<Net::HTTPCreated 201 Created readbody=true>
I assume that you understand the examples on the Net::HTTP documentation page but you do not know how to pass parameters to the GET request.
You just append the parameters to the requested address, in exactly the same way you type such address in the browser:
require 'net/http'
res = Net::HTTP.start('localhost', 3000) do |http|
http.get('/users?id=1')
end
puts res.body
If you need some generic way to build the parameters string from a hash, you may create a helper like this:
require 'cgi'
def path_with_params(page, params)
return page if params.empty?
page + "?" + params.map {|k,v| CGI.escape(k.to_s)+'='+CGI.escape(v.to_s) }.join("&")
end
path_with_params("/users", :id => 1, :name => "John&Sons")
# => "/users?name=John%26Sons&id=1"