Ruby Private Attr_Accessor and Unexpected Nil

Ruby private attr_accessor and unexpected nil

A form of foo = bar assigns to a local variable called foo. If you want to call an attr_writer, you need to use an explicit receiver: self.sneaky += 1.

This has nothing to do with private, it's just basic Ruby syntax for local variable assignments.

attr_accessor which transforms nil into string on write

Here's a little module to do it:

module NilToBlankAttrAccessor

def nil_to_blank_attr_accessor(attr)
attr_reader attr
define_method "#{attr}=" do |value|
value = '' if value.nil?
instance_variable_set "@#{attr}", value
end
end

end

Just mix it in:

class Foo
extend NilToBlankAttrAccessor
nil_to_blank_attr_accessor :bar
end

And use it:

foo = Foo.new
foo.bar = nil
p foo.bar # => ""
foo.bar = 'abc'
p foo.bar # => "abc"

How it works

NilToBlankAttrAccessor#nil_to_blank_attr_accessor first defines the attr_reader normally:

    attr_reader attr

Then it defines the writer by defining a method with the same name as the accessor, only with an "=" at the end. So, for attribute :bar, the method is named bar=

    define_method "#{attr}=" do |value|
...
end

Now it needs to set the variable. First it turns nil into an empty string:

      value = '' if value.nil?

Then use instance_variable_set, which does an instance variable assignment where the instance variable isn't known until runtime.

      instance_variable_set "@#{attr}", value

Class Foo needs nil_to_blank_attr_accessor to be a class method, not an instance method, so it uses extend instead of include:

class Foo
extend NilToBlankAttrAccessor
...
end

@name = value and name being nil when attr_accessor is set in parent class

Which version of Ruby is this? On 1.9.2 it works fine.

require 'jekyll'
require 'pp'

module Jekyll

class TestPage < Page
def initialize(site,base,page)
@name = "test"
puts name # => test
pp name # => "test"
end
end

end

a = Jekyll::TestPage.new("","","")

Ruby Returning unexpected nil

That's because you initialize Timer.new without any arguments. Thus, the seconds is nil

You need either:

before(:each) do
@timer = Timer.new
@timer.seconds = 0
end

Or implement an initializer:

class Timer
attr_accessor :seconds, :time_string

def initialize
@seconds = 0
end
...

ExpectationNotMetError, method returns nil

If you intend amount and statement to be instance methods then change your code to

def amount
@amount ||= @principal * (1 + @rate / @times_compounded) ** (@times_compounded * @years)
end

def statement
@statement ||= "After #{@years} years i'll have #{amount} dollars"
end

this is because

def self.my_method
# method body
end

creates a class method. Also, if you want amount and statement to be read-only change attr_accessor to attr_reader.

Unexpected behaviour in Ruby for puts {}.class

There are a couple things to understand:

  1. whenever a hash is the first argument to a method being called, you need to use parenthesis or remove the braces, otherwise ruby thinks it's a block. So puts { foo: "bar" } raises a syntax error, but puts foo: "bar", puts(foo: "bar"), or puts({foo: "bar"}) work fine.

  2. every method can be called with a block, however only some methods actually call the block. You can test it for yourself - puts(1) { raise } just outputs the number, and doesn't raise an error. puts { 1 } prints nothing, because the block isn't called.

  3. The puts method always returns nil. So when you say puts {}.class, that's basically the same as puts.class, which is NilClass

Ruby Rspec Keep getting NoMethodError for Nil class

@move is nil. You're stubbing out a move method (that doesn't seem to exist) to return 4, but this has nothing to do with the @move variable.

Either make sure @move is set, or modify your code so that it actually has a move method and that all direct reads to @move instead use this method, so your stub can work.

Difference between @instance_variable and attr_accessor

An instance variable is not visible outside the object it is in; but when you create an attr_accessor, it creates an instance variable and also makes it visible (and editable) outside the object.

Example with instance variable (not attr_accessor)

class MyClass
def initialize
@greeting = "hello"
end
end

m = MyClass.new
m.greeting #results in the following error:
#NoMethodError: undefined method `greeting' for #<MyClass:0x007f9e5109c058 @greeting="hello">

Example using attr_accessor:

class MyClass
attr_accessor :greeting

def initialize
@greeting = "hello"
end
end

m2 = MyClass.new
m2.greeting = "bonjour" # <-- set the @greeting variable from outside the object
m2.greeting #=> "bonjour" <-- didn't blow up as attr_accessor makes the variable accessible from outside the object

Hope that makes it clear.

Why does () returns nil in Ruby?

"No value" is treated as nil in very many places in Ruby:

-> { break }.()
#⇒ nil

42 if false
#⇒ nil

The same is here: parentheses are redundant but they maintain the code block, the empty one, hence it’s treated as nil.


With Ruby 2.6+ you might check the AST yourself:

main > RubyVM::AbstractSyntaxTree.parse('()')
#⇒ (SCOPE@1:0-1:2 tbl: [] args: nil body: (BEGIN@1:1-1:1 nil))


Related Topics



Leave a reply



Submit