The behavior of a local Ruby variable that obscures the instance method

I recently read a blog post about Ruby's behavior regarding a local variable that obscures a method (differently, for example, a block variable that obscures a local variable of a method , which is also mentioned in this StackOverflow thread ), and I found some behavior that I don’t quite understand .

A Ruby document states that :

[V] valid and method names are almost identical. If you have not assigned one of these ambiguous names, Ruby will assume that you want to call the method. After you have assigned the name Ruby, it is assumed that you want to refer to a local variable.

So, given the following class example

# person.rb

class Person
  attr_accessor :name

  def initialize(name = nil)
    @name = name
  end

  def say_name
    if name.nil?
      name = "Unknown"
    end

    puts "My name is #{name.inspect}"
  end
end

, , :

  • name.nil? name, attr_accessor
  • Ruby name = "Unknown" #say_name, name, ,
  • , Person name, , name, #say_name, nil

, irb:

irb(main):001:0> require "./person.rb"
true
# `name.nil?` using instance method fails,
# `name` local variable not assigned
irb(main):002:0> Person.new("Paul").say_name
My name is nil
nil
# `name.nil?` using instance method succeeds
# as no name given on initialisation,
# `name` local variable gets assigned
irb(main):003:0> Person.new.say_name
My name is "Unknown"
nil

, Pry, , name, :

irb(main):002:0> Person.new("Paul").say_name

From: /Users/paul/person.rb @ line 13 Person#say_name:

    10: def say_name
    11:   binding.pry
    12:
 => 13:   p name
    14:   if name.nil?
    15:     name = "Unknown"
    16:   end
    17:
    18:   puts "My name is #{name.inspect}"
    19: end

[1] pry(#<Person>)> next
"Paul"

, , , name . , name...

From: /Users/paul/person.rb @ line 14 Person#say_name:

    10: def say_name
    11:   binding.pry
    12:
    13:   p name
 => 14:   if name.nil?
    15:     name = "Unknown"
    16:   end
    17:
    18:   puts "My name is #{name.inspect}"
    19: end
[2] pry(#<Person>)> name
nil

Err... . name , , , , ... , - name = "Unknown", ...?

[3] pry(#<Person>)> exit
My name is nil
nil

, , . , ?

  • name.nil?, name? ?
  • , Pry?
  • - ?

:

 [ruby]$ ruby -v
ruby 2.4.2p198 (2017-09-14 revision 59899) [x86_64-darwin16]

  • ( ) , , .
  • , , .
  • , - , , , self.name name().

, , Pry. Person.new("Paul").say_name:

From: /Users/paul/person.rb @ line 13 Person#say_name:

    10: def say_name
    11:   binding.pry
    12:
 => 13:   p name
    14:   if name.nil?
    15:     name = "Unknown"
    16:   end
    17:
    18:   puts "My name is #{name.inspect}"
    19: end

p , , Pry, name:

[1] pry(#<Person>)> name
nil

, , Ruby , , , . p ...

[2] pry(#<Person>)> next
"Paul"

... name, .

, ? - ? , Pry name, , Ruby name?

+4
2

name , , , , Pry, binding, , , . :

def say_name
  puts "--- Before assignment of name: ---"
  puts "defined?(name) : #{defined?(name).inspect}"
  puts "binding.local_variable_defined?(:name) : #{binding.local_variable_defined?(:name).inspect}"

  puts "local_variables : #{local_variables.inspect}"
  puts "binding.local_variables : #{binding.local_variables.inspect}"

  puts "name : #{name.inspect}"
  puts "binding.eval('name') : #{binding.eval('name').inspect}"

  if name.nil?
    name = "Unknown"
  end

  puts "--- After assignment of name: ---"
  puts "defined?(name) : #{defined?(name).inspect}"
  puts "binding.local_variable_defined?(:name) : #{binding.local_variable_defined?(:name).inspect}"

  puts "local_variables : #{local_variables.inspect}"
  puts "binding.local_variables : #{binding.local_variables.inspect}"

  puts "name : #{name.inspect}"
  puts "binding.eval('name') : #{binding.eval('name').inspect}"

  puts "My name is #{name.inspect}"
end

Person.new("Paul").say_name :

--- Before assignment of name: ---
defined?(name) : "method"
binding.local_variable_defined?(:name) : true
local_variables : [:name]
binding.local_variables : [:name]
name : "Paul"
binding.eval('name') : nil
--- After assignment of name: ---
defined?(name) : "local-variable"
binding.local_variable_defined?(:name) : true
local_variables : [:name]
binding.local_variables : [:name]
name : nil
binding.eval('name') : nil
My name is nil

, binding name - name.

0

Ruby , name - , , , . . , , , , , , .

, - , , Ruby name name , , ' , .

, , , name local_variables:

def say_name_local_variable
  p defined?(name)      # => "method"
  p local_variables     # => [:name] so Ruby aware of the variable already

  if name.nil?          # <- Method call
    name = "Unknown"    # ** From this point on name refers to the variable
  end                   #    even if this block never runs.

  p defined?(name)      # => "local-variable"
  p name                # <- Variable value
  puts "My name is #{name.inspect}"
end

, , , Ruby -w, . , - , .

, , :

  def say_name
    name = self.name || 'Unknown'

    puts "My name is #{name.inspect}"
  end

, Ruby , nil false. , , 0, , . , name literal false, || .

nil? , nil false, , , , , .

+3

Source: https://habr.com/ru/post/1686997/


All Articles