Синтаксис переменной в Ruby on Rails - PullRequest
2 голосов
/ 01 ноября 2019

Я перехожу учебник по 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 и что это такое?

Ответы [ 4 ]

5 голосов
/ 01 ноября 2019

Здесь есть две вещи. Одним из них является переменные экземпляра или переменные с префиксом @. Они являются локальными для экземпляра объекта. То есть @posts доступен любому методу , вызываемому для того же экземпляра объекта . Они не разделяются между экземплярами объекта.

Если вы знакомы с JavaScript, это очень похоже на this.posts, где часть this означает «локально для этого объекта» вместо «некоторой переменной с именем * 1011». * ".

Rails использует их как способ обмена данными от контроллера к представлению. Любые переменные экземпляра, которые вы определяете, распространяются на экземпляр представления, где они могут быть использованы. Такое поведение довольно уникально для Rails.

:post вещь - это Symbol, или внутренняя строка . Концептуально символ - это просто константа, представляющая это конкретное имя. Каждый экземпляр :post ссылается на один и тот же объект, поэтому это означает, что они очень недороги с точки зрения хранения, а сравнение тривиально.

При сравнении двух строк требуется гораздо больше работы. "post" == "post" требует гораздо больше вычислений, чем :post == :post. Вы увидите, что символьные ключи часто используются в хеш-структурах Ruby, и они часто используются для представления таких вещей, как вызовы методов или имена классов, как в belongs_to :model_type и т. Д.

Думайте о них как"singleton strings".

Одна из наиболее специфических особенностей языка программирования Ruby заключается в том, насколько сильно он опирается на символы и как символы легко спутать со строками, потому что многие методы будут использовать или не заботиться, в то время как некоторыедругие очень специфичны.

Они действительно очень удобны, когда вы к ним привыкли концептуально, но это не то, что вы видите в других языках, таких как C ++, Python, JavaScript или что-то еще.

4 голосов
/ 01 ноября 2019

Прежде всего, и что очень важно, нет такой вещи, как "переменный синтаксис в Ruby on Rails" .

Ruby on Rails - это просто фреймворк, написанный на Ruby. Ruby не позволяет пользовательскому коду изменять основной синтаксис языка. Таким образом, синтаксис переменной в Ruby on Rails точно такой же, как и синтаксис переменной в Sinatra, Padrino или чем-то еще, это просто синтаксис переменной в Ruby. Это идентично ECMAScript, где также отсутствует «переменный синтаксис в Express» или «переменный синтаксис в JQuery».

На самом деле почти все языки работают;языки, в которых пользовательскому коду разрешено изменять синтаксис, не говоря уже о том, чтобы изменять синтаксис чего-либо как основополагающего, поскольку переменные представляют собой незначительное меньшинство за пределами основного потока.

Из того, что я понимаю @posts с помощью @ - это переменная, которая создается из класса Post в папке моделей.

Должен признать, у меня проблемы с анализом того, что вы имеете в виду. В Ruby нет понятия «папка», так что это совершенно неактуально. И я не понимаю, что вы имеете в виду под "экземплярами". Но я все равно попытаюсь ответить:

@posts - это переменная экземпляра . Наиболее близким аналогом к этому в ECMAScript будет частное поле экземпляра . Переменные экземпляра принадлежат экземплярам (duh), то есть объектам, в отличие от других типов переменных, которые вы чаще всего видите: локальные переменные относятся к лексической области видимости .

Обратите внимание, что это в отличие от поля private, например, в Java, где другим объектам того же типа разрешен доступ к полям private. Ruby имеет истинную объектно-ориентированную инкапсуляцию, где только самому объекту разрешен доступ к его переменным экземпляра. (Я считаю, что предложение ECMAScript имеет ту же семантику.)

Но что именно означает :post со средним значением : и

Это Symbol буквальный. ECMAScript также имеет Symbol s , но в отличие от Ruby он не имеет буквального синтаксиса для них.

Ruby унаследовал Symbol s от своих предков Lisp и Smalltalk. Между прочим, они также являются предками ECMAScript (Lisp через Scheme и Smalltalk через NewtonScript → Act-1 → Self), поэтому неудивительно, что между ними есть сходство.

Как и в ECMAScript, Smalltalk,и Lisp, Symbol в Ruby - это тип данных, который обозначает концепцию «метки» или «имени». Поэтому, когда вам нужно «назвать» что-то, вы используете Symbol. Например, когда вы определяете метод, определение метода оценивается как Symbol:

def foo; end
#=> :foo

Когда вы запрашиваете у Ruby список методов объекта, он возвращает Array из Symbol 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__]

Когда вы просите Ruby создать псевдоним между двумя методами, вы дадите ему имя существующего метода и псевдоним как Symbol s:

alias_method :foo, :bar

и т. Д.

Между Ruby и ECMAScript Symbol s есть два различия.

Первое - это синтаксическая разница, и я намекалвыше: 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 с никогда идентичны , даже если они имеют одинаковое имя :

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

Это позволяет использовать Symbol в ECMAScript в качестве не поддающихся фальсификации маркеров безопасности. Например, если я хочу убедиться, что только тот, кому я доверяю, может вызвать определенный метод, тогда я могу сохранить метод в свойстве, имя которого Symbol:

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

