Rails 中名称中带有标签的别名列

Aliasing Column with Hashtag in Name in Rails

我有一个遗留数据库,其中一个列的名称类似于 My#Column,我正在尝试为其添加别名。在我的 Sequel 模型中,我有:

alias_attribute :MyColumn, :"My#Column"

但我收到语法错误:

...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

问题似乎出在 #。我试过像 \# 一样转义它,但我得到了同样的错误。我不明白为什么我会收到语法错误,因为这种符号在其他地方对我有用。

我该怎么做才能使这个别名起作用?

这就是 alias_attribute 实际做的事情 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

所以基本上这变成了

  def MyColumn; self.My#Column; end
  def MyColumn?; self.My#Column?; end
  def MyColumn=(val); self.My#Column= val; end

请注意,这是在一行中,这意味着 self.My 之后的所有内容都变成注释(包括 end),因此您收到的错误。即使这不是 rails 中的一行,ruby 也会简单地引发 NoMethodError 因为 My 不是方法,因为 #Column 部分会被视为评论。

这看起来也很奇怪,因为 ActiveModel#alias_attribute 通过 define_proxy_call 实现了完全相同的功能,看起来像

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

在这里您可以看到它实际上检查新名称(name) 和原始名称(send) 是否为"compilable",如果不是,它会适当地处理它们。

而不是 alias_attribute 本质上是注释字符的问题。我建议使用 public_send 手动实现相同的功能,例如

  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

这应该会产生相同的结果,但不会出现语法问题。