How can I extend a module, override a method, and still call the overridden method? - ruby

I'd like to use URI in this way:
require 'open-uri'
uri = URI.parse('http://subdomain.domain.com/section/page.html')
puts uri.first_level_domain # => 'domain.com'
How can I do that?
I'm trying:
module URI
def parse
ret = super
domain = ret.host.split('.').last(2).join('.')
ret.send(:define_method, :first_level_domain, lambda { domain })
ret
end
end
but I get undefined method 'first_level_domain' for #<URI::HTTP:0x9bc7ab0> (NoMethodError)

Why something so complicated ? You could something like this
module URI
def first_level_domain
host.split('.').last(2).join('.')
end
end
uri = URI.parse('http://subdomain.domain.com/section/page.html')
uri.first_level_domain
# => "domain.com"

Related

How To change a URI object's path attribute?

I want to call an API using only slightly different URIs. This code snippet does what I want, but I want to know if there is a more efficient way to do it.
require 'net/http'
require 'json'
# These URIs differ only in the path
orders = URI('https://hft-api.lykke.com/api/Orders?orderType=Unknown')
asset_pairs = URI('https://hft-api.lykke.com/api/AssetPairs')
lykke_req = Net::HTTP::Get.new(orders)
lykke_req['User-Agent'] = 'curl/7.67.0'
lykke_req['api-key'] = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
lykke_req['Accept'] = 'application/json'
response = Net::HTTP.start(orders.hostname,
orders.port,
:use_ssl => true) {|http| http.request(lykke_req)}
puts JSON.parse(response.body)
lykke_req = Net::HTTP::Get.new(asset_pairs)
lykke_req['User-Agent'] = 'curl/7.67.0'
lykke_req['Accept'] = 'application/json'
response = Net::HTTP.start(asset_pairs.hostname,
asset_pairs.port,
:use_ssl => true) {|http| http.request(lykke_req)}
puts JSON.parse(response.body)
All I do is to reuse the same code but with a slightly different URI.
For my lykke_req objects, I can write
puts lykke_req
puts lykke_req.path
which gives me
#<Net::HTTP::Get:0x00007f947f1fdce8>
/api/Orders?orderType=Unknown
So it seems to me all i have to do is change the value of lykke_req.path. But I can't work out how to do it. I am looking for something like this
lykke_req.path = "/api/AssetPairs"
which fails with
undefined method `path=' for #<Net::HTTP::Get GET> (NoMethodError)
I found this on the official documentation page, but I can't find out what [R] means. Does it mean read only? Do I really have to go through the hassle of creating a new URI object, then creating a new Net::HTTP::Get object each time?
path [R]
The problem here is that you're trying to alter the net request object instead of the uri object:
irb(main):001:0> uri = URI('https://hft-api.lykke.com/api/Orders?orderType=Unknown')
=> #<URI::HTTPS https://hft-api.lykke.com/api/Orders?orderType=Unknown>
irb(main):002:0> uri.path = '/foo'
=> "/foo"
irb(main):003:0> uri.to_s
=> "https://hft-api.lykke.com/foo?orderType=Unknown"
But I would really just wrap this in a class so that you can encapsulate and structure your code and avoid duplication:
class LykkeAPIClient
BASE_URI = URI('https://hft-api.lykke.com/api')
def initalize(api_key:)
#api_key = api_key
end
def get_orders
get '/Orders?orderType=Unknown'
end
def get_asset_pairs
get '/AssetPairs'
end
def get(path)
req = Net::HTTP::Get.new(BASE_URI.join(path))
req['User-Agent'] = 'curl/7.67.0'
req['Accept'] = 'application/json'
req['api-key'] = #api_key
response = Net::HTTP.start(req.hostname, req.port, use_ssl: true) do |http|
http.request(uri)
end
# #todo check response status!
JSON.parse(response.body)
end
end
#client = LykkeAPIClient.new(api_key: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
#orders = #client.get_orders
Insstead of lykke_req.path= do lykke_req.uri.path=
https://ruby-doc.org/stdlib-2.6.5/libdoc/net/http/rdoc/Net/HTTPGenericRequest.html

API integration error HTTParty

I'm learning how to work with HTTParty and API and I'm having an issue with my code.
Users/admin/.rbenv/versions/2.0.0-p481/lib/ruby/2.0.0/uri/generic.rb:214:in `initialize': the scheme http does not accept registry part: :80 (or bad hostname?)
I've tried using debug_output STDOUT both as an argument to my method and after including HTTParty to have a clue but with no success. Nothing gets displayed:
require 'httparty'
class LolObserver
include HTTParty
default_timeout(1) #timeout after 1 second
attr_reader :api_key, :playerid
attr_accessor :region
def initialize(region,playerid,apikey)
#region = region_server(region)
#playerid = playerid
#api_key = apikey
end
def region_server(region)
case region
when "euw"
self.class.base_uri "https://euw.api.pvp.net"
self.region = "EUW1"
when "na"
self.class.base_uri "https://na.api.pvp.net"
self.region = "NA1"
end
end
def handle_timeouts
begin
yield
#Timeout::Error, is raised if a chunk of the response cannot be read within the read_timeout.
#Timeout::Error, is raised if a connection cannot be created within the open_timeout.
rescue Net::OpenTimeout, Net::ReadTimeout
#todo
end
end
def base_path
"/observer-mode/rest/consumer/getSpectatorGameInfo"
end
def current_game_info
handle_timeouts do
url = "#{ base_path }/#{region}/#{playerid}?api_key=#{api_key}"
puts '------------------------------'
puts url
HTTParty.get(url,:debug_output => $stdout)
end
end
end
I verified my URL which is fine so I'm lost as to where the problem is coming from.
I tested with a static base_uri and it doesn't change anything.
The odd thing is when I do:
HTTParty.get("https://euw.api.pvp.net/observer-mode/rest/consumer/getSpectatorGameInfo/EUW1/randomid?api_key=myapikey")
Everything is working fine and I'm getting a response.
HTTParty doesn't seem to like the way you set your base_uri.
Unless you need it to be like that just add another attr_reader called domain and it will work.
require 'httparty'
class LolObserver
include HTTParty
default_timeout(1) #timeout after 1 second
attr_reader :api_key, :playerid, :domain
attr_accessor :region
def initialize(region,playerid,apikey)
#region = region_server(region)
#playerid = playerid
#api_key = apikey
end
def region_server(region)
case region
when "euw"
#domain = "https://euw.api.pvp.net"
self.region = "EUW1"
when "na"
#domain = "https://na.api.pvp.net"
self.region = "NA1"
end
end
def handle_timeouts
begin
yield
#Timeout::Error, is raised if a chunk of the response cannot be read within the read_timeout.
#Timeout::Error, is raised if a connection cannot be created within the open_timeout.
rescue Net::OpenTimeout, Net::ReadTimeout
#todo
end
end
def base_path
"/observer-mode/rest/consumer/getSpectatorGameInfo"
end
def current_game_info
handle_timeouts do
url = "#{domain}/#{ base_path }/#{region}/#{playerid}?api_key=#{api_key}"
puts '------------------------------'
puts url
HTTParty.get(url,:debug_output => $stdout)
end
end
end