И никто не сможет вызвать bar, потому что невозможно построить Symbol, который соответствует ключу свойства. Только когда я дам кому-то точное Symbol, которое я использовал для хранения метода, они могут получить к нему доступ. (На самом деле, даже я не могу вызвать этот метод, потому что я забыл хранить где-то Symbol, и теперь даже я не могу создать его снова!)

[Примечание: чтобы это действительно работало, ятакже пришлось бы сделать свойство не перечисляемым, в противном случае я все еще мог бы обнаружить его, перебирая объект.]

Этого нельзя сделать с Ruby Symbol s.

ECMAScript имеет Глобальный регистр символов , и я могу сохранять и извлекать из него Symbol с помощью Symbol.for. Symbol в Ruby ведут себя как всегда, используя Symbol.for в ECMAScript.

, откуда берется params и что это?

Если вы спрашивали этовопрос без контекста, который вы разместили, ответ будет таким: или может быть локальной переменной или отправка сообщения (в ECMAScript это называется "вызовом метода") снеявный получатель и без списка аргументов. Синтаксически, в Ruby невозможно различить разыменование локальной переменной и отправку сообщения с неявным получателем и без списка аргументов. В ECMAScript эта неоднозначность не существует, так как вызов метода всегда нуждается в списке аргументов, даже если он пустой.

Чтобы узнать, является ли это сообщение или локальная переменная, мы должны рассмотреть контекст: локальная переменная создается, когда первое присваивание переменной проанализировано . (Обратите внимание, это важно: оно создается, когда присвоение проанализировано , а не когда оно оценено . Например, if false then foo = 42 end создаст локальную переменную foo.)

В вашем коде мы ясно видим, что до param s в локальной области видимости нет присваивания ранее (на самом деле, params является первым токеном в области, поэтому «до» нет)Это означает, что она не может быть локальной переменной, это должно быть отправленное сообщение.

Следовательно, оно эквивалентно

this.params().require(Symbol.for('post')).permit(Symbol.for('title'), Symbol.for('body'))
3 голосов
/ 01 ноября 2019

@posts - это переменная экземпляра - думайте о ней как о частном поле или частном свойстве объекта.

:post - это символ - что-то вроде постоянной строки, которая в этом случае использует какхэш-ключ (в params[:id]) или в качестве ключа, указывающего, какие параметры разрешены (в params.require(:post).permit(:title, :body)

2 голосов
/ 01 ноября 2019

@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 используется counter переменная, которая не была инициализирована.

Подробнее о переменной экземпляра можно прочитать здесь .


Теперь для второй части вопроса «что именно означает :post со значением: mean» . В Ruby есть строки, которые вы должны знать из JavaScript. В Ruby также есть символы, они похожи на строку, но являются глобальными одноэлементными объектами. Они быстрее ищут и сравнивают время, чем строки, но как только символ используется, он остается загруженным в регистр символов (Symbol.all_symbols) в течение всей продолжительности вашей программы. В основном они используются в качестве опций для методов и меток для хэшей (dicts на других языках).

Чтобы показать вам, что я имею в виду с одноэлементным объектом, приведу пример:

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

Мне нравитсяВы можете видеть, что объект остается тем же самым, хотя мы, казалось бы, создали два экземпляра. При первом использовании :foo он создается и добавляется в регистр, второй раз Ruby обнаруживает, что :foo уже находится в регистре, и возвращает этот символ.

Чтобы показать разницу, которую продемонстрировал metто же самое, но со строками:

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

Как вы можете видеть, обе строки имеют разные идентификаторы объектов (хотя и содержат одинаковое содержимое), что делает их разными объектами.

Вы можете прочитатьподробнее о символах здесь .


Нет, для последней части вашего вопроса "откуда взято params и что это?" , мыПридется взглянуть на фреймворк Ruby on Rails. Ваш PostController наследуется от ApplicationController, который, в свою очередь, наследуется от ActionController::Base, который предоставляет вам несколько вспомогательных методов. params является одним из таких методов.

В Ruby круглые скобки являются необязательными при вызове методов и в основном не используются при вызове метода без аргументов. Вот что происходит здесь, запустив код:

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

Сначала вы вызовете метод params. Этот метод наследуется от ActionController::Base и возвращает параметры веб-запроса. Затем вы вызываете require для возвращаемых параметров, который выбирает значение под символом / меткой :post, если его нет, возникает исключение ActionController::ParameterMissing (это часть поведенияrequire). Затем вы вызываете permit для возвращенного значения с аргументами :title и :body, это разрешает эти метки / символы с их значениями.

Чтобы сделать выводы, следующие связи строквсе вместе:

@post = Post.new(post_params)

Говорит, назначьте новый экземпляр post переменной экземпляра @post. Вы передадите возвращаемое значение метода post_params конструктору. В этом случае :title и :body занесены в белый список и будут использоваться во время инициализации.


Последнее, что нужно знать, это то, что Rails делает некоторые вещи позади сцены, чтобы сделать переменные экземпляра в контроллереперенести на вид. Поэтому, когда вы назначите @post значение, вы сможете получить к нему доступ в представлении, которое вы отображаете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...