簡體   English   中英

Ruby on Rails 中的變量語法

[英]Variable Syntax in Ruby on Rails

我正在閱讀 ruby on rails 教程,但我仍然對語法有些困惑。

class PostController < ApplicationController
    def index
        @posts = Post.all
    end

    def create
        @post = Post.new(post_params)
    end 

private
    def post_params
        params.require(:post).permit(:title, :body)
    end
end

我來自 javascript 背景。

據我了解@posts@是從模型文件夾中的Post class 實例化的變量。 但是:post:到底是什么意思, params來自哪里,它是什么?

這里有兩件事。 一種是實例變量,或以@為前綴的變量。 這些是 object 實例的本地文件。 也就是說,在同一個 object 實例上調用的任何方法都可以訪問@posts 這些不在 object 實例之間共享。

如果您熟悉 JavaScript,這很像this.posts ,其中this部分的意思是“此對象的本地”而不是“一些名為posts的變量”。

Rails 使用這些作為從 controller 到視圖共享數據的一種方式。 您定義的任何實例變量都會傳播到可以使用它們的視圖實例。 這是 Rails 獨有的行為。

:post是一個符號,或者是一個內部化的字符串 從概念上講,符號只是代表該特定名稱的常數。 :post每個實例都指向相同的 object,因此這意味着它們在存儲方面非常便宜,並且比較是微不足道的。

在比較兩個字符串時,需要做更多的工作。 "post" == "post":post ==:post需要做更多的計算。 您會在 Ruby hash 結構中看到很多使用的符號鍵,它們通常用於表示事物,例如方法調用或belongs_to:model_type名稱等。

將它們視為“單例字符串”。

關於 Ruby 編程語言的一個更奇特的事情是它對符號的依賴程度以及符號如何容易與字符串混淆,因為許多方法會采取非此即彼的方式,而另一些則非常特殊。

當你在概念上習慣它們時,它們真的很方便,但它不是你在其他語言中看到的東西,比如 C++、Python、JavaScript 或其他語言。

首先,非常重要的是,沒有“Ruby on Rails 中的變量語法”這樣的東西

Ruby on Rails 只是一個用 Ruby 編寫的框架。 Ruby 不允許用戶代碼更改基本語言語法。 因此,Rails 上 Ruby 中的變量語法與 Sinatra 或 Padrino 中的變量語法完全相同,只是 Ruby 中的變量語法。 這與 ECMAScript 相同,其中也沒有“Express 中的可變語法”或“JQuery 中的可變語法”。

事實上,這就是所有語言的工作原理。 允許用戶代碼更改語法的語言,更不用說更改像變量這樣基本的語法是主流之外的極少數方式。

據我了解@posts@是從模型文件夾中的Post class 實例化的變量。

我必須承認我無法解析你的意思。 Ruby 沒有“文件夾”的概念,因此完全不相關。 而且我不明白您所說的“實例化”是什么意思。 但無論如何我都會嘗試回答:

@posts是一個實例變量 與 ECMAScript 中最接近的類似物是私有實例字段 實例變量屬於實例(duh)又名對象,而不是您最常看到的其他類型的變量: 局部變量屬於詞法 scope

請注意,這例如 Java 中的private字段不同,其中允許相同類型的其他對象訪問private字段。 Ruby 具有真正的面向對象封裝,其中允許 object 本身訪問其實例變量。 (我相信 ECMAScript 提案具有相同的語義。)

但是:post:到底是什么意思和

這是一個Symbol文字。 ECMAScript 也有Symbol s ,但與 Ruby 不同,它沒有文字語法。

Ruby 從其祖先 Lisp 和 Smalltalk 繼承了Symbol 順便說一句,它們也是 ECMAScript 的祖先(Lisp 通過 Scheme 和 Smalltalk 通過 NewtonScript → Act-1 → Self),因此兩者之間有相似之處也就不足為奇了。

就像在 ECMAScript、Smalltalk 和 Lisp 中一樣,Ruby 中的Symbol是表示“標簽”或“名稱”概念的數據類型。 因此,每當您需要“命名”某些東西時,您都可以使用Symbol 例如,當您定義方法時,方法定義的計算結果為Symbol

def foo; end
#=> :foo

當您向 Ruby 詢問對象的方法列表時,它將返回SymbolArray

