What does this Ruby line say? - ruby

I am working with a custom renderer, and I used some copy paste from another site. I can't seem to figure out what this piece is doing right here.
"#{options[:callback]}(#{data})"
Here is the piece of code in full context.
ActionController.add_renderer :as3 do |data, options|
data = ActiveSupport::JSON.encode(data) unless data.respond_to?(:to_str)
data = "#{options[:callback]}(#{data})" unless options[:callback].blank?
self.content_type ||= Mime::JSON
self.response_body = data
end

It's simple string interpolation. It will produce a string like this, where callback is the value of options[:callback], and value is whatever is in the variable data.
"callback(value)"
In Ruby, double-quoted strings support interpolation via #{} syntax. That is, if you have a variable x containing the value 3, the string "The value of x is #{x}" will be evaluated to "The value of x is 3". Inside a #{} you can have any arbitrarily complex Ruby expression, including array/hash indexing. So, the first part of the string, "#{options[:callback]}" is simply substituting the value of options[:callback] into the string.
The next part, the () is simply raw string data, not executable code. Inside the (), you have a second #{} substitution of data. It might be clearer if you replace the two variable substituions with x and y:
x = 3
y = 4
"#{ x }(#{ y })"
The above will evaluate to the string "3(4)"

This is converting a JSON response to JSONP; imagine data is:
'{"some": "thing", "goes": "here"}'
JSONP states that the data should be wrapped in a JavaScript function call. So of options[:callback] is the string test (the name of the function to call), the resulting JSONP would be:
'test({"some": "thing", "goes": "here"})'

It's a template that replaces the first field with the value of options poiinted to by the interned string :callback, and the second field, inside the parens with the contents of data.
I'd bet a buck that the resulting string is going to be eval'd somewhere else, where it will become a call to a procedure. That would work something like this:
options[:callback] = "foo"
data="arg,arg,arg"
(Notice that data is being encoded into JSON, so the string passed as data is a json string.
The string then turns into "foo(arg.arg.arg)", and when it's eval'd it becomes a call to routine foo with those arguments.
http://www.ruby-doc.org/core-1.9.3/Kernel.html#method-i-eval
Update:
Actually, I take it back about the Ruby eval -- although that would work, it's more likely turning into a Javascript function call. This would then let you pass the name of a javascript function as a string and the code would return the appropriate callback function for execution by javascript later.

You can rewrite
"#{options[:callback]}(#{data})"
as
options[:callback].to_s + "(" + data.to_s + ")"

Related

How to avoid String Interpolation in Ruby returning unrendered variables?

I have table, in which cells are populated with data from a Ruby function call, which itself is a String Interpolation.
I want to take the first 5 characters of this string, and output it in the cell.
My problem is, is that what is outputted is not the result of the interpolation, but the variables itself.
Where am I going wrong here?
.slim table cell
td.event
== activity_cell.subtitle[1, 5]
activity_cell.rb
def subtitle
"#{fa_icon(icon1)} #{model_fa_icon} #{type_text} #{html_escape(extra_text)}"
end
output:
"#{fa
Note:
The string interpolation is rendered correctly if I'm not splitting the string to get the first X characters.

Beginner question: What is a role of plus in front of a string in ruby?

I have a code that looks like following
value = +"#{x}/part"
value << "/part2"
I understand that value would contain something like valueOfX/part/part2, but I don't understand why there is + in front of the string. I tried searching for it, but search engines are not very good at understanding what "plus in front of a string ruby" means. I also tried to run this in online ruby repl with no difference when + is added or not added.
So question is why may it be useful to have + like this?
If the string is frozen, then return duplicated mutable string.
If the string is not frozen, then return the string itself.
source: https://ruby-doc.org/core/String.html#method-i-2B-40
So in your case, since your string is not frozen, your code is equivalent to:
value = "#{x}/part"
EDIT:
As explained by #stefan in the comments, in Ruby 2.x, interpolated string were frozen with frozen_string_literal: true. So value = +"#{x}/part" is not equivalent to value = "#{x}/part". It's not the case anymore with Ruby 3.

The code always outputs "not"

The following code always outputs "not":
print "input a number please. "
TestNumber = gets
if TestNumber % 2 == 0
print "The number is even"
else
print "The number is not even"
end
What is going wrong with my code?
The gets() method returns an object of type String.
When you call %() on a String object, the return value is a new String object (usually it changes the text. You can read more about string formatting here).
Since there are no String objects that == 0, the if/else will always take the same path.
If you want to use the return value of gets() like a number, you will need to transform it into one first. The simplest approach is probably to use the to_i() method on String objects, which returns a new 'Integer' object. If you're doing something where the user input will not always be an integer (e.g. 3.14 or 1.5), you might need to use a different approach.
One last thing: in your example the result of gets() is saved into a constant called TestNumber. Constants are different to normal variables, and they will probably cause problems if you're not using them intentionally. Normal variables don't start with capital letters. (You can read more about ruby variables here). In ruby you need to write you variable names like this: test_number.
I suspect your Testnumber variable might be interpreted as a string during the operation. make sure the testnum is converted to an integer first even if you put in say 100 it could be its being interpreted as the stirng "100" and not the integer 100.
A similar issue can be found here: Ruby Modulo Division
You have to convert TestNumber from string to integer, as your input has linefeed and/or other unwanted characters that do not match an integer.
Use TestNumber = gets.to_i to convert to integer before testing.

When do you have to use #{} while referring to variables in Ruby?

Sometimes if I've defined a a variable, for example
xyz="example"
and I'd like to refer back to xyz, I can either type xyz or #{xyz} in statements/loops etc.
My question is when do I use xyz and when do I use #{xyz}? And how do they differ?
#{} allows you to use any Ruby expression (not necessarily a variable) inside an interpolating quote (doubly-quoted strings, regular expressions...). It will evaluate the expression, convert the result to a string using the to_s method, then insert ("interpolate") the result into the string at that spot.
For example, if you have a string "Hello, apollo!" and want to replace the apollo with the contents of a variable, you could say "Hello, #{name}!".
You could even put a whole program inside a string:
"One plus two equals #{
def last_name_by_first_name(last_name)
People.find_by_last_name(last_name).first.first_name
end
find_by_last_name('Jack')
}!"
or
"#{a = 1} + #{b = 2} = #{a + b}"
(But please don't do that, it's a horrid way to write code. Sticking with variable look-ups and simple function calls should be enough for most purposes.)
Anywhere outside of a string (or other interpolating quote), #{} is an error.

