使用“on:”的语法在 Ruby on Rails 中意味着什么?

its*_*rzi 2 ruby ruby-on-rails

这真的很难用谷歌搜索,因为我不知道它是 Ruby 的东西还是 Rails 的东西,而且谷歌在搜索“on”方面做得不好

\n

在一个看起来像这样的文件中

\n
# app/models/concerns/searchable.rb\nmodule Searchable\n  extend ActiveSupport::Concern\n\n  included do\n    include Elasticsearch::Model\n    include Elasticsearch::Model::Callbacks\n\n    # Every time our entry is created, updated, or deleted, we update the index accordingly.\n    after_commit on: %i[create update] do\n      __elasticsearch__.index_document\n    end\n\n    after_commit on: %i[destroy] do\n      __elasticsearch__.delete_document\n    end\n\n    # We serialize our model\'s attributes to JSON, including only the title and category fields.\n    def as_indexed_json(_options = {})\n      as_json(only: %i[title category])\n    end\n\n    # Here we define the index configuration\n    settings settings_attributes do\n      # We apply mappings to the title and category fields.\n      mappings dynamic: false do\n        # for the title we use our own autocomplete analyzer that we defined below in the settings_attributes method.\n        indexes :title, type: :text, analyzer: :autocomplete\n        # the category must be of the keyword type since we\'re only going to use it to filter articles.\n        indexes :category, type: :keyword\n      end\n    end\n\n    def self.search(query, filters)\n      # lambda function adds conditions to the search definition.\n      set_filters = lambda do |context_type, filter|\n        @search_definition[:query][:bool][context_type] |= [filter]\n      end\n\n      @search_definition = {\n        # we indicate that there should be no more than 5 documents to return.\n        size: 5,\n        # we define an empty query with the ability to dynamically change the definition\n        # Query DSL https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html\n        query: {\n          bool: {\n            must: [],\n            should: [],\n            filter: []\n          }\n        }\n      }\n\n      # match all documents if the query is blank.\n      if query.blank?\n        set_filters.call(:must, match_all: {})\n      else\n        set_filters.call(\n          :must,\n          match: {\n            title: {\n              query: query,\n              # fuzziness means you can make one typo and still match your document.\n              fuzziness: 1\n            }\n          }\n        )\n      end\n\n      # the system will return only those documents that pass this filter\n      set_filters.call(:filter, term: { category: filters[:category] }) if filters[:category].present?\n\n      # and finally we pass the search query to Elasticsearch.\n      __elasticsearch__.search(@search_definition)\n    end\n  end\n\n  class_methods do\n    def settings_attributes\n      {\n        index: {\n          analysis: {\n            analyzer: {\n              # we define a custom analyzer with the name autocomplete.\n              autocomplete: {\n                # type should be custom for custom analyzers.\n                type: :custom,\n                # we use a standard tokenizer.\n                tokenizer: :standard,\n                # we apply two token filters.\n                # autocomplete filter is a custom filter that we defined above.\n                # and lowercase is a built-in filter.\n                filter: %i[lowercase autocomplete]\n              }\n            },\n            filter: {\n              # we define a custom token filter with the name autocomplete.\n\n              # Autocomplete filter is of edge_ngram type. The edge_ngram tokenizer divides the text into smaller parts (grams).\n              # For example, the word \xe2\x80\x9cruby\xe2\x80\x9d will be split into [\xe2\x80\x9cru\xe2\x80\x9d, \xe2\x80\x9crub\xe2\x80\x9d, \xe2\x80\x9cruby\xe2\x80\x9d].\n\n              # edge_ngram is useful when we need to implement autocomplete functionality. However, the so-called "completion suggester" - is another way to integrate the necessary options.\n              autocomplete: {\n                type: :edge_ngram,\n                min_gram: 2,\n                max_gram: 25\n              }\n            }\n          }\n        }\n      }\n    end\n  end\nend\n
Run Code Online (Sandbox Code Playgroud)\n

我不确定这after_commit on: %i[create update] do是什么意思。\n我设法找到了此信息https://apidock.com/rails/ActiveRecord/Transactions/ClassMethods/after_commit \n这让我了解了如何使用此语法。\n但我\我仍然不确定这个语法“on:”是如何创建的。这看起来不像 Ruby 的东西。它看起来像是某个东西的 Rails 简写,但它到底是什么?

\n

另外,是否有任何来源列出了 Rails 提供的所有简写?弄清楚某个东西是 Rails 速记还是 Ruby 语法真是太痛苦了。

\n

Jör*_*tag 5

\n

使用“on:”的语法在 Ruby on Rails 中意味着什么?

\n
\n

由于您具体询问的是语法,而不是语义,所以我将回答您有关语法的问题。

\n
\n

这真的很难用谷歌搜索,因为我不知道它是 Ruby 的东西还是 Rails 的东西

\n
\n

这很容易回答:Ruby 不允许修改语法,所以它不可能是Rails 的东西。任何与语法相关的内容都必须是“Ruby 的东西”,因为 Rails 或任何其他用户代码都不能改变 Ruby 的语法。

\n