''.methods
#=> [:unpack1, :encode!, :include?, :%, :*, :+, :count, :partition, :sum, :next, :casecmp, :casecmp?, :insert, :<=>, :bytesize, :match?, :succ!, :match, :==, :===, :next!, :=~, :index, :[], :[]=, :getbyte, :rindex, :replace, :upto, :chr, :scrub, :empty?, :eql?, :undump, :scrub!, :setbyte, :byteslice, :clear, :+@, :-@, :capitalize, :upcase, :downcase, :downcase!, :dump, :upcase!, :split, :capitalize!, :swapcase!, :freeze, :inspect, :grapheme_clusters, :lines, :swapcase, :oct, :codepoints, :crypt, :bytes, :hex, :concat, :ljust, :length, :size, :chars, :succ, :scan, :reverse, :reverse!, :chop, :<<, :strip, :end_with?, :lstrip, :prepend, :rjust, :to_str, :to_sym, :intern, :delete_prefix, :chomp, :sub!, :to_s, :to_i, :to_f, :delete_suffix, :lstrip!, :gsub!, :chop!, :center, :sub, :ord, :start_with?, :delete_prefix!, :delete_suffix!, :chomp!, :rstrip, :delete, :rstrip!, :gsub, :tr_s!, :tr, :tr_s, :strip!, :squeeze, :tr!, :each_codepoint, :delete!, :squeeze!, :each_line, :each_byte, :each_char, :force_encoding, :each_grapheme_cluster, :hash, :slice!, :rpartition, :encoding, :unpack, :b, :valid_encoding?, :slice, :unicode_normalize, :unicode_normalize!, :unicode_normalized?, :ascii_only?, :to_c, :to_r, :encode, :clamp, :<=, :between?, :>=, :>, :<, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :methods, :untrusted?, :trust, :singleton_methods, :tainted?, :private_methods, :untrust, :frozen?, :method, :public_send, :public_method, :singleton_method, :protected_methods, :define_singleton_method, :extend, :to_enum, :enum_for, :!~, :respond_to?, :object_id, :send, :display, :singleton_class, :nil?, :class, :yield_self, :clone, :dup, :itself, :untaint, :then, :taint, :!, :equal?, :!=, :instance_eval, :instance_exec, :__id__, :__send__]

當您要求 Ruby 在兩個方法之間創建別名時,您將給它現有方法的名稱和別名為Symbol s:

alias_method :foo, :bar

等等。

不過,Ruby 和 ECMAScript 的Symbol之間有兩個區別。

第一個是語法差異,我在上面提到過:Ruby 具有Symbol s ( :foo ) 的字面語法,ECMAScript 沒有。

第二個是重要的語義差異。 (您只詢問了語法,但我相信這種差異對於具有 ECMAScript 背景的人來說很重要。)在 Ruby 中, Symbol始終是實習的,這意味着當您通過名稱引用Symbol時,它始終是相同的Symbol 換句話說:

:foo.equal?(:foo)
#=> true

'foo'.to_sym.equal?('foo'.to_sym)
#=> true

:foo.equal?('foo'.to_sym)
#=> true

等等。

而在 ECMAScript 中,情況正好相反:兩個Symbol永遠不會相同,即使它們具有相同的 name

Symbol('foo') === Symbol('foo')
//=> false

這允許將 ECMAScript 中的Symbol用作不可偽造的安全令牌。 例如,如果我想確保只有我信任的人才能調用特定方法,那么我可以將該方法存儲在名稱為Symbol的屬性中:

const foo = {
    [Symbol('bar')]: () => console.log('Hello')
}

而且沒有人可以調用bar ,因為不可能構造一個與屬性鍵匹配的Symbol 只有當我某人我用來存儲該方法的確切Symbol時,他們才能訪問它。 (事實上,連我都不能調用這個方法,因為我忘了把Symbol存放在某個地方,現在連我都不能再構造它了!)

[注意:要使其真正起作用,我還必須使該屬性不可枚舉,否則我仍然可以通過遍歷 object 來發現它。]

這不能用 Ruby Symbol s 來完成。

ECMAScript 有一個全局符號注冊表,我可以使用Symbol.for從中存儲和檢索Symbol Ruby 的Symbol行為就像在 ECMAScript 中使用Symbol.for一樣。

params來自哪里,它是什么?