Can't get Curl URL to work inside a Ruby Module Method

I am having a problem where I can't get any of the following methods, (1, 2 and 3) to work.
require "curb"
#username = 'user'
#api_key = 'key'
#base_uri = 'https://url.com'
#offer_id = 999
#login_method = "login=#{#username}&api_key=#{#api_key}"
#method_3_url ="#{#base_uri}/3/?#{#login_method}"
module My_script
def self.call_method(url)
Curl::Easy.http_get(url){|curl| curl.follow_location = true; curl.max_redirects=10;}
end
def self.method1
call_method("#{#base_uri}/1/#{#login_method}")
end
def self.method2
call_method("#{#base_uri}/2/?#{#login_method}")
end
def self.method3
call_method("#{#base_uri}/3/?#{#login_method}")
end
end
I get the following error:
Curl::Err::MalformedURLError: URL using bad/illegal format or missing
URL from
/Users/home/.rvm/gems/ruby-2.0.0-p598/gems/curb-0.8.8/lib/curl/easy.rb:72:in
`perform'
When I run call_method(#method_3_url) it does seem to work correctly.
I can also take the original POST URL and paste it into Chrome and it'll work..
I have spent hours looking for a solution online for this and I can't seem to make it work.. I also get a similar error when using HTTParty. Please help :-)
Your instance variables aren't in the module, and are therefore out of scope.
Instead of:
#foo = 'bar'
module Foo
...
end
You're looking for:
module Foo
#foo = 'bar'
...
end

Unable to override read method URI::HTTP

I am trying to stub the following:
uri = URI(base_url)
source = uri.read
I have re-written the read method as follows:
equire 'open-uri'
module OpenURI
module OpenRead
def read
return IO.read('source.html')
end
end
end
But it doesn't seem to work. New to ruby and could use some pointers. I always seem to end up with
NoMethodError: undefined method `read' for #<URI::HTTP:0x10ac59918>
uri = URI(base_url)
source = uri.read
You use the read method, so take a look where it is:
uri.method(:read).method_location
If you want to know where to override, go for
uri.method(:read).owner
or simply
def uri.read
<your body>
end

How to override a method of ruby lib?

When I use ruby 1.8.7 Net::HTTP.post_form(URI.parse(URL), params), I found it has a bug, see :
https://stackoverflow.com/questions/3214502/how-to-send-an-array-with-nethttp-post-form
My code is a regular ruby script, and I want to override that method(net/http.rb#set_form_data) like this:
require 'net/http'
require 'uri'
module Net
module HTTPHeader
def set_form_data(params, sep = '&')
params_array = params.map do |k,v|
v.inject([]){|c, val| c << "#{urlencode(k.to_s)}=#{urlencode(val.to_s)}"}.join(sep)
end
self.body = params_array.join(sep)
self.content_type = 'application/x-www-form-urlencoded'
end
end
end
res = Net::HTTP.post_form(URI.parse(URL),
{'type'=>'flowers',
'colors[]' => %w[red white blue] })
But it seems my set_form_data method has not been invoked(I debuged). Is there anything wrong in my code?
You've been bit by a quirk in the way "alias" works. In net/http.rb, we find:
def set_form_data(params, sep = '&')
...
end
alias form_data= set_form_data
The caller is using the alias to call this method:
req.form_data = params
When you redefine the method, the alias is still stuck to the original definition, so your redefined method never gets called. To get around this, you can re-do the alias after redefining the method:
module Net
module HTTPHeader
def set_form_data(params, sep = '&')
..
end
alias form_data= set_form_data
end
end

Resources