How do you store a Ruby regex via a Rails controller? - ruby-on-rails

For an admin function in a Rails app, I want to be able to store regexes in the DB (as strings), and add them via a standard controller action.
I've run into 2 issues:
1) The Rails parameter filters seem to be automatically escaping backslashes (escape characters), which messes up the regex. For instance:
\s{1,2}(foo)
becomes:
\\s{1,2}(foo)
2) So then I tried to use a write_attribute to gsub instances of double backslashes with single backslashes (essentially unescaping them). This proved to be much trickier than expected. (I'm using Ruby 1.9.2 if it matters). Some things I've found:
"hello\\world".gsub(/\\/, ' ') #=> "hello world"
"hello\\world".gsub(/\\/, "\\") #=> "hello\\world"
"hello\\world".gsub(/\\/, '\\') #=> "hello\\world"
What I'm trying to do is:
"hello\\world".gsub(/\\/, something) #=> "hello\world"
I'd love to know both solutions.
1) How can you safely pass and store regexes as params to a Rails controller action?
2) How can you substitute double backslashes with a single backslash?

In short, you can't substitute a double backslash with a single one in a string, because a single backslash in a string is an escape character. What you can do is the following:
Regexp.new("hello\\world") #=> /hello\world/
This will convert your string into a regular expression. So that means: store your regular expressions as strings (with the escaped characters) and convert them into regular expressions when you want to compare against them:
regexp = "\\s{1,2}(foo)"
reg = Regexp.new(regexp) #=> /\s{1,2}(foo)/
" foo" =~ reg #=> 0

Related

Ruby Convert string into undescore, avoid the "/" in the resulting string

I have a name spaced class..
"CommonCar::RedTrunk"
I need to convert it to an underscored string "common_car_red_trunk", but when I use
"CommonCar::RedTrunk".underscore, I get "common_car/red_trunk" instead.
Is there another method to accomplish what I need?
Solutions:
"CommonCar::RedTrunk".gsub(':', '').underscore
or:
"CommonCar::RedTrunk".sub('::', '').underscore
or:
"CommonCar::RedTrunk".tr(':', '').underscore
Alternate:
Or turn any of these around and do the underscore() first, followed by whatever method you want to use to replace "/" with "_".
Explanation:
While all of these methods look basically the same, there are subtle differences that can be very impactful.
In short:
gsub() – uses a regex to do pattern matching, therefore, it's finding any occurrence of ":" and replacing it with "".
sub() – uses a regex to do pattern matching, similarly to gsub(), with the exception that it's only finding the first occurrence (the "g" in gsub() meaning "global"). This is why when using that method, it was necessary to use "::", otherwise a single ":" would have been left. Keep in mind with this method, it will only work with a single-nested namespace. Meaning "CommonCar::RedTrunk::BigWheels" would have been transformed to "CommonCarRedTrunk::BigWheels".
tr() – uses the string parameters as arrays of single character replacments. In this case, because we're only replacing a single character, it'll work identically to gsub(). However, if you wanted to replace "on" with "EX", for example, gsub("on", "EX") would produce "CommEXCar::RedTrunk" while tr("on", "EX") would produce "CEmmEXCar::RedTruXk".
Docs:
https://apidock.com/ruby/String/gsub
https://apidock.com/ruby/String/sub
https://apidock.com/ruby/String/tr
This is a pure-Ruby solution.
r = /(?<=[a-z])(?=[A-Z])|::/
"CommonCar::RedTrunk".gsub(r, '_').downcase
#=> "common_car_red_trunk"
See (the first form of) String#gsub and String#downcase.
The regular expression can be made self-documenting by writing it in free-spacing mode:
r = /
(?<=[a-z]) # assert that the previous character is lower-case
(?=[A-Z]) # assert that the following character is upper-case
| # or
:: # match '::'
/x # free-spacing regex definition mode
(?<=[a-z]) is a positive lookbehind; (?=[A-Z]) is a positive lookahead.
Note that /(?<=[a-z])(?=[A-Z])/ matches an empty ("zero-width") string. r matches, for example, the empty string between 'Common' and 'Car', because it is preceeded by a lower-case letter and followed by an upper-case letter.
I don't know Rails but I'm guessing you could write
"CommonCar::RedTrunk".delete(':').underscore

ruby regex match