如果您在沒有發布的上下文的情況下問這個問題,答案將是:它可以是局部變量消息發送(在 ECMAScript 中,它將被稱為“方法調用”),具有隱式接收器且沒有參數列表。 從語法上講,在 Ruby 中,不可能區分取消引用局部變量和發送帶有隱式接收器且沒有參數列表的消息。 在 ECMAScript 中,這種歧義是不存在的,因為方法調用總是需要一個參數列表,即使它是空的。

為了知道它是消息還是局部變量,我們必須考慮上下文:局部變量是在對變量的第一次賦值被解析時創建的。 (注意,這很重要:它是在解析賦值時創建的,而不是在評估時創建的。例如if false then foo = 42 end將創建局部變量foo 。)

在你的代碼中,我們可以清楚的看到本地scope之前的params沒有賦值(其實paramsparam中的第一個token,所以沒有“before”),也就是說不能是本地的變量,它必須是消息發送。

因此它等價於

this.params().require(Symbol.for('post')).permit(Symbol.for('title'), Symbol.for('body'))

@posts是一個實例變量 - 將其視為 object 的私有字段或私有屬性。

:post是一個符號 - 類似於一個常量字符串,在這種情況下用作 hash 鍵(在params[:id]中)或作為指示允許哪些參數的鍵(在params.require(:post).permit(:title, :body)

@post是一個實例變量。 實例變量是與 class 的實例一樣存在的變量。 普通變量的 scope 要窄得多,我舉個例子:

class Counter
  def initialize(start = 0)
    @counter = start
  end

  def increment
    @counter += 1
  end
end

counter = Counter.new
counter.increment #=> 1
counter.increment #=> 2

如果你在哪里用 counter 替換@counter你會得到:

class Counter
  def initialize(start = 0)
    counter = start
  end

  def increment
    counter += 1
  end
end

counter = Counter.new
counter.increment # NoMethodError (undefined method `+' for nil:NilClass)

不同之處在於counter在構造函數中設置,但僅在該方法中可用。 因此,當調用increment時,使用的counter是一個尚未初始化的變量。

您可以在此處閱讀有關實例變量的更多信息。


現在對於問題的第二部分:post和: 到底是什么意思” Ruby 有你應該從 JavaScript 知道的字符串。 Ruby 也有符號,它們類似於字符串,但是是全局 singleton 對象。 它們比字符串具有更快的查找和比較時間,但是一旦使用了符號,它就會在整個程序持續時間內保持加載在符號寄存器 ( Symbol.all_symbols ) 中。 它們主要用作哈希方法和標簽的選項(其他語言中的字典)。

為了向您展示我對 singleton object 的意思,讓我舉一個例子:

:foo.object_id #=> 1686748
:foo.object_id #=> 1686748

如您所見,object 保持不變,即使我們似乎創建了兩個實例。 第一次使用:foo時,它被創建並添加到寄存器中,第二次 Ruby 檢測到:foo已經在寄存器中並返回該符號。

為了向您展示差異,讓 met 演示相同的內容,但使用字符串:

'foo'.object_id #=> 24742300
'foo'.object_id #=> 26029360

如您所見,兩個字符串都有不同的 object id(盡管包含相同的內容),這使它們成為不同的對象。

您可以在此處閱讀有關符號的更多信息。


對於您問題的最后一部分params來自哪里,它是什么?” ,我們必須看看 Ruby on Rails 框架。 您的PostController繼承自ApplicationController ,后者又繼承自ActionController::Base ,后者為您提供了一些輔助方法。 params就是其中一種方法。

在 Ruby 中,圓括號在調用方法時是可選的,並且在調用沒有 arguments 的方法時幾乎不用。 這就是這里發生的事情,通過運行代碼:

params.require(:post).permit(:title, :body)

您將首先調用params方法。 該方法繼承自ActionController::Base ,返回web請求的參數。 然后對返回的參數調用require ,它會獲取符號/標簽:post下的值,如果它不存在,則會引發異常ActionController::ParameterMissing (這是require行為的一部分)。 然后,您使用 arguments :title:body對返回的值調用permit ,這允許這些標簽/符號及其值。

總而言之,以下行將所有內容聯系在一起:

@post = Post.new(post_params)

說,將 post 的新實例分配給實例變量@post 您將post_params方法的返回值傳遞給構造函數。 在這種情況下:title:body被列入白名單並將在初始化期間使用。


最后要知道的一件事是,Rails 做了一些幕后工作,以使 controller 中的實例變量轉移到視圖中。 因此,當您@post分配一個值時,您將能夠在您正在呈現的視圖中訪問它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM