No implicit conversion of Array into String? - ruby

I have a Sinatra application with the following GET method that takes the URL passed in:
get %r{/html/(.+)} do
url = params[:captures] # stores url => http://www.example.com
gethtml(url)
end
However, when gethtml(url) is called, it raises the error Sinatra no implicit conversion of Array into String.
gethtml accepts input such as http://example.com.
I know this is a data-type conversion issue and I tried calling to_s but it did not work.
Any help would be appreciated.

params[:captures] returns an array of strings, while get_html most likely accepts one URL as string.
Since you want to use the first group that matches as the URL:
get %r|/html/(.+)| do
get_html params[:captures].first
end
This is consistent with the Route matching with Regular Expressions example in the Routes section of the README.

Related

Rails, rspec: using "send()" to dynamically generate capybara URLs for controller specs

Inside a controller spec for a #show action:
resource_name = "adoption_transfer_type"
it "returns a single resource" do
resource = create(resource_name.to_sym)
# Works
xhr :get, api_v1_lookups_adoption_transfer_type_path(resource.id)
# Does not work
xhr :get, send("api_v1_lookups_#{resource_name}_path(#{resource.id})")
end
It results in the following error:
Failure/Error: xhr :get, send("api_v1_lookups_adoption_transfer_type_path(#{resource.id})")
NoMethodError:
undefined method `api_v1_lookups_adoption_transfer_type_path(66)'
It looks like send(...) is converting it to a string literal instead of executing it as Ruby code, but I thought that was the purpose of the send(...) method.
The broader picture is I'm using a single spec file to generate the specs for several lookup resources, since they are all the same and there's no reason to manage 10+ files when I can do it all in one.
the string interpolates to api_v1_lookups_adoption_transfer_type_path(2) which means you're trying to call some method by that name. Instead you want:
send("api_v1_lookups_#{resource_name}_path", resource.id)
This is how you pass arguments to a send call, also i'd get into the habit of using public_send =)

How can request params be validated to ensure they include required params and don’t include unsupported params?

This is particularly in the context of a REST API built with Ruby and Sinatra.
It's easy enough to manually check to make sure that the required params are not nil. And it's easy to iterate through a flat params hash to see if it's allowed in a whitelist.
However, when the params hash also include hashes it becomes more difficult.
One way of handling this I've thought of is converting the params hash to JSON and using a library to validate it against a JSON schema.
I have come across the sinatra-param gem but I haven't had a chance to see if it can validate sub-hashes or check for unsupported params.
Edit: Another possible way, that might make more sense is passing params directly to the model (I'm using DataMapper) and using its validation and errors instead of rewriting validations.
If each of your routes are going to take the same 4 params (IE :one, :two, :three, :four), you could set up a before filter, store an array of those four params as an instance variable in the before (which is accessible to all routes) and use a sexy little method from class Enumerable called all?:
before do
#base_params = [params[:one], params[:two], params[:three], params[:four]]
unless #base_params.all?
redirect '/error_route'
end
end
Enumerable#all? will return true only if all values in your 'collection' are not false or nil. Documentation can be found here for Ruby 1.9
Additionally if you find that you have different sets of params, you can create a hash instead of just an array of #base_params where they keys are the string value of request.request_method:
before do
#base_params = {"GET" => [params[:one], params[:two], params[:three], params[:four]],
"POST" => [params[:five], params[:six], params[:seven]],
"PUT" => [params[:one], params[:five], params[:six]]}
unless #base_params[request.request_method].all?
redirect '/error_route'
end
end

Capturing URL parameters after hashtag with Sinatra

I'm trying to capture the URL parameters from the following URL with Sinatra: http://localhost:4567/token#access_token=7nuf5lgupiya8fd6rz4yzkzvwwo2ria&scope=user_read
I'm tried using a couple code blocks to do this:
get '/token' do
puts params['access_token']
end
and
get '/:token' do |token|
puts token
end
and
get '/token#:token' do |token|
puts token
end
However none of these work. In the first block I get an empty string, in the second block I get the string "token", and in the third block I get "Sinatra doesn't know this ditty".
What would be the appopriate solution in this example?
Is that url you wrote correct? I think it needs to be
http://localhost:4567/token?access_token=7nuf5lgupiya8fd6rz4yzkzvwwo2ria&scope=user_read
With a ? instead of a # after /token. With that change, you should be able to access all the query parameters in the params hash.

Local variables in a block that are not passed as parameters [duplicate]

This question already has answers here:
Rails params explained?
(5 answers)
Closed 9 years ago.
I'm trying to understand how to make a variable available to a block that is not passed to the block as a parameter.
For example, how does Sinatra make params hash available?
get '/hello/:name' do
howAmIAccessingThis = params[:name]
end
Where is params coming from? This:
get '/hello/:name' do |params|
#hisName = params[:name]
end
might make sense because params is declared as a block argument, but that's not how it works. Looking through the source I cannot find how the params hash is getting passed to the block without it being a block parameter.
If it is not a local variable or a block variable, then it is a method. I don't know about Sinatra, but there must be a method params defined somewhere.
Using Parameters
Parameters in Sinatra are like everything else--simple and straightforward.
#!/usr/bin/env ruby
require 'rubygems'
require 'sinatra'
get '/hello/:name' do
"Hello #{params[:name]}!"
end
Once you've made this change, you'll need to restart the Sinatra application. Kill it with Ctrl-C and run it again. (There's a way around this, but we'll look at that in a future article.) Now, the parameters are straightforward. We've made an action called /hello/:name. This syntax is imitating what the URLs will look like, so go to http://localhost:4567/hello/Your Name to see it in action.
The /hello portion matches that portion of the URL from the request you made, and :name will absorb any other text you give it and put it in the params hash under the key :name. Parameters are just that easy. There is of course much more you can do with these, including regexp-based parameters, but this is all you'll need in almost every case.
Reference: http://ruby.about.com/od/sinatra/a/sinatra2.htm
EDIT
params values can come from the query string of a GET request, or the form data of a POST request, but there's also a third place they can come from: The path of the URL.
As you might know, Rails uses something called routes to direct requests to their corresponding controller actions. These routes may contain segments that are extracted from the URL and put into params. For example, if you have a route like this:
match 'products/:id', ...
Then a request to a URL like http://example.com/products/42 will set params[:id] to 42
So, whenever an URL GET, POST or Path contains such pattern then params hash is automatically constructed by rails.
Also check the Parameters section(Section 4) here

Sinatra and question mark

I need to make some methods with Sinatra that should look like:
http//:localhost:1234/add?string_to_add
But when I declare it like this:
get "/add?:string_to_add" do
...
end
it doesn't see the string_to_add param.
How should I declare my method and use this parameter to make things work?
In a URL, a question mark separates the path part from the query part. The query part normally consists of name/value pairs, and is often constructed by a web browser to match the data a user has entered into a form. For example a url might look like:
http://example.com/submit?name=John&age=93
Here the path section in /submit, and the query sections is name=John&age=93 which refers to the value “John” for the name key, and “93” for the age.
When you create a route in Sinatra, you only specify the path part. Sinatra then parses the query, and makes the data in it available in the params object. In this example you could do something like this:
get '/submit' do
name = params[:name]
age = params[:age]
# use name and age variables
...
end
If you use a ? character when defining a Sinatra route, it makes part of the url optional. In the example you used (get "/add?:string_to_add"), it will actually match any url starting with /ad, then optionally another d, and then anything else will be put in the :string_to_add key of the params hash, and the query section will be parsed separately. In other words the question mark makes the preceding d character optional.
If you want to get the ‘raw’ text of the query string in Sinatra, you can use the query_string method of the request object. In your example that would look something like this:
get '/add' do
string_to_add = request.query_string
...
end
Note that the route doesn’t include the ? character, just the base /add.
You should declare it as:
get "/add?:string_to_add?" do
...
end

Resources