How to override a method of ruby lib? - ruby

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

Related

case sensitive headers in get request using httparty in rails

I'm currently getting an error when I make a GET request using httparty. The call works when I use curl. The error is as follows:
\"Authdate\":\"1531403501\"}" }, { "error_code":
"external_auth_error", "error_message": "Date header is missing or
timestamp out of bounds" } ] }
When I make the request via curl this is the header I use.
curl -X GET -H "AuthDate: 1531403501"
However, as you can see, the request changes from AuthDate to Authdate causing the error. Here is how I'm making the call:
require 'openssl'
require 'base64'
module SeamlessGov
class Form
include HTTParty
attr_accessor :form_id
base_uri "https://nycopp.seamlessdocs.com/api"
def initialize(id)
#api_key = ENV['SEAMLESS_GOV_API_KEY']
#signature = generate_signature
#form_id = id
#timestamp = Time.now.to_i
end
def relative_uri
"/form/#{#form_id}/elements"
end
def create_form
self.class.get(relative_uri, headers: generate_headers)
end
private
def generate_signature
OpenSSL::HMAC.hexdigest('sha256', ENV['SEAMLESS_GOV_SECRET'], "GET+#{relative_uri}+#{#timestamp}")
end
def generate_headers
{
"Authorization" => "HMAC-SHA256 api_key='#{#api_key}' signature='#{#signature}'",
"AuthDate" => #timestamp
}
end
end
end
any workaround this?
Headers are case-insensitive per the spec https://stackoverflow.com/a/41169947/1518336, so it seems like the server you're accessing is in the wrong.
Looking at Net::HTTPHeader, on which HTTParty is implemented
Unlike raw hash access, HTTPHeader provides access via case-insensitive keys
It looks like the class downcases the header keys for uniformity.
You'll likely need to look at a different networking library which doesn't rely on the net/http. Perhaps curb?
There is a work around this in the following article
https://github.com/jnunemaker/httparty/issues/406#issuecomment-239542015
I created the file lib/net_http.rb
require 'net/http'
class Net::HTTP::ImmutableHeaderKey
attr_reader :key
def initialize(key)
#key = key
end
def downcase
self
end
def capitalize
self
end
def split(*)
[self]
end
def hash
key.hash
end
def eql?(other)
key.eql? other.key.eql?
end
def to_s
def self.to_s
key
end
self
end
end
Then in the headers
def generate_headers
{
"Authorization" => "HMAC-SHA256 api_key='#{#api_key}' signature='#{#timestamp}'",
Net::HTTP::ImmutableHeaderKey.new('AuthDate') => "#{#timestamp}"
}
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

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

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"

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 do I monkey-patch ruby's URI.parse method

Some popular blog sites typically use square brackets in their URLs but ruby's built-in URI.parse() method chokes on them, raising a nasty exception, as per:
http://redmine.ruby-lang.org/issues/show/1466
I'm trying to write a simple monkey-patch that gracefully handles URLs with the square bracket. The following is what I have so far:
require 'uri'
module URI
def self.parse_with_safety(uri)
safe_uri = uri.replace('[', '%5B')
safe_uri = safe_uri.replace(']', '%5D')
URI.parse_without_safety(safe_uri)
end
alias_method_chain :parse, :safety
end
But when run, this generates an error:
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.8/lib/active_support/core_ext/module/aliasing.rb:33:in alias_method: NameError: undefined method 'parse' for module 'URI'
How can I successfully monkey-patch URI.parse?
alias_method_chain is executed on the module level so it only affects instance methods.
What you have to do is execute it on the module's class level:
require 'uri'
module URI
class << self
def parse_with_safety(uri)
parse_without_safety uri.gsub('[', '%5B').gsub(']', '%5D')
end
alias parse_without_safety parse
alias parse parse_with_safety
end
end
#nil his comment is very helpful, we ended up with the following:
def parse_with_safety(uri)
begin
parse_without_safety uri.gsub(/([{}|\^\[\]\#`])/) {|s| URI.escape(s)}
rescue
parse_without_safety '/'
end
end

Resources