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:
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, butputs foo: "bar"
,puts(foo: "bar")
, orputs({foo: "bar"})
work fine.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.The
puts
method always returns nil. So when you sayputs {}.class
, that's basically the same asputs.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
How to Change Ruby to Version 1.9.3 (Again) with Rvm
Multipart Response in Ruby/Rack
Rails Cancan and State MAChine - Authorizing States
Webmock Simulate Failing API (No Internet, Timeout ++)
Catching Command-Line Errors Using %X
How to Install Libksba on MAC Osx
Argumenterror: Unknown Key: :Conditions. Valid Keys Are: :Class_Name, :Class, :Foreign_Key
Ruby - How to Thread Across Cores/Processors
Google Analytics API Error "Selected Dimensions and Metrics Cannot Be Queried Together."
Have Delayed_Job Log "Puts", SQL Queries and Jobs Status
Rails 4 Many to Many Association Not Working
Can't Install Ruby via Rvm, Error Running '_Rvm_Make -J4' on Ubuntu 22.04
How to Include Ё in [А-Я] Regexp Char Interval
+= Operator Appears to Modify Frozen String
How to Call Methods Defined in Applicationcontroller in Models
Rails Is Not Using My Global Ruby Version