gsub! On an argument doesn't work

I am making a function that turns the first argument into a PHP var (useless, I know), and set it equal to the second argument. I'm trying to gsub! it to get rid of all the characters that can't be used in a PHP var. Here is what I have:
dvar = "$" + name.gsub!(/.?\/!#\#{}$%^&*()`~/, "") { |match| puts match }
I have the puts match there to make sure some of the characters were removed. name is a variable passed into a method in which this is its purpose. I am getting this error:
TypeError: can't convert nil into String
cVar at ./Web.rb:31
(root) at C:\Users\Andrew\Documents\NetBeansProjects\Web\lib\main.rb:13
Web.rb is the file this line is in, and main.rb is the file calling this method. How can I fix this?
EDIT: If I remove the ! in gsub!, it goes through, but the characters aren't removed.
Short answer
Use dvar = "$" + name.tr(".?\/!#\#{}$%^&*()``~", '')
Long answer
The problem you are facing is that the gsub! call is returning nil. You can't concatenate (+) a String with a nil.
That's happening because you have a malformed Regexp. You aren't escaping the special regex symbols, like $, * and ., just for a start. Also, the way it is now, gsub will only match if your string contains all that symbols in sequence. You should use the pipe (|) operator to make an OR like operation.
gsub! will also return nil if no substitutions happened.
See the documentation for gsub and gsub! here: http://ruby-doc.org/core/classes/String.html#M001186
I think you should replace gsub! with gsub. Do you really need name to change?
Example:
name = "m$var.name$$"
dvar = "$" + name.gsub!(/\$|\.|\*/, "") # $ or . or *
# dvar now contains $mvarname and name is mvarname
Your line, corrected:
dvar = "$" + name.gsub(/\.|\?|\/|\!|\#|\\|\#|\{|\}|\$|\%|\^|\&|\*|\(|\)|\`|\~/, "")
# some things shouldn't (or aren't needed to) be escaped, I don't remember them all right now
As J-_-L appointed, you could also use a character class ([]), that makes it a little clearer, I guess. Well, it's hard to mentally parse anyway.
dvar = "$" + name.gsub(/[\.\?\/\!\#\\\#\{\}\$\%\^\&\*\(\)\`\~]/, "")
But because what you are doing is simple character replacement, the best method is tr (again reminded by J-_-L!):
dvar = "$" + name.tr(".?\/!#\#{}$%^&*()`~", '')
Way easier to read and make modifications.
You cannot apply a second parameter
and a block to gsub (the block is ignored)
The regex is wrong, you forgot the
square brackets:
/[.?\/!#\#{}$%^&*()~]/`
Because your regex is wrong, it
didn't match anything and because
gsub! returns nil if nothing was
replaced, you get this strange nil no
method error
btw: you should use gsub not gsub! in
this case, because you are using the
return value (and not name itself) --
and the error would not have happened
i dont see what the block is for
just do
name = 'hello.?\/!##$%^&*()`~hello'
dvar = "$" + name.gsub(/\.|\?|\\|\/|\!|\#|\#|\{|\}|\$|\%|\^|\&|\*|\(|\)|\`|\~/, "")
puts dvar # => "$hellohello"
or use [] to denote OR
dvar = "$" + name.gsub(/[\.\?\\\/\!\#\\\#\{\}\$\%\^\&\*\(\)\`\~]/, "")
you have to escape the special characters and then OR them so it will remove them individually not just if they are all found together
also there is really no need to use gsub! to modify the string in place use the non mutator gsub() since you assign it to a new variable,
gsub! returns nil for which the operator + is not defined for stings, which gives you the no method error mentioned
It seems as the 'name' object is nil, you may be calling gsub! on nil which usually complains with a NoMethodError: private method gusb! called for nilNilClass, since I don't know the version of ruby you are using I am not sure if the error would be the same, but it's a good place to start looking at.

Resources