【问题标题】:Ruby Class definition explanation for a java developer [closed]Java开发人员的Ruby类定义解释[关闭]
【发布时间】:2017-02-28 20:09:20
【问题描述】:

我是一名 Java 开发人员,很难理解这段 Ruby 代码,有人可以用 Java 术语解释这段代码吗?我可以理解方法部分(defs),但 belongs_to 部分让我很困惑

module Test

  class Signature < Base
    belongs_to :recipient, :class_name => 'Test::UserInformation'
    belongs_to :sd_file, :class_name => 'Test::TransportFile',
                                         :foreign_key => 'reference_id'

    def sd_status
      sd_file.nil? ? nil : sd_file.Status
    end

    def sd_sent_on
      sd_file.nil? ? nil : sd_file.sent
    end

    def sd_received_on
      sd_file.nil? ? nil : sd_file.received
    end

    def self.find_information(options={})
      unless (start_date = options.delete(:start_date)).blank? ||
               (end_date   = options.delete(:end_date)).blank?
          date_conditions = ' AND '                           +
                            '(requested_at > ?) AND ' +
                            '(requested_at < ?)'            
      end

【问题讨论】:

  • belongs_to 是 ruby​​ 中的类方法或 java 中的静态方法。方法签名看起来像def self.belongs_to(arg1, arg2)
  • 值得一提的是,belongs_to 不在 Ruby 本身中,它在 Rails(特别是 ActiveRecord)中,可能值得检查一下the guide

标签: java ruby oop


【解决方案1】:

这里的关键是要意识到(几乎)Ruby 中的所有内容都是脚本,(几乎)Ruby 中的所有内容都是消息发送(Java 中的“虚拟方法调用”),以及(几乎)Ruby 中的所有内容是一个对象。

在 Java 中,唯一的可执行代码是在方法定义中。其他一切都只是声明,是编译器创建一些编译器内部数据结构的指令。类声明不是在运行时执行,而是在编译时由编译器解释,然后生成相应的类结构、方法、字段等。

在 Ruby 中并非如此。文件的内容只是一个从上到下执行的脚本。这就是为什么你可以拥有这样的东西:

if OS.windows?
  class Foo
  end
else
  class Bar
  end
end

同样,模块或类定义的主体只是一个脚本,它允许您拥有如下内容:

if OS.windows?
  def foo
  end
else
  def bar
  end
end

所以,如您所见,实际上几乎所有内容都是脚本,特别是类定义的主体只是一个从上到下执行的脚本,就像任何其他脚本一样。

顺便说一下,由于类定义是在运行时动态执行的,而不是由编译器静态解释的,所以类定义中的超类定义也是如此:实际上,它只是一个任意的 Ruby 表达式,它返回一个Class 的实例对象,它不一定是静态常量类,即你可以这样写:

class Foo < if rand < 0.5 then String else Array end
end
# Yes, I am aware that this makes zero sense. Here is a more sensible example:

class Point < Struct.new(:x, :y)
end
# `Struct` is a class whose `::new` method returns a class

class Search < R '/search'
end
# This is an example from the Camping web microframework:
# `Search` is a controller which is bound to the URI route `/search`

如果您想知道:如果类定义只是一段代码,它会返回任何内容吗?答案是“是的,确实如此!”实际上,Ruby 中的 everything 会返回一些东西,everything 是 Ruby 中的表达式。没有声明。只有表达式。类定义计算为在类定义中计算的最后一个表达式的值。通常,在类定义中求值的最后一个表达式是方法定义(是的,方法定义与任何其他表达式一样,因此也有返回值),方法定义的求值结果为方法名称被定义,表示为Symbol

此外,几乎所有东西都是一个对象:有一个顶级全局对象,它没有名称,但通常称为main(因为这是它的to_sinspect 返回的内容)。您在脚本的顶层运行的所有事情都发生在self(Java 中的this)绑定到main 上。并且类也是对象,它们是Class 类的实例。在类定义主体中,self 绑定到正在定义的类。

现在,“几乎所有东西都是消息发送”:通常,每当您在 Ruby 中看到某事正在“完成”时,它就是消息发送的结果:1 + 2 实际上是 1.+(2)!false实际上是false.!foo.bar = baz实际上是foo.bar=(baz)ary[idx]实际上是ary.[](idx)ary[idx] = val实际上是ary.[]=(idx, val)等等。

