Ruby on Rails: String with variable parsing - ruby-on-rails

in ruby i can build a Variable with the date like this
irb(main):004:0> a = "#{Date.today}" => "2012-03-23"
But how can i do this with a already created string:
irb(main):005:0> a = '#{Date.today}' => "\#{Date.today}"
The background is, that i'am storing a path with different variables in a database and i need to replace these variables at runtime.
Thanks for any help.

You could eval the string like
a = 'Date.today'
result = eval(a)
While this works, it's can be extremely dangerous if you don't fully control the contents of that string (which is really hard if deal with any kind of user input).
So in general you are advised to never use eval. Instead you could build some simple DSL (domain specific language) where you have tokens in your string that are later replaced with pre-calculated values. A simple example could be:
a = "Today is :today"
result = a.gsub(/:(\w+)/) do |match|
case $1
when "today"
Date.today.to_s
end
end
As you are not evaluing arbitrary Ruby code, this is much safer. Alternatively, depending on your actual usage, you might also be satisfied with String formatting.

If you really want to run arbitrary Ruby code, you can use eval like this:
a = '#{Date.today}'
eval("\"#{a}\"")

Related

Shortcut for conditional variable manipulation in Ruby/Rails

One of the parameters my API controller receives has a big key name and I need to convert it from string to integer, if it is present, before sending it to the model to be persisted. Usually I would do one of the following:
params[:really_big_key_name] = params[:really_big_key_name].to_i unless params[:really_big_key_name].blank?
or
params[:really_big_key_name] = params[:really_big_key_name].present? ? params[:really_big_key_name].to_i : nil
As you can see, the code line becomes big, with more than 80 characters, and I want to stick with Ruby best practices. Is there a shorter, more Ruby way to do the same? Maybe "in place" methods. Something like arrays do with bang methods. Unfortunately, to_i! does not exist for strings, which is exactly what I need.
You could do this:
params[:really_big_key_name] = params[:really_big_key_name].try(:to_i)

trying to keep some escape characters.. but not others. ruby rails

