Ruby namespacing issues - ruby

I'm attempting to build a gem for interacting w/ the Yahoo Placemaker API but I'm running into an issue. When I attempt to run the following code I get:
NameError: uninitialized constant Yahoo::Placemaker::Net
from /Users/Kyle/.rvm/gems/ruby-1.9.2-p290/gems/yahoo-placemaker-0.0.1/lib/yahoo-placemaker.rb:17:in `extract'
from (irb):4
from /Users/Kyle/.rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>'
yahoo-placemaker.rb
require "yahoo-placemaker/version"
require 'json'
require 'ostruct'
require 'net/http'
module Yahoo
module Placemaker
def self.extract (text = '')
host = 'wherein.yahooapis.com'
payload = {
'documentContent' => text,
'appid' => APP_ID,
'outputType' => 'json',
'documentType' => 'text/plain'
}
req = Net::HTTP::Post.new('/v1/document')
req.body = to_url_params(payload)
response = Net::HTTP.new(host).start do |http|
http.request(req)
end
json = JSON.parse(response.body)
Yahoo::Placemaker::Result.new(json)
end
end
end

I have yet to figure out how exactly constant name resolution works in Ruby (I think the rules are a bit messy here), but from my experience it could well be that Net is looked up in the current namespace instead of the global one. Try using the fully qualified name:
::Net::HTTP::Post.new
A similar problem could occur in this line:
Yahoo::Placemaker::Result
You should replace it with either ::Yahoo::Placemaker::Result or better Result (as it lives in the current namespace).

Try requiring net/http before. Ruby is falling back to find it in the module if it isn't defined.
require 'net/http'

Related

How to fix Ruby Rack SessionId error Error: undefined class/module Rack::Session::SessionId when decoding the rack.session cookie

While writing a ruby script to decode rack session cookies and load the object using the ruby function Marshal load, I am getting the following error:
Error: undefined class/module Rack::Session::SessionId
This is my ruby script:
require 'pp'
require 'base64'
require 'uri'
require 'rack' # solves the error: Error: undefined class/module Rack::
c = gets
cookie, signature = c.split("--")
decoded = Base64.decode64(URI.decode(cookie))
begin
object = Marshal.load(decoded)
pp object
rescue ArgumentError => e
puts "Error: " + e.to_s
end
How do I fix this? I tried looking for solutions on stackoverflow but couldn't figure it out.
Thanks to Louis Nyffenegger (#snyff) who helped me with this
https://twitter.com/snyff/status/1407054131697758211?s=20
This error can be solved by creating a method stub
require 'pp'
require 'base64'
require 'uri'
require 'rack'
class Rack::Session::SessionId
end
c = gets
cookie, signature = c.split("--")
decoded = Base64.decode64(URI.decode(cookie))
begin
object = Marshal.load(decoded)
pp object
rescue ArgumentError => e
puts "Error: " + e.to_s
end
Try clearing all of the cookies for the site.
I think this works because something is causing an exception while loading the Rack::Session::SessionId class.
This type of error happens occasionally when working on Rails applications. A SyntaxError (or any other exception) inside a class definition (e.g. in a model) can then cause an undefined class/module error instead of the real error.

Overriding third-party method in Ruby

I'm developing some tool using pure ruby and RestClient and I'd like to override default log_request method of Request class.
lib/restclient/request.rb
module RestClient
class Request
def log_request
RestClient.log << "SECRET"
end
end
end
But now, if I try to test this, it is not working:
$ irb
irb(main):001:0> require 'restclient'
=> true
irb(main):002:0> RestClient.log = "stdout"
=> "stdout"
irb(main):003:0> RestClient.get("http://localhost")
RestClient.get "http://localhost", "Accept"=>"*/*; q=0.5, application/xml", "Accept-Encoding"=>"gzip, deflate"
Expected to see only SECRET as output.
I'm probably missing, how to "inject" my code in default RestClient library ?
How can I do this from another file in lib/mytool/somefile.rb ?
Since you place it in your lib folder, this file is never being loaded as RestClient::Request constant is already defined. Place this code inside your config/initializers folder
This file need to be exactly required after require 'restclient'
Adding load 'lib/restclient/request.rb' did the trick

Can't understand the ruby code

I can't understand what the below ruby code does. Can anyone give me some explanation. Thanks!
map '/healthz' do
run Healthz.new(logger)
end
The Healthz is:
class Healthz
def initialize(logger)
#logger = logger
end
def call(env)
#logger.debug "healthz access"
healthz = Component.updated_healthz
[200, { 'Content-Type' => 'application/json', 'Content-Length' => healthz.length.to_s }, healthz]
rescue => e
#logger.error "healthz error #{e.inspect} #{e.backtrace.join("\n")}"
raise e
end
end
And the lib used are:
require "eventmachine"
require 'thin'
require "yajl"
require "nats/client"
require "base64"
require 'set'
Since you're using eventmachine and thin, I'd guess that code is some kind of routing code for a simple web application.
That is, it maps the /healtz route of the application to the Healtz class, so that if you start up the app, and point your browser to localhost:<whatever_port_thin_uses>/healtz, it would start up a Healtz.new instance for you.
Since I don't know what Healtz actually does, I've no idea what will actually happen, but my guess is that it's some kind of rack application.
And, as I already stated, this is just my guess, from seeing the list of libs you're using.

Convert to string/text ruby

I'm using the following code to download a page through a POST request:
require 'net/http'
require 'uri'
res = Net::HTTP.post_form(URI.parse('http://example.com'),{'post'=>'1'})
puts res.split("Date")
The URL I originally used has been replaced with example.com
It works great, but when I try to call split (last line) it returns an error:
<main>': undefined methodsplit' for # (NoMethodError)
I'm new to ruby, so I'm confused about this error.
The method you are calling returns a HTTPResponse object, so you need to leverage that object's methods to get what you want. maybe something like:
require 'net/http'
require 'uri'
res = Net::HTTP.post_form(URI.parse('http://example.com'),{'post'=>'1'})
puts res.body.split("Date")
Notice the body method.
Or, if you want to see all the data returned:
require 'net/http'
require 'uri'
res = Net::HTTP.post_form(URI.parse('http://example.com'),{'post'=>'1'})
puts res.inspect
Hope this helps!

Calling Sinatra from within Sinatra

I have a Sinatra based REST service app and I would like to call one of the resources from within one of the routes, effectively composing one resource from another. E.g.
get '/someresource' do
otherresource = get '/otherresource'
# do something with otherresource, return a new resource
end
get '/otherresource' do
# etc.
end
A redirect will not work since I need to do some processing on the second resource and create a new one from it. Obviously I could a) use RestClient or some other client framework or b) structure my code so all of the logic for otherresource is in a method and just call that, however, it feels like it would be much cleaner if I could just re-use my resources from within Sinatra using their DSL.
Another option (I know this isn't answering your actual question) is to put your common code (even the template render) within a helper method, for example:
helpers do
def common_code( layout = true )
#title = 'common'
erb :common, :layout => layout
end
end
get '/foo' do
#subtitle = 'foo'
common_code
end
get '/bar' do
#subtitle = 'bar'
common_code
end
get '/baz' do
#subtitle = 'baz'
#common_snippet = common_code( false )
erb :large_page_with_common_snippet_injected
end
Sinatra's documentation covers this - essentially you use the underlying rack interface's call method:
http://www.sinatrarb.com/intro.html#Triggering%20Another%20Route
Triggering Another Route
Sometimes pass is not what you want, instead
you would like to get the result of calling another route. Simply use
call to achieve this:
get '/foo' do
status, headers, body = call env.merge("PATH_INFO" => '/bar')
[status, headers, body.map(&:upcase)]
end
get '/bar' do
"bar"
end
I was able to hack something up by making a quick and dirty rack request and calling the Sinatra (a rack app) application directly. It's not pretty, but it works. Note that it would probably be better to extract the code that generates this resource into a helper method instead of doing something like this. But it is possible, and there might be better, cleaner ways of doing it than this.
#!/usr/bin/env ruby
require 'rubygems'
require 'stringio'
require 'sinatra'
get '/someresource' do
resource = self.call(
'REQUEST_METHOD' => 'GET',
'PATH_INFO' => '/otherresource',
'rack.input' => StringIO.new
)[2].join('')
resource.upcase
end
get '/otherresource' do
"test"
end
If you want to know more about what's going on behind the scenes, I've written a few articles on the basics of Rack you can read. There is What is Rack? and Using Rack.
This may or may not apply in your case, but when I’ve needed to create routes like this, I usually try something along these lines:
%w(main other).each do |uri|
get "/#{uri}" do
#res = "hello"
#res.upcase! if uri == "other"
#res
end
end
Building on AboutRuby's answer, I needed to support fetching static files in lib/public as well as query paramters and cookies (for maintaining authenticated sessions.) I also chose to raise exceptions on non-200 responses (and handle them in the calling functions).
If you trace Sinatra's self.call method in sinatra/base.rb, it takes an env parameter and builds a Rack::Request with it, so you can dig in there to see what parameters are supported.
I don't recall all the conditions of the return statements (I think there were some Ruby 2 changes), so feel free to tune to your requirements.
Here's the function I'm using:
def get_route url
fn = File.join(File.dirname(__FILE__), 'public'+url)
return File.read(fn) if (File.exist?fn)
base_url, query = url.split('?')
begin
result = self.call('REQUEST_METHOD' => 'GET',
'PATH_INFO' => base_url,
'QUERY_STRING' => query,
'rack.input' => StringIO.new,
'HTTP_COOKIE' => #env['HTTP_COOKIE'] # Pass auth credentials
)
rescue Exception=>e
puts "Exception when fetching self route: #{url}"
raise e
end
raise "Error when fetching self route: #{url}" unless result[0]==200 # status
return File.read(result[2].path) if result[2].is_a? Rack::File
return result[2].join('') rescue result[2].to_json
end

Resources