Altering params in Sinatra before blocks - ruby

I am trying to alter the request params in a before block in Sinatra.
It seems that when using this syntax, it works:
before do
#params['global-before'] = 'yes'
end
But when using this syntax, it does not work:
before '/:id/test' do
#params['route-before'] = 'yes'
end
Here is a full example:
# test.rb
require 'sinatra'
require 'sinatra/reloader'
set :bind, '0.0.0.0'
set :port, 3000
before do
#params['global-before'] = 'yes'
end
before '/:id/test' do
#params['route-before'] = 'yes'
end
get '/' do
params.to_json
end
get '/:id/test' do
params.to_json
end
Then running:
$ ruby test.rb
$ curl localhost:3000
{"global-before":"yes"}
$ curl localhost:3000/123/test
{"global-before":"yes","id":"123"}
I was expecting to see the params['route-before'] populated as well.
I have tried using request.params instead of #params but that did not work at all.
Can anyone shed some light on this?
Update:
Opened an issue in Sinatra's issue tracker

The route filter goes first, and it has route parameters: {"id"=>"123"}, so this happens:
original, #params = #params, #params.merge(params) if params.any?
where original ends up as {} and #params as {"id"=>"123"}. When the global filter runs, there are no route parameters, so original remains unassigned (nil) and #params is the {} that was originally there.
After the filter processes, in the ensure clause, there is this:
#params = original if original
So global filter skips it, because original is nil, because there were no route parameters. The route filter resets the #params to whatever it was before the filter ran, because original is preserved, because there were route parameters.
I can't say whether this is a bug or an intended behaviour, but it's a "how" at least, if not a "why". It may make sense asking the Sinatra team (and reporting back here with the verdict).
tl;dr: #params are reset to pre-filter state if there are parameters in the filter's path pattern.
Note: you can hack around it by making your own instance variable:
before '/:id/test' do
#route_before = 'yes'
end
get '/:id/test' do
"Did we run route before? #{#route_before}"
end

Related

How to use login credentials from yaml in ruby

I'm new in ruby and I can't move forward from using login cred. from a yml file for a ruby project .I have a basic yml file
login:
urls:
gmail: "https://accounts.google.com/signin"
users:
username: something
password: something_new
I've created a yml.rb with require yml ,and access the yml path & loading the file .
But I don't know how to go through users/username ... in my test.rb :( .I've added in the "it " a variable to store the yml class and at the end i'm trying with
expect data['valid_user']
expect data['login']['urls']['gmail']
expect data['login']['users']['username']
but in the terminal I receive th error "NoMethodError: undefined method `[]' for nil:NilClass "
Update
Here is my yml.rb
require 'rspec'
require 'yaml'
class YamlHelper
#env = {}
def initialize
file = "#{Dir.pwd}path of yml file"
#env = YAML.load_file(file)
end
def get_variables
#env
end
end
Here is my test.rb file
describe 'My behaviour' do
before(:each) do
#browser = Selenium::WebDriver.for :firefox
end
it 'verifies yml login' do
yaml_helper = YamlHelper.new
data = yaml_helper.get_variables
expect data['valid_user']
expect test_data['login']['urls']['gmail']
expect test_data['login']['users']['username']
expect test_data['login']['users']['password']
end
after(:each) do #browser.quit
end
Can anyone take a look ?
thanks in advance
Have a lovely day
It looks like the code is almost there. When I'm debugging this sort of thing I'll often try to distil it down to the most basic test first
Something like:
require 'yaml'
file = "#{Dir.pwd}/data.yml"
data = YAML.load_file(file)
data['valid_user']
#=> nil
data['login']['urls']['gmail']
#=> "https://accounts.google.com/signin"
data['login']['users']['username']
#=> "something"
From the above you can see there's probably a typo in your test.rb file: test_data should most likely be data. Also, your YAML file doesn't contain the valid_user key, so you probably want to remove it from the test, at least for now.
The other two keys load fine.
The error you're seeing NoMethodError: undefined method '[]' for nil:NilClass means that one of the hashes you're variables you're treating like a hash is actually nil. This sort of bug is fairly common when you're diving into nested hashes. It means one of two things:
You've correctly descended into the hash, but the data is not present in the YAML.
The data is present in the YAML, but you're not getting to it correctly.
One change you can make that will make this code a bit more resilient is to replace:
test_data['login']['users']['username']
with:
test_data.dig('login', 'users', 'username')
The latter uses dig, which delves into the data structure and tries to return the value you're after, but if it gets a nil back at any point it'll just return nil, rather than throwing an exception.
Finally, for the test you've pasted here, you don't need the before(:each) or after(:each) blocks – Selenium is only necessary for browser testing.

Ruby oci8 fails when using dotenv for password

