I'm using create() to insert into a table that already exists, but some of the table fields and variable names start with a number. Currently in Ruby I'm getting a syntax error "unexpected tIDENTIFIER" when I try to do something like the following-
foo.each do |x|
Object.create(
3pm: x.3pm,
3pa: x.3pa
)
end
If I change it to '3pm' => x.3pm, it gives me a syntax error on the x.3pm portion.
How could I escape this to get it functioning?
The problem is that 3pm is not a valid identifier in Ruby. In Ruby, symbol literals declared with the :name syntax and method names must both be valid identifiers.
In the case of the keys, you can get around this by either using strings instead, as you discovered, or by using the :'name' syntax for symbol literals:
foo.each do |x|
Object.create(
:'3pm' => x.3pm,
:'3pa' => x.3pa
)
end
Unfortunately, this still leaves the problem of 3pm and 3pa not being valid method names. Normally, since those are not valid methods names, they couldn't even be methods on x in the first place. In this case though, x is likely using the either the define_method or method_missing features of Ruby to create or simulate the existance of a method named 3pm (even though that's normally not a valid method name in Ruby).
Thankfully, we can get around this by using Object#public_send to call the method:
foo.each do |x|
Object.create(
:'3pm' => x.public_send(:'3pm'),
:'3pa' => x.public_send(:'3pa')
)
end
That should resolve your problem.
Related
I'm using Rails 5. I'm having an issue extracting the numeric portion of a field and inserting it into my model attribute. I have
puts "age:#{age}"
self.age = age[/\d+/].present? ? age[/\d+/].to_i : nil
But I'm getting the output
age:41
TypeError: no implicit conversion of Regexp into Integer
from /Users/davea/Documents/workspace/myproject/app/models/my_object_time.rb:129:in `[]'
from /Users/davea/Documents/workspace/myproject/app/models/my_object_time.rb:129:in `set_age'
from /Users/davea/Documents/workspace/myproject/app/models/my_object_time.rb:51:in `block in <class:RaceTime>'
Is there a better way to write the above to extract the number (if ther eis one) but also avoid the error I'm seeing?
The error message is telling you that the [] you are calling expects an Integer as an argument, and thus it tries to convert the Regexp to an Integer (by calling to_int) but fails because Regexps don't respond to to_int.
Since String#[] does take a Regexp as an argument, it's obvious that the object referenced by age cannot be a String, it must be something else. Unfortunately, you don't tell us what object it is (you really should include such information in your question!) but we can make an educated guess: there are only three [] methods in Ruby I can think of, which would conceivably raise this exact error: Array#[], MatchData#[], and Integer#[]. So, my best guess at this point is that your age object is actually not a String, but rather a MatchData object, an Array, or an Integer.
But, we can do better: you included the result of to_s in your question. Array#to_s returns something like '[1, 2, 3]', but your question shows that age.to_s returns something like '41'. So, it can't be an Array.
Ergo, age is either a MatchData object or an Integer, but you are expecting it to be a String. You need to figure out where age is coming from, and why it isn't what you expect it to be, then fix that.
Your age variable is probably a number. I get the same error when I try to use the [/regex/] method with a number:
[5] pry(main)> 1[//]
TypeError: no implicit conversion of Regexp into Integer
You could say age = age.to_s first
I think you should better write that like:
str = "sometext 123 sometext"
self.age = /(?<age>\d+)/ =~ str.to_s ? age.to_i : nil
I am trying to use method_missing to convert dollar to different currencies.
class Numeric
##currency={"euro"=>2, "yen"=>6}
def method_missing(method_id,*args,&block)
method_id.to_s.gsub!(/s$/,'') #get rid of s in order to handle "euros"
##currency.has_key?(method_id) ? self*##currency[method_id] : super
end
end
> 3.euro #expect to return 6
NoMethodError: undefined method 'euro' for 3:Fixnum
from (pry):24:in 'method_missing'
> 3.euros #expect to return 6
NoMethodError: undefined method 'euros' for 3:Fixnum
from (pry):24:in 'method_missing'
I guess 3.euro isn't working because :euro.to_s.gsub!(/s$/,'') returns nil. I am not sure why it returns no method error.
Thanks for any help.
When method_missing will be called, then euro will be assigned to the method_id as a symbol. But your ##currency hash holds all keys as a strings, you need to convert them as symbols.
method_id.to_s.gsub!(/s$/,'') no way will change the actual object method_id. Because method_id.to_s will give you a new string object, and #gsub! will work on this only. No way you are changing the method_id, it remains as it is. Better to use non-bang one, because it will give you the actual object if no change made, or changed object, if change is made, then you need to assign this return value to a new variable.
With your ##currency, ##currency.has_key?(method_id) evaluated as false and super class method_missing has been called. Why? As method_id didn't convert into strings, which you expected may happened.
But, if you want to respond for both like euro or euros, then
class Numeric
##currency={"euro" => 2, "yen" => 6}
def method_missing(method_id,*args,&block)
#get rid of s in order to handle "euros"
new_method_id = method_id.to_s.gsub(/s$/,'')
##currency.has_key?(new_method_id) ? self*##currency[new_method_id] : super
end
end
3.euros # => 6
3.euro # => 6
gsub! modifies a string in place and returns nil. That won't work very well with the string you're using, which is a temporary created by to_s and never stored in a variable. Also, you are not even storing the result anywhere anyway. Why should gsub! on a string be expected to modify symbols, which are immutable anyway?
Try using gsub instead, which returns the modified string and leaves the caller alone. You'll also need to store the return value.
real_method_name = method_id.to_s.gsub(/s$/, "")
Also: The reason 3.euro didn't work is because your hash uses strings as keys but method_id is passed to the method as a symbol. If you didn't have to do string manipulations (to remove s suffixes, in this case), I would have suggested to just use symbols as your hash keys. As it is, though, you need to do string operations anyway, so my answer uses strings.
While trying to answer another member's question, I happened across this strange behaviour:
puts :some_object => 'another_object'
Surprisingly, the output is this:
{:some_object=>"another_object"}
What is this new devilry? It looks as though I've created a hash using #puts, and without using the normal curly-bracket syntax.
I can't test this theory though, because this just generates an error:
puts (:some_object => 'another_object').class
# => syntax error, unexpected =>, expecting ')'
What's going on here?
Edit: Okay, thanks to bundacia's explanation, it's now easy for me to test and confirm that it's a hash (whereas I wasn't sure how to do that before):
def test(x)
puts x.class
end
test(:some_object => 'another_object')
# => Hash
Many thanks!
You are passing a hash to puts. In ruby, if the last argument you're passing to a function is a hash the curly braces are optional. So your example is equivalent to:
puts( {:some_object => 'another_object'} )
This question already has answers here:
How to understand symbols in Ruby
(11 answers)
Closed 10 years ago.
class A
def test
"Test from instance"
end
class << self
def test
"Test from class"
end
end
end
p A.send(:test) # "Test from class"
p A.new.method(:test).call # "Test from instance"
Here symbol works as expected, but here:
s="test"
s1=:s
p s1 # :s
why :s is printed here?? I dont understand the reason behind it.
Can anyone please explain for me ?
Symbols are sort of lightweight strings (though they are not strings). The send() and method() methods can take strings or symbols; one is converted to the other in the inner workings (not sure which) and then ruby executes the method with the matching name. Hence A.send(:text) is equivalent to A.text(). If you had a variable named methodName = :text, you could do A.send(methodName) but not A.methodName().
Symbols are not variables, so you can't assign a value to a symbol. In your example, the symbol :s is unrelated to the variable s (despite the fact that they have the same "name", preceding it with a colon makes it a symbol instead of a variable). You're assigning a string value to the variable s but telling it to print the symbol :s, which it does.
Symbols are just a special kind of stringlike value that's more efficient for the runtime to deal with than a regular string. That's it. They aren't methods or variables or anything like that.
When you do A.send(:test), all you are doing is saying "hey, A, call the method named 'test'". You aren't sending the method itself, just the name; it's the logic inside send that is responsible for looking up the actual method to call.
The same thing goes when you ask for method with A.new.method(:test). All you are passing to method is the name "test", not the method defined with that name. It's up to method to use the name and find the actual method so it can return it, and it's that return value - a Method object - that you are doing call on. You can't do call on a Symbol like :test, because it's just a name.
From https://stackoverflow.com/a/1255362/509710:
p foo does puts foo.inspect, i.e. it prints the value of inspect instead of to_s, which is more suitable for debugging (because you can e.g. tell the difference between 1, "1" and "2\b1", which you can't when printing without inspect).
s="test"
s1=:s
p :s.object_id #137448
p s.object_id #77489950
p s1.object_id #137448
I have understand it now. I was assigning a symbol but expecting a string.
You set the value of s1 to be :s, so why would you expect it to return anything different?
If you look at the ruby API for the Object class, you will see both Object#send and Object#method take a symbol as a parameter, so the top example is also totally expected.
Say I have class Player and I want a boolean method to check if a player is attacked:
class Player
attr_accessor :name, :health, :attacked?
def initialize(name)
#name = name
#health = 100
#attacked? = false
end
end
I get a syntax error:
SyntaxError: (irb):14: syntax error, unexpected '='
#attacked? = false
^
from /usr/bin/irb:12:in `<main>'
Removing the question mark from attacked fixes the problem, but I thought it would better follow convention to have attacked? in my code. It's not a big deal to forgo the question mark, but why are zero? and nil? conventions when#variables? and def methods?= are invalid?
Note that if you comment out the line causing your error (#attacked? = false), you will still get an error relating to the question mark:
NameError: invalid attribute name `attacked?'
The problem is that ? is not a valid character in a variable name. The first error (the SyntaxError that you’re seeing) is caused at parse time and caught immediately. The second error is caused when Ruby tries to evaluate the code and cannot create an instance variable with a name containing an invalid character.
A question mark is a valid character at the end of a method name though (actually it’s possible to have a method with a ? anywhere in its name, but you can’t call such a method directly).
One way to achieve what you want is something like this:
class Player
attr_accessor :name, :health, :attacked
alias :attacked? :attacked
def initialize(name)
#name = name
#health = 100
#attacked = false
end
end
This leaves attacked without the question mark, but adds attacked? as an alias.
I've run into the same problem before, and wished that I could make instance variables with a trailing question mark. It seems to be a corner case in Ruby's grammar. Check this out:
>> 1 ? 2 : 3
=> 2
>> 1?2:3
=> 2
>> #a = true
=> true
>> #a?1:2
=> 1
>> a = true
=> true
>> a ? 1 : 2
=> 1
>> a?1:2
SyntaxError: (irb):9: syntax error, unexpected ':', expecting $end
So the ? symbol is overloaded in Ruby's grammar -- it is used for the ternary operator, and as part of valid identifiers for method names. This causes ambiguity in the last case, where Ruby's lexer seems to bite off the a? as a single token, which leaves it unable to parse the rest of the ternary operator properly. With instance variables, this can't happen.
Again, I agree that allowing question marks in instance variable names would be very useful, certainly more valuable than making some obscure uses of the ternary operator non-ambiguous.