I am having trouble formatting my string correctly. I am reading strings from a file and trying to use them as js code.
file_line = blah'blah"blah
string = line.gsub(/'/, "\\\'").gsub(/"/, "\\\"").dump
I want the output to be:
blah\'blah\"blah
But I cant seem to format it right. I have tried a bunch of things.
I'd use a single gsub matching both, ' and ", along with a block to prepend a \:
line = %q{blah'blah"blah}
string = line.gsub(/["']/) { |m| "\\#{m}" }
#=> "blah\\'blah\\\"blah"
puts string
Output:
blah\'blah\"blah
string = "blah'blah\"blah"
puts string.gsub(/'/,"\\\\'").gsub(/"/,'\"') # => blah\'blah\"blah
There's a whole lot of escaping going on here. To be honest I don't really understand the first one, but the second one is simple. I think in the first one we are escaping the backslash we want to add, and then escaping those two backslashes to avoid ruby interpretting them as a reference to the string. Or something. Trying to do a single level of escaping yields this:
puts string.gsub(/'/,"\\'").gsub(/"/,'\"') # => blahblah\"blahblah\"blah

What is the best possible way to avoid the sql injection?

I am using ruby 1.8.7 and rails 2.3.2
The following code is prone to sql injection
params[:id] = "1) OR 1=1--"
User.delete_all("id = #{params[:id]}")
My question is by doing the following will be the best solution to avoid sql injection or not. If not then what is the best way to do so?
User.delete_all("id = #{params[:id].to_i}")
What about:
User.where(id: params[:id]).delete_all
Ok sorry for Rails 2.x its:
User.delete_all(["id = ?", params[:id]])
Check doc
Btw, be sure you want to use delete_all instead of destroy_all, the former doesn't trigger callbacks.
You can use this also
User.delete(params[:id])
The other answers answer this well for Rails and it'll work fine if you follow their suggestions. In a more generic setting when you have to handle this yourself you can typically use a regular expression to extract a value that's in an expected format. This is really simple with an integer id. Think of it like this:
if params[:id] =~ /(\d+)/
safe_id = $1.to_i
# do something with safe_id now
end
That gets a little more complicated when you're handling strings and arbitrary data. If you have to handle such data then you can use the quoting methods available for the database adapters. In Rails this is ultimately rolled into a consistent interface:
safe_string = ActiveRecord::Base.connection.quote(unsafe_string)
For most database systems this will handle single quotes and backslashes in a special manner.
If you're outside of Rails you will have to use the quoting methods specific to your database adapter, but usage is quite similar.
The takeaway:
If your data has a particular format, enforce the format with a regular expression
Otherwise, use your database adapter's quoting function to make the data "safe" for use in a query
Rails will handle most of this for you if you properly use the various methods and "conditions"
Use the rails methods to pass your where options. You can always hardcode them, as in the example that you give, but the usual way would be something like:
User.where(:id => params[:id]).delete_all
User.where("id = ?", params[:id]).delete_all
User.where("id = :id", :id => params[:id]).delete_all
They are well tested and in case a new vulnerability is detected, an update will fix the problem and your code will not need to be changed.
By the way, if you just want to delete 1 record based on its id, what I would do is:
User.find(params[:id]).destroy

Stripping the first character of a string

i have
string = "$575.00 "
string.to_f
// => 0.0
string = "575.00 "
string.to_f
// => 575.0
the value coming in is in this format and i need to insert into a database field that is decimal any suggestions
"$575.00 "
We did this so often we wrote an extension to String called cost_to_f:
class String
def cost_to_f
self.delete('$,').to_f
end
end
We store such extensions in config/initializers/extensions/string.rb.
You can then simply call:
"$5,425.55".cost_to_f #=> 5425.55
If you are using this method rarely, the best bet is to simply create a function, since adding functions to core classes is not exactly something I would recommend lightly:
def cost_to_f(string)
string.delete('$,').to_f
end
If you need it in more than one class, you can always put it in a module, then include that module wherever you need it.
One more tidbit. You mentioned that you need to process this string when it is being written to the database. With ActiveRecord, the best way to do this is:
class Item < ActiveRecord::Base
def price=(p)
p = p.cost_to_f if p.is_a?(String)
write_attribute(:price, p)
end
end
EDIT: Updated to use String#delete!
So many answers... i'll try to summarize all that are available now, before give own answer.
1. string.gsub(/[\$,]/, '')
string.gsub!(/^\$/, '')
2. string[1..-1]
3. string.slice(0) # => "ome string"
4. s/^.//
Why (g)sub and regexp Just for deleting a character? String#tr is faster and shorter. String#delete is even better.
Good, fast, simple. Power of reverse indexing.
Hm... looks like it returns "S". Because it is an alias to String#[]
Perl? /me is cheking question tags...
And my advice is:
What if you have not dollar, but yena? Or what if you don't even have anything before numbers?
So i'll prefer:
string[/\d.+/]
This will crop leading non-decimal symbols, that prevent to_f to work well.
P.S.: By the way. It's known, that float is bad practice for storing money amounts.
Use Float or Decimal for Accounting Application Dollar Amount?
You could try something like this.
string = string[1..-1] if string.match(/^\$/)
Or this.
string.gsub!(/^\$/, '')
Remember to put that backslash in your Regexp, it also means "end of string."
you can use regex for that:
s/^.//
As laways, this is PCRE syntax.
In Ruby, you can use the sub() method of the string class to replace the string:
result = string.sub(/^./,"")
This should work.
[EDIT]
Ok, someone asked what's the gsub() is for:
gsub() acts like sub() but with the /g modifier in PCRE (for global replacement):
s/a/b/
in PCRE is
string.sub(/a/, "b")
and
s/a/b/g
is
string.gsub(/a/, "b")
in Ruby
What I'd use (instead of regular expressions) is simply the built-in slice! method in the String class. For example,
s = "Some string"
s.slice!(0) # Deletes and returns the 0th character from the string.
s # => "ome string"
Documentation here.

Is there a way in Ruby/Rails to execute code that is in a string?

So I have a database of different code samples (read snippets).
The code samples are created by users.
Is there a way in Rails to execute it?
So for example I have the following code in my database (with id=123):
return #var.reverse
Is there a way for me to execute it? Something like:
#var = 'Hello'
#result = exec(CodeSample.find(123))
So the result would be 'olleH'
You can use eval:
code = '#var.reverse'
#var = 'Hello'
#result = eval(code) # => "olleH"
But be very careful in doing so; you're giving that code full access to your system. Try out eval('exit()') and see what happens.
To the eval answer (which is the right one) I would add: get thee a copy of the Pickaxe Book (either Programming Ruby or Programming Ruby 1.9 depending on your Ruby version) and read the chapter called "Locking Ruby in the Safe." That chapter is all about Ruby's safe levels and tainted objects, and the chapter opens with exactly your use case and why you need to be paranoid about it.
There is also another approach which you can use if you have a very limited use case or to limit the use cases.
I had to use this approach to allow users to dynamically specify relative times e.g.3.months.ago
I used a regex to sanitize the input from the users like so
PERMITTED_OPERATIONS = /^\{\%([1-9]\.(day|year|month|hour|minute)(s\.|\.)ago|Time\.now)\%\}$/
def permit?(operation)
return !PERMITTED_OPERATIONS.match(operation.to_s).nil?
end
You could extend the regex to allow for from_now as well or create an array of regexes for permitted operations and loop over it.
Would welcome any comments on this approach.

Resources