I have a legacy database with a column named similar to My#Column
which I am trying to alias. In my Sequel model I have:
alias_attribute :MyColumn, :"My#Column"
But I get a syntax error:
...Ruby24-x64/lib/ruby/gems/2.4.0/gems/activesupport-5.1.4/lib/active_support/core_ext/module/aliasing.rb:26: syntax error, unexpected end-of-input, expecting keyword_end
The problem seems to be the #
. I have tried escaping it like \#
but I get the same error. I don't understand why I get a syntax error as this kind of symbol has worked for me in other places.
What do I do to get this alias to work?
This is what alias_attribute
is actually doing Source
module_eval <<-STR, __FILE__, __LINE__ + 1
def #{new_name}; self.#{old_name}; end # def subject; self.title; end
def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end
def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end
STR
So essentially this is becoming
def MyColumn; self.My#Column; end
def MyColumn?; self.My#Column?; end
def MyColumn=(val); self.My#Column= val; end
Notice the fact that this is in a single line which means that everything after self.My
becomes a comment (including the end
) and thus the error you are receiving. Even if this was not a single line in rails
ruby
would simply raise a NoMethodError
for the fact that My
is not a method because the #Column
portion would be treated as a comment.
This also seems strange since ActiveModel#alias_attribute
has a functional implementation of the exact same via define_proxy_call
which looks like
def define_proxy_call(include_private, mod, name, send, *extra)
defn = if NAME_COMPILABLE_REGEXP.match?(name)
"def #{name}(*args)"
else
"define_method(:'#{name}') do |*args|"
end
extra = (extra.map!(&:inspect) << "*args").join(", ".freeze)
target = if CALL_COMPILABLE_REGEXP.match?(send)
"#{"self." unless include_private}#{send}(#{extra})"
else
"send(:'#{send}', #{extra})"
end
mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
#{defn}
#{target}
end
RUBY
end
Here you can see it actually checks to see if the new name(name
) and original name(send
) are "compilable" and if not it handles them appropriately.
Rather than alias_attribute
which is going to have issues with what is essentially a comment character. I would suggest manually implementing the same using public_send
e.g.
def MyColumn
self.public_send("My#Column")
end
def MyColumn=(val)
self.public_send("My#Column=",val)
end
def MyColumn?
self.public_send("My#Column?")
end
This should result in the same but without the syntax issues.