您所询问的只是基本的 Ruby 语法,仅此而已,与 Rails 无关。

\n
\n

我不确定after_commit on: %i[create update] do是什么意思。

\n
\n

您在这里看到的称为消息发送。(在 Java 或 C# 等其他编程语言以及 Ruby 文档的某些部分中,它可能被称为方法调用,而在 C++ 等编程语言中,它可能被称为虚拟成员函数调用。)更准确地说,它是一条消息使用隐式接收器发送。

\n

消息总是发送到特定的接收者(就像现实世界中的消息一样)。消息发送的一般语法如下所示:

\n
foo.bar(baz, quux: 23) {|garple| glorp(garple) }\n
Run Code Online (Sandbox Code Playgroud)\n

这里,

\n
    \n
  • foo接收者,即接收消息的对象。请注意,foo当然可以是任意任意 Ruby 表达式,例如(2 + 3).to_s,在 中,消息to_s被发送到计算表达式的结果2 + 3,而这实际上只是+发送到计算表达式的结果的消息2,传递计算表达式的结果3作为单个位置参数。
  • \n
  • bar消息选择器,或者简称为消息。它告诉接收者对象要做什么。
  • \n
  • 消息选择器后面的括号包含参数列表。在这里,我们有一个位置参数,它是表达式baz(可以是局部变量或另一条消息发送,稍后会详细介绍),以及一个关键字参数quux,它是具有值 的关键字23。(同样,该值可以是任意任意 Ruby 表达式。)注意:实际上,这不一定是关键字参数。它也可能是一个Hash. 稍后会详细介绍。
  • \n
  • 参数列表之后是​​文字块参数。在 Ruby 中发送的每条消息都可以有一个文本块参数 \xe2\x80\xa6,这取决于调用的方法来忽略它、使用它或对其执行任何操作。
  • \n
  • 块是一段轻量级的可执行代码,因此,就像方法一样,它有一个参数列表和一个主体。参数列表由|管道符号 \xc2\xa0\xe2\x80\x93 分隔,在这种情况下,只有一个名为 的位置参数garple,但它可以具有方法可以具有的所有相同类型的参数,以及块局部变量。当然,主体可以包含任意 Ruby 表达式。
  • \n
\n

现在,重要的是其中很多元素都是可选的:

\n
    \n
  • 您可以省略括号:foo.bar(baz, quux: 23)与 相同foo.bar baz, quux: 23,这也意味着foo.bar()与 相同foo.bar
  • \n
  • 您可以省略显式接收器,在这种情况下,隐式接收器是,即与 相同,当然与 相同。selfself.foo(bar, baz: 23)foo(bar, baz: 23)foo bar, baz: 23
  • \n
  • 如果你把两者放在一起,那就意味着eg与我之前提到的self.foo()相同:如果你只是在没有上下文的情况下自己编写,你实际上不知道它是局部变量还是消息发送。只有当您看到接收者或参数(或两者)时,您才能确定它是一个消息发送,并且只有当您看到同一范围内的赋值时,您才能确定它是一个变量。如果您没有看到这些东西,则可能是其中之一。foofoo
  • \n
  • 您可以省略不使用的块参数列表,也可以完全省略该块。
  • \n
  • 如果参数列表的最后一个参数(显然在块之前,在参数列表的右括号之后传递)是文字Hash则可以省略大括号,即foo.bar(baz, { quux: 23, garple: 42 })也可以写为foo.bar(baz, quux: 23, garple: 42)也可以写成作为foo.bar baz, quux: 23, garple: 42。这就是我之前提到的:传递新式Hash文字的语法和传递关键字参数的语法实际上是相同的。您必须查看方法定义的参数列表才能确定它是两者中的哪一个,并且在 Ruby 2.0 中,当关键字参数和参数是在 Ruby 3.2 中首次引入的。
  • \n
\n

那么让我们剖析一下您在这里看到的语法:

\n
after_commit on: %i[create update] do\n  __elasticsearch__.index_document\nend\n
Run Code Online (Sandbox Code Playgroud)\n

第一层是

\n
after_commit \xe2\x80\xa6 some stuff \xe2\x80\xa6 do\n  \xe2\x80\xa6 some stuff \xe2\x80\xa6\nend\n
Run Code Online (Sandbox Code Playgroud)\n

我们知道这是一个消息发送而不是一个局部变量,因为有一个文字块参数,并且变量不带参数,只有消息发送才带参数。

\n

因此,这是将消息发送after_commit到隐式接收器self(在模块定义主体中只是模块本身),并传递一些参数,包括文字块。

\n

如果我们重新添加可选元素,我们可以看到

\n
after_commit \xe2\x80\xa6 some stuff \xe2\x80\xa6 do\n  \xe2\x80\xa6 some stuff \xe2\x80\xa6\nend\n
Run Code Online (Sandbox Code Playgroud)\n

相当于

\n
self.after_commit(\xe2\x80\xa6 some stuff \xe2\x80\xa6) do\n  \xe2\x80\xa6 some stuff \xe2\x80\xa6\nend\n
Run Code Online (Sandbox Code Playgroud)\n

该块没有参数列表,只有主体。正文的内容是

\n
__elasticsearch__.index_document\n
Run Code Online (Sandbox Code Playgroud)\n

同样,我们知道这index_document是一条消息发送,因为它有一个接收者。每当您看到参数或接收者或两者时,您就知道您有一条消息发送。所以,这是将消息发送index_document到接收者表达式__elasticsearch__

\n

现在,什么是__elasticsearch__?正如我上面提到的,如果没有上下文,我们实际上无法知道它是什么:它可能是没有参数列表的无接收者消息发送,即发送到隐式接收者的消息self,大致相当于self.__elasticsearch__()。或者,它可以是局部变量。解决这种歧义的方法是查看前面的上下文:如果在此之前有一个要__elasticsearch__ 解析的赋值(不一定执行),它将被视为局部变量,否则,将被视为消息发送。

\n

在这种特殊情况下,没有对 进行赋值__elasticsearch__,因此,它必须是一个消息发送,即它正在将消息发送__elasticsearch__到隐式接收者self(这里仍然是模块本身,因为块词法捕获self,尽管这是语言语义,并且您严格询问语法)。

\n

如果我们重新添加可选元素,我们可以看到

\n
__elasticsearch__.index_document\n
Run Code Online (Sandbox Code Playgroud)\n

相当于

\n
self.__elasticsearch__().index_document()\n
Run Code Online (Sandbox Code Playgroud)\n

到目前为止,我们已经剖析了块的主体以及最外层的消息发送。如果我们将迄今为止发现的内容放在一起并将所有可选语法元素添加回来,我们会看到

\n
after_commit on: %i[create update] do\n  __elasticsearch__.index_document\nend\n
Run Code Online (Sandbox Code Playgroud)\n

相当于

\n
self.after_commit(on: %i[create update]) do\n  self.__elasticsearch__().index_document()\nend\n
Run Code Online (Sandbox Code Playgroud)\n

现在,让我们看看参数列表:

\n
(on: %i[create update])\n
Run Code Online (Sandbox Code Playgroud)\n

具体来说,让我们首先关注表达式%i[create update]

\n

这是一个百分比文字,更准确地说,是一个Symbol Array百分比文字。它具有以下形式

\n
    \n
  • %特点
  • \n
  • i特点
  • \n
  • 开始分隔符
  • \n
  • Symbols 由空格分隔
  • \n
  • 结束分隔符
  • \n
\n

如果开始分隔符<是、[(或之一{,则结束分隔符必须是相应的>])}。否则,开始分隔符可以是任意字符,结束分隔符必须是同一字符。

\n

这些百分比文字允许您从空格分隔的裸词Array简洁地创建s。Array

\n

在这种情况下,

\n
%i[create update]\n
Run Code Online (Sandbox Code Playgroud)\n

相当于

\n
[:create, :update]\n
Run Code Online (Sandbox Code Playgroud)\n

如上所述,这里的参数列表的解释存在歧义:这可能是一个关键字参数,on其值是表达式求值的结果[:create, :update] 也可能是Hash相当于 的文字{ :on => [:create, :update] }

\n

如果不知道 的定义是什么样的,我们就无法知道哪个是哪个after_update

\n

所以你有它:

\n

如果使用关键字参数after_update定义,如下所示:

\n
def after_update(on:); end\n
Run Code Online (Sandbox Code Playgroud)\n

那么整个事情将会被解释成这样:

\n
self.after_commit(on: [:create, :update]) do\n  self.__elasticsearch__().index_document()\nend\n
Run Code Online (Sandbox Code Playgroud)\n

而 ifafter_update是用位置参数定义的,如下所示:

\n
def after_update(condition); end\n
Run Code Online (Sandbox Code Playgroud)\n

那么整个事情将会被解释成这样:

\n
self.after_commit({ :on => [:create, :update] }) do\n  self.__elasticsearch__().index_document()\nend\n
Run Code Online (Sandbox Code Playgroud)\n

这涉及以下语法元素:

\n
    \n
  • 消息发送
  • \n
  • 参数\n
      \n
    • 任一关键字参数
    • \n
    • 或位置参数\n
        \n
      • 带有尾随Hash文字
      • \n
      \n
    • \n
    \n
  • \n
  • 块文字
  • \n
  • 百分比文字
  • \n
\n
\n

但我仍然不确定这个语法“on:”是如何创建的。

\n
\n

目前还不太清楚“这个语法是如何创建的”是什么意思。所有Ruby 语法的创建方式(事实上,任何编程语言的所有语法都是创建的)是通过在编程语言规范中写下语法规则来创建的。现在,不幸的是,Ruby 没有一个统一的规范文档,但例如,您可以在ISO/IEC 30170:2012信息技术 \xe2\x80\x94 编程语言 \xe2\x80\x94中找到部分语法规范红宝石规范您还可以在ruby​​/spec中找到零碎内容,例如百分比Symbol Array文字。其他来源包括从 YARV 源代码生成的 RDoc 文档Ruby 问题跟踪器邮件列表,特别是ruby​​-core(英语)ruby​​-dev(日语)邮件列表。

\n