I'm completely new in Ruby and Sinatra so please forgive the trivial question:
I wanted to compute the view name instead of just passing in a symbol. I wanted the same action to return different views depending on the current state. There's like 20 different states so putting a good naming convention allows me to express the view name as a string very easily:
get "/page" do
erb "page-#{session[:page]}"
end
When I do that all I get is the string instead of the rendered view. Can anyone explain to me how I could do that in Sinatra?
I'd say you're probably looking for String#to_sym. I didn't test now, but all the examples say erb receives a symbol argument, not a string - so try this:
erb "page-#{session[:page]}".to_sym
or equivalently
erb :"page-#{session[:page]}"
If you pass string to erb it tries to render that string directly, not seeking for view with corresponding name. Converting string to symbol will help.
Related
I am using ActiveRecord and Sinatra (probably irrelevant context). ActiveRecord has a dynamic finder method find_by_[column]
I would like to pass a raw parameter string from HTTP directly into one of these methods. I'm pretty sure that ActiveRecord will sanitize the string I pass in. Right? Here's some example code to show what I want to do:
post '/login' do
user = User.find_by_username(params[:username])
...
end
Is it problematic to pass unsanitized http parameters into this kind of ActiveRecord method?
More generally, how should one go about figuring out which ActiveRecord methods sanitize their inputs? I keep reading this site and getting scared! http://rails-sqli.org/
Yes, they are safe. Because the dynamic finders are simply translated into the normal hash form. Find the implementation at github
Note that the new syntax (since version 4) is: find_by(username: params[:username])
To be sure if the user exists you can try something like:
if User.exists? :username => params[:username]
user = User.find_by_username(params[:username])
end
This greatly reduces the chances of SQLi.
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
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
I'm trying to scrape a site where I can only rely on classes and element hierarchy to find the right nodes. But using Mechanize::Page#search returns Nokogiri::XML::Elements which I can't use to fill and submit forms etc.
I'd really like to use pure CSS selectors but matching for classes seems to be pretty straight forward with the various _with methods too. However, matching things like :not(.class) is pretty verbose compared to simply using CSS selectors while I have no idea how to match for element hierarchy.
Is there a way to convert Nokogiri elements back to Mechanize objects or even better get them straight from the search method?
Like stated in this answer you can simply construct a new Mechanize::Form object using your Nokogiri::XML::Element retrieved via Mechanize::Page#search or Mechanize::Page#at:
a = Mechanize.new
page = a.get 'https://stackoverflow.com/'
# Get the search form via ID as a Nokogiri::XML::Element
form = page.at '#search'
# Convert it back to a Mechanize::Form object
form = Mechanize::Form.new form, a, page
# Use it!
form.q = 'Foobar'
result = form.submit
Note: You have to provide the Mechanize object and the Mechanize::Page object to the constructor to be able to submit the form. Otherwise it would just be a Mechanize::Form object without context.
There seems to be no central utility function to convert Nokogiri::XML::Elements to Mechanize elements but rather the conversions are implemented where they are needed. Consequently, writing a method that searches the document by CSS or XPath and returns Mechanize elements if applicable would require a pretty big switch-case on the node type. Not exactly what I imagined.
I'm using a web application project.
I have a folder in my web root called Users and in the folder I have a page called UserList.aspx
What I want to be able to do is type in Response.Redirect(Users.UserList.URL)
What I reckon I can probably do is create a class that extends Page and add a static property called URL that calls MethodInfo.GetCurrentMethod().ReflectedType (I think this works haven't tested) and then have that convert Users.UserList -> ~/Users/UserList.aspx
The problems with this method that I know of are one I need to go through every page and make it extend the base class and it doesn't work with any pages that contain a '-' character.
The advantages are that if pages are moved around then there aren't any broken links (Resharper gives out when there is a Page with the wrong namespace).
Also then every individual page that takes query string params could have a static method so that if I want to add/remove params I can see what uses those params etc.
Also if I want to call that page I don't have to check the name of the params e.g. UserId userId, Id or id. So that would look something like Users.ViewUser.GetUrl(1) -> ~/Users/ViewUser.aspx?UserId=1
So the question is: Is there a better way of doing this? Or is this a bad idea in principal?
You could just create an extension method for the base Page class that does what you are thinking. That would avoid having to go back and modify the base class for all your pages.
There is a better way. Create a traffic cop that knows about paths. Then if paths change, your data model changes or other stuff you just change that one place. Plus you could have read from a config file and make changes at run time.
Thus your call looks like this:
Repose.Redirect(TrafficCop["Users.UserList"].URL)
or some other way if you don't like the syntax.
The MethodInfo.GetCurrentMethod().ReflectedType doesn't work so I came up with another method of doing this using generics.
Instead of Users.ViewUser.GetUrl() or Users.ViewUser.URL it's GetUrl()
For a page with parameters it's still Users.ViewUser.GetUrl(1), it isn't ideal because they should both have the same way of being called but better than strings I guess.
Going to leave the question open for a while just in case.
edidt: I think I will actually just create another method called GetUrl(String getQuery) because if I have two parameters that are of the same type it doesn't work very well.
further edit: I found out how to do exactly what I want to do.
created a class called BasePage:Page where T : Page
on that are the static methods redirect and geturl
each page inherits from the base page as follows: MyPage:BasePage
Any page can redirect to that page by using the command MyPage.Redirect();