belongs_to 也不例外:它实际上是发送给self 的消息,在类定义的上下文中,它就是正在定义的类。换句话说,它只是调用“类方法”。 (注:Ruby 中并没有真正的类方法,每个方法都是实例方法。正如我们已经建立的,类和任何其他对象一样是对象,所以它们可以像任何其他对象一样拥有实例方法,不需要一个特殊的“类方法”或“静态方法”构造!)

让我们从一个更简单的例子开始:

class Foo
  attr_reader :bar
end

同样,attr_reader 是发送给self 的消息,在本例中为Fooattr_reader 只生成一个属性读取器(Java 语言中的“getter”)。它看起来有点像这样(如果你还没有完全掌握 Ruby Reflection 也不要担心,无论如何你应该能够跟随):

class Class # in reality, it is defined in `Module`, a superclass of `Class`
  def attr_reader(name)
    define_method(name) do
      instance_variable_get(:"@#{name}")
    end
  end
end

顺便说一句:def 不是消息发送,它是一个内置关键字。但是有一个相应的方法可以代替,你在上面看到使用:Module#define_method

事实上,Ruby 甚至没有构造函数! new 只是一个“类方法”,看起来很像这样:

class Class
  def new(*args, &block)
    new_obj = allocate
    new_obj.initialize(*args, &block)
    return new_obj
  end
end

这里有一些微妙之处,例如initialize默认为private,因此实际上我们需要使用反射来规避访问限制,但这就是它的要点。

哦,既然我刚刚提到了private:那也是一种方法,而不是被解释为编译器指令的关键字。

所以,回顾一下,我们的立场是什么:类定义只是一段代码,方法定义只是被执行的表达式。但是由于类定义只是一段代码,其中可以有任意代码,包括消息发送(Java 中的“方法调用”)。 belongs_to 只是向类本身发送消息(方法调用),即方法是在类对象本身的继承链中的某个位置定义的(不要与 instances的继承链混淆> 班级!)

但真正重要的一点是:它只是一个方法调用。没什么可怕的。没什么神奇的。没什么特别的。

现在,这个方法调用了什么

它是一个流行的 Ruby ORM 库ActiveRecord 的一部分,它在self(即调用出现在其定义中的类)和另一个名称作为参数提供的类(或者更准确地说:由于 ActiveRecord 很大程度上依赖于约定,因此类的名称可以从参数派生)。根据 Ruby 类之间的这种关联,ActiveRecord ORM 会自动推断出支持这些类的数据库表之间的对应关系。

解释它的工作原理相当于写一个关于 ActiveRecord 的完整教程,a)对于 Stack Overflow 的答案来说太过分了,b)其他人已经做得比我好得多。但无论如何我得到的印象是,你并不想知道belongs_to 在内部是如何工作的,而是方法调用如何出现在你受过 Java 训练的头脑中,这只是“死代码”而已不会被执行。这个谜题的解决方案很简单:它不是死代码,它会被执行。

【讨论】:

    【解决方案2】:

    belongs_to 可能看起来像是一段非常神奇的 ruby​​ 代码,它使用 SQL 表关联将您的模型连接到另一个模型。

    但它所做的只是接受一些哈希参数,创建一系列动态方法。

    让我们分开

    belongs_to :sd_file, class_name: 'Test::TransportFile', foreign_key: 'reference_id'
    

    belongs_to - 建立关联的方法

    sd_file - 使用什么方法访问它:

    • Test::Signature.first.sd_file

    class_name - 指定加载哪个 ruby​​ 类的选项。 sd_file 将指向 Test::TransportFile。如果一切都符合约定,这通常由rails 自动完成。但是你可以手动指定

    foreign_key - 键入将两条记录联系在一起的表。这是在Test::TransportFileTest::Signature 中(我不记得了)。它将与另一个表中的id 绑定。

    【讨论】:

      猜你喜欢
      • 2011-07-07
      • 2010-10-07
      • 1970-01-01
      • 2013-06-26
      • 1970-01-01
      • 1970-01-01
      • 2013-05-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多