代码之家  ›  专栏  ›  技术社区  ›  lost9123193

Ruby on Rails中的变量语法

  •  1
  • lost9123193  · 技术社区  · 5 年前

    我正在复习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 在“模型”文件夹中初始化。但是到底是什么 :post : 卑鄙又在哪里 params 是从哪里来的?

    2 回复  |  直到 5 年前
        1
  •  3
  •   tadman    5 年前

    这里有两件事。一个是 实例变量 ,或 @ -前缀变量。这些是对象实例的本地。那就是 @posts 任何方法都可以访问 在同一对象实例上调用 是的。这些不在对象实例之间共享。

    如果你熟悉javascript,这很像 this.posts 其中 this part的意思是“这个对象的本地”,而不是“某个名为 posts “。

    rails将这些作为从控制器到视图共享数据的一种方式。您定义的任何实例变量都会传播到视图实例中,在那里可以使用它们。这是Rails特有的行为。

    这个 :post 事物是一个符号,或者 内部字符串 是的。从概念上讲,符号只是表示特定名称的常量每一个实例 :发布 指的是同一个对象,这意味着它们在存储和比较方面非常便宜。

    当比较两个字符串时,需要做更多的工作。 "post" == "post" 需要做的计算比 :post == :post 是的。你会看到很多Ruby哈希结构中使用的符号键,它们通常被用来表示一些东西,比如方法调用或类名,如 belongs_to :model_type 等等。

    把它们想象成“单子弦”。

    Ruby编程语言最奇特的地方之一是它对符号的依赖程度以及符号与字符串的混淆程度,因为许多方法要么采用要么不采用,而有些方法则非常特殊。

    当你在概念上习惯它们的时候,它们是非常方便的,但是它不是你在其他语言中看到的东西,比如C++、Python、JavaScript或者你有什么。

        2
  •  2
  •   mrzasa    5 年前

    @posts 是实例变量-将其视为对象的私有字段或私有属性。

    :post 是一个符号-类似于在本例中用作哈希键的常量字符串(在 params[:id] )或作为指示允许哪些参数的键(在 params.require(:post).permit(:title, :body)

        3
  •  0
  •   3limin4t0r    5 年前

    @post 是一个实例变量。实例变量是只要类的实例存在就存在的变量。普通变量的作用域要窄得多,让我举个例子:

    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 使用计数器,您将得到:

    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 这个 柜台 used是尚未初始化的变量。

    您可以阅读更多关于实例变量的信息 here .


    现在是问题的第二部分 “到底怎么办 :post 用“中庸” 是的。Ruby有一些字符串,您应该从JavaScript中了解这些字符串ruby也有符号,它们类似于字符串,但是是全局单例对象。它们具有比字符串更快的查找和比较时间,但一旦使用了符号,它将保持加载在符号寄存器中( Symbol.all_symbols )在整个课程期间它们主要用作散列(其他语言中的dict)的方法和标签的选项。

    为了向您展示我对singleton对象的含义,让我提供一个示例:

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

    如您所见,即使我们似乎创建了两个实例,对象仍然保持不变。第一次 :foo 当Ruby第二次检测到 :foo公司 已经在寄存器中并返回该符号。

    为了向您展示差异,让met演示相同的内容,但使用字符串:

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

    如您所见,两个字符串都有不同的对象id(尽管包含相同的内容),这使它们成为不同的对象。

    你可以阅读更多关于符号的内容 here .


    你问题的最后一部分没有 “在哪里 params 从哪里来的?” ,我们将不得不看看Ruby on Rails框架你的 PostController 继承自 ApplicationController 又继承自 ActionController::Base 它提供了一些帮助方法。 参数 是其中一种方法。

    在Ruby中,括号在调用方法时是可选的,在调用不带参数的方法时通常是不使用的这就是这里发生的事情,通过运行代码:

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

    你先打电话给 params 方法。此方法继承自 ActionController::基本 并返回web请求的参数。然后你打电话 require 在返回的参数上,它获取符号/标签下的值 :发布 ,如果它不存在异常 ActionController::ParameterMissing 被提出(这是 要求 ). 然后你打电话 permit 带参数的返回值 :title :body ,这允许这些标签/符号及其值。

    为了总结,下面这一行将所有内容联系在一起:

    @post = Post.new(post_params)
    

    说,将post的新实例分配给实例变量 @岗位 是的。您将传递 post_params 方法在这种情况下 :标题 :正文 是白名单,将在初始化期间使用。


    最后要知道的是,Rails做了一些幕后工作,使控制器中的实例变量转移到视图中所以当你分配 @岗位 一个值,您将能够在正在呈现的视图中访问它。

        4
  •  0
  •   Jörg W Mittag    5 年前

    首先,非常重要的是, 没有“ruby on rails中的变量语法” 是的。

    ruby on rails只是一个用ruby编写的框架。ruby不允许用户代码更改基本语言语法。因此,Ruby on Rails中的变量语法与Sinatra或Padrino中的变量语法完全相同,或者其他什么,它只是Ruby中的变量语法这与ECMAScript相同,ECMAScript中也没有“Express中的变量语法”或“JQuery中的变量语法”。

    事实上,几乎所有语言的工作原理都是这样的:允许用户代码改变语法的语言,更不用说改变像变量这样基本的东西的语法,是主流之外的一小部分。

    据我所知 @posts @ 是从 Post 在“模型”文件夹中初始化。

    我必须承认,我很难理解你的意思ruby没有“文件夹”的概念,所以这完全不相关。我不明白你所说的“实例化自”是什么意思但我还是会尝试回答:

    @岗位 是一个 instance variable . 与ecmascript中的最接近的模拟是 private instance field . 实例变量属于实例(duh)aka对象,而不是您最常看到的其他类型的变量: local variables belong to the lexical scope 是的。

    注意这是 不像 private 例如Java中的字段,其中 其他 允许相同类型的对象访问 私有的 领域。ruby具有真正的面向对象封装,其中 只有 允许对象本身访问其实例变量(我相信ecmascript建议具有相同的语义。)

    但是到底是什么 :post : 中庸和

    这是一个 Symbol 字面意义的。ECMAScript也有 Symbol s ,但与Ruby不同,它没有文本语法。

    红宝石遗传 符号 从它的祖先口齿不清和小声说话。顺便说一下,这些也是ecmascript的祖先(lisp via scheme和smalltalk via newtonscript–act-1–self),所以毫不奇怪两者之间有相似之处。

    就像在ecmascript、smalltalk和lisp中一样, 符号 在Ruby中是一种数据类型,表示“label”或“name”的概念所以,当你需要“命名”某个东西时,你可以使用 符号 是的。例如,定义方法时,方法定义的计算结果为 符号 以下内容:

    def foo; end
    #=> :foo
    

    当您向Ruby请求对象方法的列表时,它将返回 Array 属于 符号 S:

    ''.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__]
    

    当您要求露比在两种方法之间创建别名时,您将给它提供现有方法和别名的名称。 符号 学生:

    alias_method :foo, :bar
    

    等等。

    Ruby和ECMAScript有两个不同之处 符号 不过,是s。

    第一个是 句法的 区别,我在上面提到过:ruby有 符号 s公司( :foo ),ECMAScript没有。

    第二个是重要的 语义的 差别。(你只问过语法,但我相信这种差异对于有ecmascript背景的人来说很重要。)在ruby中, 符号 s总是被拘留,这意味着当你提到 符号 按姓名, 总是一样的 符号 . 换句话说:

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

    等等。

    而在ECMAScript中 完全相反 是真的:两个 符号 S是 从未 一模一样, 即使他们同名 以下内容:

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

    这允许 符号 在ecmascript中用作不可伪造的安全令牌。例如,如果我想确定 只有 我信任的人可以调用特定的方法,然后我可以将该方法存储在名为 符号 :

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

    以及 没有人 可以打电话 bar ,因为它是 不可能的 建造 符号 与属性键匹配。只有当我 某人 准确的 符号 我以前存储方法,他们能访问它吗。(事实上,甚至我都不能调用此方法,因为我忘记存储 符号 在某个地方,现在连我都不能再建造它了!)

    [注意:要使其真正工作,我还必须使属性不可枚举,否则我仍然可以通过迭代对象来发现它。]

    鲁比做不到 符号 S.

    ECMAScript有一个 全局符号注册表 ,我可以存储和检索 符号 从它使用 Symbol.for 是的。鲁比的 符号 s的行为就像总是使用 符号。 在ECMAScript中。

    在哪里 params 是从哪里来的?

    如果你问这个问题时没有你所贴的上下文,答案是:它可以 任何一个 是局部变量 消息发送(在ecmascript中称为“方法调用”),带有隐式接收器,没有参数列表。从语法上讲,在ruby中不可能区分解除对局部变量的引用和使用隐式接收器和无参数列表发送消息。在ECMAScript,由于方法调用,这种歧义是不存在的。 总是 需要参数列表,即使它是空的。

    为了知道它是消息还是局部变量,我们必须考虑上下文:当变量的第一个赋值是 已分析 . (注意,这一点很重要:它是在赋值为 已分析 ,而不是当它是 评价的 . 例如。 if false then foo = 42 end 将创建局部变量 foo (第三章)

    在你的代码中,我们可以清楚地看到 param 在本地范围内(事实上, 参数 是作用域中的第一个标记,因此没有“before”),这意味着它不能是局部变量,必须是消息发送。

    因此相当于

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