Ruby object added to meta class is not visible
I have been trying to get the hang of metaprogramming in ruby by reading other people's code on the same. I have this piece of code that is probably too cryptic for me to debug. Here is the code.
#!/usr/bin/env ruby
class Person
def print_name
p "#{Person.identity.name}"
end
class << self
attr_accessor :identity
def identify()
self.identity ||= Identity.new
yield(identity)
end
end
class Identity
attr_accessor :name
def initialize
@name = "kibet"
end
end
end
me = Person.new
me.print_name
An开发者_高级运维d the error I'm getting is this
`print_name': undefined method `name' for nil:NilClass (NoMethodError)
from ./meta_config.rb:28
Help's highly appreciated.
I vaguely understood what you were trying to do there.. Check this out
class Person
def print_name
p "#{Person.identity.name}"
end
class << self
attr_accessor :identity
def identity
@identity || Identity.new
end
end
class Identity
attr_accessor :name
def initialize
@name = "kibet"
end
end
end
me = Person.new
me.print_name
Things to note:
- I guess the method name was a typo. U meant identity intead of identify and please get rid of the braces. This is ruby :)
- calling self.identity inside will cause a stackoverflow. hence, directly access the value of instance variable
- I still could not understand why you need a yield there, when you would never pass a block.
This looks like a strategy that is used for configuring attributes on a class. Something like the following:
class Foo
class << self
attr_accessor :config
def configure()
self.config ||= Configuration.new
yield(config)
end
end
class Configuration
attr_accessor :hostname
def initialize
@hostname = 'www.example.com'
end
end
end
This code would allow you to set up an initializer that could look like:
Foo.config do |config|
config.hostname = "www.sometestsite.com"
end
You can then use the instance of config in your class to make a method:
class Foo
...
def self.foo
puts "this method is crawling: #{Foo.config.hostname}"
end
...
end
It's similar to #{Rails.root}/config/environments/development.rb:
ApplicationName::Application.configure do
...
...
end
I found two problems:
def print_name
p "#{Person.identify.name}"
end
There was a typo. You wrote identity, not identify.
def identify()
#yield(identity) if block_given?
self.identity ||= Identity.new
end
Why do you need the yield? You have no block, when you call it. So I commented it out. When you need the block, then I would check, if there is a block.
And your problem: You didn't return a value, as your method ended with the yield. I changed the sequence.
"#{Person.foo.name}"
is redundant. It's the equivalent of Person.foo.name.to_s
, and since name is "kibet"
, and String#to_s returns itself (I hope!), then it's the equivalent of Person.foo.name
.
精彩评论