java开发人员的Ruby类定义解释

时间:2017-02-28 20:09:21

标签: java ruby oop

我是一名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

2 个答案:

答案 0 :(得分:3)

这里的关键是要认识到(几乎)Ruby中的所有内容都是一个脚本,(几乎)Ruby中的所有内容都是一个消息发送(&#34;虚拟方法调用&#34;用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`

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

此外,几乎所有东西都是一个对象:有一个顶级的全局对象,它没有名称,但通常被称为main(因为它的{{1}是什么}和to_s返回)。您在脚本顶级运行的所有内容都与inspect(Java语言中的self)绑定到this。类也是对象,它们是main类的实例。在类定义主体中,Class绑定到正在定义的类。

现在,几乎所有事情都是发送消息&#34;通常,每当你看到某些事情已经完成时#34;在Ruby中,它是发送消息的结果:self实际上是1 + 21.+(2)实际上是!falsefalse.!实际上是foo.bar = bazfoo.bar=(baz)实际上是ary[idx]ary.[](idx)实际上是ary[idx] = val,依旧等等。

belongs_to也不例外:它实际上是一个发送给ary.[]=(idx, val)的消息,它在类定义的上下文中是被定义的类。换句话说,它只是调用一个&#34;类方法&#34;。 (注意:Ruby中没有真正的类方法,每个方法都是一个实例方法。正如我们已经建立的那样,类是任何其他对象的对象,所以它们可以像任何其他对象一样拥有实例方法,没有需要一个特殊的&#34;类方法&#34;或者#34;静态方法&#34;构造!)

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

self

同样,attr_reader是发送给class Foo attr_reader :bar end 的邮件,在这种情况下为selfFoo只是生成一个属性读取器(一个&#34; getter&#34;用Java说话)。它看起来有点像这样(如果你还没有完全掌握Ruby Reflection,请不要担心,无论如何你应该能够跟进):

attr_reader

顺便说一下: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 不是邮件发送,而是内置关键字。但是有一种相应的方法可以替代使用,您可以在上面使用它:Module#define_method

事实上,Ruby甚至没有构造函数! new只是一个&#34;类方法&#34;,它看起来非常像这样:

def

这里有一些细微之处,例如initialize默认为class Class def new(*args, &block) new_obj = allocate new_obj.initialize(*args, &block) return new_obj end end ,所以实际上我们需要使用反射来规避访问限制,但这是它的要点

哦,因为我刚才提到private:这也是一个方法,而不是被解释为编译器指令的关键字。

因此,回顾一下,我们在哪里:类定义只是一段代码,方法定义只是执行的表达式。但由于类定义只是一段代码,因此可以有任意代码,包括消息发送(&#34;方法调用&#34;在Java中说话)。 private只是对类本身的消息发送(方法调用),即该方法在类对象本身的继承链中的某处定义(不要与实例的继承链混淆< / em>班级!)

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

现在,此方法调用做什么

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

解释究竟是如何工作将等于编写ActiveRecord的完整教程,其中a)对于Stack Overflow答案来说太多了,而b)其他人已经做得比我好得多。但无论如何我得到的印象是,你并不是很想知道self内部是如何工作的,而是一个方法调用如何出现在你训练有素的思想被用来做的事情中#34 ;死代码&#34;没有被执行。这个谜语的解决方案很简单:它不是死代码而且它会被执行。

答案 1 :(得分:1)

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相关联。