For my ruby on rails project, I have a model called message which has a to field. I want to implement a wildcard search so that, for example, %545 will bring up all messages ending with 545, 545% will bring up all numbers starting with 545, %545% will bring up all messages including 545.
I have a query like Message.where("to like ?", str) where str is the string to match, e.g. %545, %545%, 545%...etc.
Everything works but I'm concerned about SQL injection attack. So I want to do a regex matching for str so that it only allows % and numbers to pass through. So I want strings like %545, %545%, 545% to pass, but not abc, %545a, a545%, %54a5% to pass.
I've tried str.scan(/.*?(\d+%)/) but that doesn't work.
Thanks.
You are correctly using placeholders, so you are protected from SQL injection attacks already. Rails will escape any unsafe characters in the pattern; you don't need to take any further action.
If you still want to strip characters other than digits and %, you can use Ruby's String#delete method:
str.delete('^1-9%')
The '^1-9%' argument means "Delete every character that is not 1 to 9 or %". (n.b. you cannot use \d here, because #delete doesn't understand regular expression meta characters.)
See https://ruby-doc.org/core-2.5.3/String.html#method-i-delete.

How to gsub slash (/) to "" in ruby

To gsub / to "" ruby
I tried as,
ss = "http://url.com/?code=\#{code}"
I am fetching this url from database
then have to gsub \ to '' to pass the dynamic value in code
How to gsub \ to ''
required output
ss = "http://url.com/?code=#{code}"
Your problem is actually not a problem. When you write "http://url.com/?code=\#{code}" in ruby, \# means that ruby is escaping the # character, cause # is a protected character. So you should have the backslash to escape it.
Just to prove this, if you write in a console your string with single quotes (single quotes will escape any special character (but single quotes, of course)):
>> 'http://url.com/?code=#{code}'
=> "http://url.com/?code=\#{code}"
This may be a little obscure but, if you want to evaluate the parameter code in the string, you could do something like this:
>> code = 'my_code'
>> eval("\"http://url.com/?code=\#{code}\"")
=> "http://url.com/?code=my_code"
I believe what you may be asking is "how do I force Ruby to evaluate string interpolation when the interpolation pattern has been escaped?" In that case, you can do this:
eval("\"#{ss}\"")
If this is what you are attempting to do, though, I would highly discourage you. You should not store strings containing the literal characters #{ } in your database fields. Instead, use %s and then sprintf the values into them:
# Stored db value
ss = "http://url.com/?code=%s"
# Replace `%s` with value of `code` variable
result = sprintf(ss, code)
If you only need to know how to remove \ from your string, though, you can represent a \ in a String or Regexp literal by escaping it with another \.
ss.gsub(/\\/,'')
You can try in this way also, working fine for my case.
url = 'www.abc.com?user_id=#{user[:id]}'
uri = URI.parse(url.gsub("=\#", "="))
uri.query = URI.encode_www_form({user_id: 12})
puts uri.to_s ==> "www.abc.com?user_id=12"

regex basic url expression

Hi I'm creating a regular expression (ruby) to test the beginning and end of string. I have both parts but can't join them.
Beginning of string
\A(http:\/\/+)
End of string
(.pdf)\z
How to join?
Bonus if it could validate in-between and accept anything (to avoid http://.pdf)
By the way, rubular http://rubular.com is a neat place to validate expressions
Use .+ to match any character except \n one or more times.
\A(http:\/\/+).+(\.pdf)\z
Should match http://www.stackoverflow.com/bestbook.pdf but not http://.pdf

Non-regexp version of gsub in Ruby

I am looking for a version of gsub which doesn't try to interpret its input as regular expressions and uses normal C-like escaped strings.
Update
The question was initiated by a strange behavior:
text.gsub("pattern", "\\\\\\")
and
text.gsub("pattern", "\\\\\\\\")
are treated as the same, and
text.gsub("pattern", "\\\\")
is treated as single backslash.
There are two layers of escaping for the second parameter of gsub:
The first layer is Ruby string constant. If it is written like \\\\\\ it is unescaped by Ruby like \\\
the second layer is gsub itself: \\\ is treated like \\ + \
double backslash is resolved into single: \\ => \ and the single trailing backslash at the end is resolved as itself.
8 backslashes are parsed in the similar way:
"\\\\\\\\" => "\\\\"
and then
"\\\\" => "\\"
so the constants consisting of six and eight backslashes are resolved into two backslashes.
To make life a bit easier, a block may be used in gsub function. String constants in a block are passed only through Ruby layer (thanks to #Sorrow).
"foo\\bar".gsub("\\") {"\\\\"}
gsub accepts strings as first parameter:
the pattern is typically a Regexp; if given as
a String, any regular expression metacharacters
it contains will be interpreted literally
Example:
"hello world, i am thy code".gsub("o", "-foo-")
=> "hell-foo- w-foo-rld, i am thy c-foo-de"

Resources