I'm attempting to use the dotenv gem to securely store a password for an oci8 connection. My .env file looks like this:
# this file is stored in the same location as config.ru
SCOTT_PASS='tiger'
Here's my config.ru file:
require 'dashing'
require 'dotenv'
Dotenv.load
configure do
set :auth_token, 'YOUR_AUTH_TOKEN'
helpers do
def protected!
# Put any authentication code you want in here.
# This method is run before accessing any resource.
end
end
end
map Sinatra::Application.assets_prefix do
run Sinatra::Application.sprockets
end
run Sinatra::Application
Here's the job which is failing. It fails with a null password error (ORA-01005).
SCHEDULER.every '1m', :first_in => 0 do |job|
conn = OCI8.new('scott', ENV['SCOTT_PASS'], 'orcl')
cursor = conn.parse("SELECT COUNT(*) FROM USER_TABLES")
cursor.exec
r = cursor.fetch
send_event('table_count', { current: r })
cursor.close
conn.logoff
end
I was able to confirm Dotenv.load is working properly as I was able to set other variables successfully, so there seems to be something unique about the oci8 connection.
I'm new to both Ruby and programming, so I might be overlooking something simple. Thanks!
This was a fairly obscure issue, but the root cause was due to a complex password being used (alpha numeric and special characters). The same password worked correctly in a small ruby script, but fails when placed under the rufus scheduler. Switching to a 20+ character password without numbers or special characters resolved the issue. Guessing there might some type of escaping required.

Sinatra/Rack session.fetch producing unexpected results

I was writing a quick helper in Sinatra for redirect_to_next, where I redirect to the path provided by session[:next] if it exists, or a default.
In Sinatra, session really provided by Rack, and by spec, it is said to provide a hash like interface for fetch. I wrote the following error helper to explain my problem.
error 401 do
session[:next] = request.path
puts "get #{session[:next]}"
puts "fetch #{session.fetch(:next, '/')}"
redirect "/login"
end
When I attempt to access /settings when not logged in, I halt 401 which runs the above code. This is what it prints to my terminal:
get /settings
fetch /
:next exists as a key, so why is it giving me the default as if it does not?
Update
This minimal example shows the same behavior.
require 'sinatra'
set :sessions, true
get '/' do
session[:testing] = "hello"
puts "get #{session[:testing]}"
puts "fetch #{session.fetch(:testing, 'goodbye')}"
end
Logs
[2012-04-29 14:11:51] INFO WEBrick::HTTPServer#start: pid=1954 port=9292
get hello
fetch goodbye
10.0.2.2 - - [29/Apr/2012 14:11:54] "GET / HTTP/1.1" 200 - 0.0485
Software
ruby (1.9.3p194)
rack (1.4.1)
sinatra (1.3.2)
The session hash isn’t a normal Ruby Hash, it’s a Rack::Session::Abstract::SessionHash. SessionHash actually inherits from Hash, but it overrides the []= and [] methods, calling to_s on any keys before storing and retrieving them.
Extending your update example:
require 'sinatra'
set :sessions, true
get '/' do
session[:testing] = "hello"
puts "get #{session[:testing]}"
puts "fetch #{session.fetch(:testing, 'goodbye')}"
puts "fetch with string #{session.fetch(:testing.to_s, 'goodbye')}"
end
gives this output:
get hello
fetch goodbye
fetch with string hello
When you use Hash#fetch, passing a symbol, the method gets dispatched directly to the parent hash, without being converted to a string, and so the matching key isn’t found.
So, always use Strings as keys in your sessions and everything should work.

Metaprogramming sinatra get

I have a list of words in a list, and I want to handle get requests to any of them (and respond the same way).
#words = ["foo","bar"....etc]
One of the ways I thought I could do this is to loop through the list and have a get directive generated for each word when sinatra is launched.
#words.each do |word|
get word do
# what to do
end
end
that doesn't work, but something in that fashion, maybe.
Another way of doing it might be responding to get %r{/(.+)} and then doing some processing inside there to see if it matches anything in the list and respond accordingly, but I'm interested nonetheless to see if there's a way I can do it as described above.
What you wrote does work, with a very minor change. For example:
require 'sinatra'
%w[foo bar].each do |word|
get "/#{word}" do
"You said #{word}!"
end
end
$ curl http://localhost:4567/bar
You said bar!
Instead of a catch-all regex route, you can craft a custom regexp dynamically to match only the words you want to match:
require 'sinatra'
words = %w[foo bar]
route_re = %r{^/(#{words.join '|'})$}i # case-insensitive
p route_re
#=> /^\/(foo|bar)$/i
get route_re do |name|
"You said #{name}!"
end
$ curl http://localhost:4567/FoO
You said FoO!
$ curl -I http://localhost:4567/jim
HTTP/1.1 404 Not Found
X-Cascade: pass
Content-Type: text/html;charset=utf-8
Content-Length: 413
Connection: keep-alive
Server: thin 1.2.7 codename No Hup
Depending on what you need, this might be enough:
get '/:word' do
# check if params[:word] is in your list
# and respond accordingly
if #words.include?(params[:word])
"yeeah"
else
pass
end
end
But keep in mind that this route matches everything.

How can I use Ruby to check if a domain exists?

Something along the lines of:
def domain_exists?(domain)
# perform check
# return true|false
end
puts "valid!" if domain_exists?("example.com")
require 'socket'
def domain_exists?(domain)
begin
Socket.gethostbyname(domain)
rescue SocketError
return false
end
true
end
If you want to check whether a domain is registered or not, then you need to perform a Whois query.
http://www.ruby-whois.org/
With ruby-whois is pretty easy:
Install gem and require.
a = Whois.whois("google.com")
a.available?
=> false
There is also a CLI bundled if you install it via ruby gems: ruby-whois
web page at: ruby-whois.org
You could shell out to nslookup like this:
`nslookup #{domain}`
and parse the results as text with regexes etc.
Or you can use the Socket class, specifically Socket.getaddrinfo. See previous StackOverflow answer on this very question.

Resources