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

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

Related

ruby clone an object

I need to clone an existing object and change that cloned object.
The problem is that my changes change original object.
Here's the code:
require "httparty"
class Http
attr_accessor :options
attr_accessor :rescue_response
include HTTParty
def initialize(options)
options[:path] = '/' if options[:path].nil? == true
options[:verify] = false
self.options = options
self.rescue_response = {
:code => 500
}
end
def get
self.class.get(self.options[:path], self.options)
end
def post
self.class.post(self.options[:path], self.options)
end
def put
self.class.put(self.options[:path], self.options)
end
def delete
self.class.put(self.options[:path], self.options)
end
end
Scenario:
test = Http.new({})
test2 = test
test2.options[:path] = "www"
p test2
p test
Output:
#<Http:0x00007fbc958c5bc8 #options={:path=>"www", :verify=>false}, #rescue_response={:code=>500}>
#<Http:0x00007fbc958c5bc8 #options={:path=>"www", :verify=>false}, #rescue_response={:code=>500}>
Is there a way to fix this?
You don't even need to clone here, you just need to make a new instance.
Right here:
test = Http.new({})
test2 = test
you don't have two instances of Http, you have one. You just have two variables pointing to the same instance.
You could instead change it to this, and you wouldn't have the problem.
test = Http.new({})
test2 = Http.new({})
If, however, you used a shared options argument, that's where you'd encounter an issue:
options = { path: nil }
test = Http.new(options)
# options has been mutated, which may be undesirable
puts options[:path] # => "/"
To avoid this "side effect", you could change the initialize method to use a clone of the options:
def initialize(options)
options = options.clone
# ... do other stuff
end
You could also make use of the splat operator, which is a little more cryptic but possibly more idiomatic:
def initialize(**options)
# do stuff with options, no need to clone
end
You would then call the constructor like so:
options = { path: nil }
test = Http.new(**options)
puts test.options[:path] # => "/"
# the original hasn't been mutated
puts options[:path] # => nil
You want .clone or perhaps .dup
test2 = test.clone
But depending on your purposes, but in this case, you probably want .clone
see What's the difference between Ruby's dup and clone methods?
The main difference is that .clone also copies the objects singleton methods and frozen state.
On a side note, you can also change
options[:path] = '/' if options[:path].nil? # you don't need "== true"

Getting undefined error in mass text app

I'm creating a app that sends mass texts using a JSON file with the numbers and names. Every time I test load the app in IRB I get the error:
NameError: undefined local variable or method `data_from_file' for main:Object
from /home/qc/tep/Coding Stuff/Ruby/text app/main.rb:14:in `contacts_from_file'
I understand what the error means, but I don't understand why I'm getting the error, here's the source code:
require 'json'
def sanatize(numbers)
"+1" + number.gsub(/^1|\D/, "")
end
def numbers_from_file
file = open('numbers.json').read
JSON.parse(file)
end
def contacts_from_file
contacts= { }
data_from_file['feed']['entry'].each do |entry|
name = entry['gsx$name']['$t']
number = entry['gsx$number']['$t']
contacts[sanatize(number)] = name
end
contacts
end
def contact_numbers
contacts_from_file.keys
end
def contact_name
contacts_from_file[number]
end
And here's the JSON file:
{
'1**********' => 'Big Bird'
'1**********'} => 'Josh'
}
If anybody could help me and tell me why the data_from_file is "undefined" it would be extremely helpful, thank you ahead of time.
You never define data_from_file, you just try to read from it in the contects_from_file method.
Perhaps you meant numbers_from_file instead of data_from_file?

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

In Sinatra(Ruby), how should I create global variables which are assigned values only once in the application lifetime?

In Sinatra, I'm unable to create global variables which are assigned values only once in the application lifetime. Am I missing something? My simplified code looks like this:
require 'rubygems' if RUBY_VERSION < "1.9"
require 'sinatra/base'
class WebApp < Sinatra::Base
#a = 1
before do
#b = 2
end
get '/' do
puts #a, #b
"#{#a}, #{#b}"
end
end
WebApp.run!
This results in
nil
2
in the terminal and ,2 in the browser.
If I try to put #a = 1 in the initialize method, I'm getting an error in the WebApp.run! line.
I feel I'm missing something because if I can't have global variables, then how can I load large data during application instantiation?
before do seems to get called every time there is a request from the client side.
class WebApp < Sinatra::Base
configure do
set :my_config_property, 'hello world'
end
get '/' do
"#{settings.my_config_property}"
end
end
Beware that if you use Shotgun, or some other Rack runner tool that reloads the code on each request the value will be recreated each time and it will look as if it's not assigned only once. Run in production mode to disable reloading and you will see that it's only assigned on the first request (you can do this with for example rackup --env production config.ru).
I ran into a similar issue, I was trying to initialize an instance variable #a using the initialize method but kept receiving an exception every time:
class MyApp < Sinatra::Application
def initialize
#a = 1
end
get '/' do
puts #a
'inside get'
end
end
I finally decided to look into the Sinatra code for initialize:
# File 'lib/sinatra/base.rb', line 877
def initialize(app = nil)
super()
#app = app
#template_cache = Tilt::Cache.new
yield self if block_given?
end
Looks like it does some necessary bootstrapping and I needed to call super().
def initialize
super()
#a = 1
end
This seemed to fix my issue and everything worked as expected.
Another option:
helpers do
def a
a ||= 1
end
end
Building on Theo's accepted solution, it is also possible to do:
class App < Sinatra::Application
set :blabla, ''
namespace '/b' do
get '/baby' do
# do something where bouh is assigned a value
settings.blabla = 'bouh'
end
end
namespace '/z'
get '/human' do
# settings.blabla is available here with newly assigned value
end
end
end
You could use OpenStruct.
require 'rubygems'
require 'sinatra'
require 'ostruct'
configure do
Struct = OpenStruct.new(
:foo => 'bar'
)
end
get '/' do
"#{Struct.foo}" # => bar
end
You can even use the Struct class in views and other loaded files.

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