Initial value for attr_reader in the class level - ruby

I want to initialize my_attr_reader. Changing attr_reader to cattr_reader doesn't help because there is such a method cattr_reader for some reason.
How can I do that?
module Mod1
def method1
puts "method1 from Mod1"
end
end
MyClass = Object.new
class << MyClass
include Mod1
attr_reader :my_attr_reader
my_attr_reader = "111" # doesn't get initialized
def initialize
self.my_attr_reader = "123" # doesn't get initialized
end
def my_class1_method1
puts "MyClass method1"
end
end
MyClass.my_class1_method1
MyClass.method1
p MyClass.my_attr_reader # nil
P.S. Why does include work here, whereas extend doesn't, even so it should be exactly the opposite?

You have a few problems here, so I'll split this up into sections.
A note about cattr_reader
cattr_reader would probably be useful for you, but it is a part of Rails, not Ruby. You will not be able to use this in Ruby code without first including the right parts of Rails.
How to use attr_reader
Your main problem here is that attr_reader and cattr_reader create instance and class variables respectively, but you are using local variables instead. Instance variables start with #, and class variables start with ##. Class variables have odd and confusing behaviors, and cattr_reader isn't built into Ruby as I mentioned above, so I would recommend using attr_reader on the class level.
Why does include work here, whereas extend doesn't, even so it should be exactly the opposite?
extend adds in class-level methods, while include includes instance methods. You are defining method as an instance method of Mod1, so you should be using include.

Related

Why am I getting NoMethodError undefined method for nil::NilClass

['board'].each{|script| require_relative script}
class GameRunner
#board = Board.new
def initialize
end
def getBoard
#board
end
end
This piece of code generates an error when getBoard is called. But when I move the instantiation of #board to the initialize block there is no errors. Why?
Edit: An answer with a more clear explanation on what is going on: Ruby class instance variable vs. class variable
Here #board is an instance variable. Instance variables belong to an object (instance), hence why they are called instance variables.
You have two references to an instance variable named #board in your code. Now, ask yourself: which object do they belong to? In other words: what is self at the point where you reference the instance variable?
class GameRunner
# Here, `self` is `GameRunner`
#board = Board.new
def getBoard
# Here, `self` is an *instance* of `GameRunner`
#board
end
end
At the first reference to #board, self is the GameRunner class itself. Remember, class are objects just like any other object; they are instances of the Class class just like strings are instances of the String class, integers are instances of the Integer class, and game runners are instances of the GameRunner class.
You can easily see that the instance variable has been defined and initialized:
GameRunner.instance_variables
#=> [:#board]
GameRunner.instance_variable_get(:#board)
#=> #<Board:0x0000deadbeef1230>
At your second mention, however, self is an instance of GameRunner, and not the GameRunner class itself.
Or, to put it differently: you have two completely independent instance variables of two completely independent objects. The instance variables just happen to have the same name.
It's exactly the same as if you did:
game_runner1 = GameRunner.new
game_runner2 = GameRunner.new
The instance variables of game_runner1 and game_runner2 are private to each of those two objects. game_runner1 does not know anything about the instance variables of game_runner2 and vice versa. The same thing is true about game_runner1 and GameRunner.
Again, it is important to remember that classes are just objects like any other object.
It looks like what you actually want is to have both references refer to the same instance variable, namely an instance variable of an instance of GameRunner. You can achieve that by moving the assignment into an instance method, something like this:
class GameRunner
def initializeBoard
# Here, `self` is an *instance* of `GameRunner`
#board = Board.new
end
def getBoard
# Here, `self` is an *instance* of `GameRunner`
#board
end
end
However, this is somewhat annoying because you always have to remember to call initializeBoard before you can use the object, and you have to make sure that once you have called initializeBoard, you never call it again.
To make initialization tasks like this easier, Ruby has a convention: the default implementation of Class#new will call a method named initialize on the newly allocated object:
class Class
def new(...)
obj = allocate
obj.initialize(...)
obj
end
end
[This is not quite accurate because initialize is private by default, so it would be more like obj.__send__(:initialize, ...), but you get the idea.]
So, if we simply rename the initializeBoard method to initialize, that will ensure that our instance variable is always initialized by GameRunner::new:
class GameRunner
def initialize
# Here, `self` is an *instance* of `GameRunner`
#board = Board.new
end
def getBoard
# Here, `self` is an *instance* of `GameRunner`
#board
end
end
Note that your code violates multiple Ruby community coding standards:
Ruby uses 2 spaces for indentation, not 4.
There should be no empty line after class or before end
Method names use snake_case, not camelCase. IOW, your getter method should be called get_board.
… Except it shouldn't, because getters should simply be called noun, not get_noun, i.e. your getter method should be called simply board.
Lastly, trivial getters should not be defined by hand, but using the core Module#attr_reader method.
If we combine all of this, your class should look like this:
class GameRunner
attr_reader :board
def initialize
#board = Board.new
end
end
I, personally, prefer to avoid referring to instance variables directly as much as possible, and only use getters and setters. However, that is not a majority coding style, that is just my personal preference:
class GameRunner
attr_reader :board
private
def initialize
self.board = Board.new
end
attr_writer :board
end
Here #board is a class variable, not an instance variable.
What you probably mean is:
class GameRunner
# Anything declared here is assumed to be class-level
def initialize
# Anything inside an instance method is an instance variable
#board = Board.new
end
def getBoard
#board
end
end
Since classes are objects, the class can also have its own instance variables. Confusingly they also use the same # prefix.
It's worth noting that accessors like this are usually declared in Ruby as:
attr_reader :board
Which makes the method for you. The get prefix is almost always omitted because mutator methods (e.g. set) are the same but with the = suffix.

Using super with class_eval

I have an app that includes modules into core Classes for adding client customizations.
I'm finding that class_eval is a good way to override methods in the core Class, but sometimes I would like to avoid re-writing the entire method, and just defer to the original method.
For example, if I have a method called account_balance, it would be nice to do something like this in my module (i.e. the module that gets included into the Class):
module CustomClient
def self.included base
base.class_eval do
def account_balance
send_alert_email if balance < min
super # Then this would just defer the rest of the logic defined in the original class
end
end
end
end
But using class_eval seems to take the super method out of the lookup path.
Does anyone know how to work around this?
Thanks!
I think there are several ways to do what you're wanting to do. One is to open the class and alias the old implementation:
class MyClass
def method1
1
end
end
class MyClass
alias_method :old_method1, :method1
def method1
old_method1 + 1
end
end
MyClass.new.method1
=> 2
This is a form of monkey patching, so probably best to make use of the idiom in moderation. Also, sometimes what is wanted is a separate helper method that holds the common functionality.
EDIT: See Jörg W Mittag's answer for a more comprehensive set of options.
I'm finding that instance_eval is a good way to override methods in the core Class,
You are not overriding. You are overwriting aka monkeypatching.
but sometimes I would like to avoid re-writing the entire method, and just defer to the original method.
You can't defer to the original method. There is no original method. You overwrote it.
But using instance_eval seems to take the super method out of the lookup path.
There is no inheritance in your example. super doesn't even come into play.
See this answer for possible solutions and alternatives: When monkey patching a method, can you call the overridden method from the new implementation?
As you say, alias_method must be used carefully. Given this contrived example :
module CustomClient
...
host.class_eval do
alias :old_account_balance :account_balance
def account_balance ...
old_account_balance
end
...
class CoreClass
def old_account_balance ... defined here or in a superclass or
in another included module
def account_balance
# some new stuff ...
old_account_balance # some old stuff ...
end
include CustomClient
end
you end up with an infinite loop because, after alias, old_account_balance is a copy of account_balance, which now calls itself :
$ ruby -w t4.rb
t4.rb:21: warning: method redefined; discarding old old_account_balance
t4.rb:2: warning: previous definition of old_account_balance was here
[ output of puts removed ]
t4.rb:6: stack level too deep (SystemStackError)
[from the Pickaxe] The problem with this technique [alias_method] is that you’re relying on there not being an existing method called old_xxx. A better alternative is to make use of method objects, which are effectively anonymous.
Having said that, if you own the source code, a simple alias is good enough. But for a more general case, i'll use Jörg's Method Wrapping technique.
class CoreClass
def account_balance
puts 'CoreClass#account_balance, stuff deferred to the original method.'
end
end
module CustomClient
def self.included host
#is_defined_account_balance = host.new.respond_to? :account_balance
puts "is_defined_account_balance=#{#is_defined_account_balance}"
# pass this flag from CustomClient to host :
host.instance_variable_set(:#is_defined_account_balance,
#is_defined_account_balance)
host.class_eval do
old_account_balance = instance_method(:account_balance) if
#is_defined_account_balance
define_method(:account_balance) do |*args|
puts 'CustomClient#account_balance, additional stuff'
# like super :
old_account_balance.bind(self).call(*args) if
self.class.instance_variable_get(:#is_defined_account_balance)
end
end
end
end
class CoreClass
include CustomClient
end
print 'CoreClass.new.account_balance : '
CoreClass.new.account_balance
Output :
$ ruby -w t5.rb
is_defined_account_balance=true
CoreClass.new.account_balance : CustomClient#account_balance, additional stuff
CoreClass#account_balance, stuff deferred to the original method.
Why not a class variable ##is_defined_account_balance ? [from the Pickaxe] The module or class definition containing the include gains access to the constants, class variables, and instance methods of the module it includes.
It would avoid passing it from CustomClient to host and simplify the test :
old_account_balance if ##is_defined_account_balance # = super
But some dislike class variables as much as global variables.
[from the Pickaxe] The method Object#instance_eval lets you set self to be some arbitrary object, evaluates the code in a block with, and then resets self.
module CustomClient
def self.included base
base.instance_eval do
puts "about to def account_balance in #{self}"
def account_balance
super
end
end
end
end
class Client
include CustomClient #=> about to def account_balance in Client
end
As you can see, def account_balance is evaluated in the context of class Client, the host class which includes the module, hence account_balance becomes a singleton method (aka class method) of Client :
print 'Client.singleton_methods : '
p Client.singleton_methods #=> Client.singleton_methods : [:account_balance]
Client.new.account_balance won't work because it's not an instance method.
"I have an app that includes modules into core Classes"
As you don't give much details, I have imagined the following infrastructure :
class SuperClient
def account_balance
puts 'SuperClient#account_balance'
end
end
class Client < SuperClient
include CustomClient
end
Now replace instance_eval by class_eval. [from the Pickaxe] class_eval sets things up as if you were in the body of a class definition, so method definitions will define instance methods.
module CustomClient
...
base.class_eval do
...
print 'Client.new.account_balance : '
Client.new.account_balance
Output :
#=> from include CustomClient :
about to def account_balance in Client #=> as class Client, in the body of Client
Client.singleton_methods : []
Client.new.account_balance : SuperClient#account_balance #=> from super
"But using instance_eval seems to take the super method out of the lookup path."
super has worked. The problem was instance_eval.

Dynamically define named classes in Ruby

I am writing an internal DSL in Ruby. For this, I need to programmatically create named classes and nested classes. What is the best way to do so? I recon that there are two ways to do so:
Use Class.new to create an anonymous class, then use define_method to add methods to it, and finally call const_set to add them as named constants to some namespace.
Use some sort of eval
I've tested the first way and it worked, but being new to Ruby, I am not sure that putting classes as constants is the right way.
Are there other, better ways? If not, which of the above is preferable?
If you want to create a class with a dynamic name, you'll have to do almost exactly what you said. However, you do not need to use define_method. You can just pass a block to Class.new in which you initialize the class. This is semantically identical to the contents of class/end.
Remember with const_set, to be conscientious of the receiver (self) in that scope. If you want the class defined globally you will need to call const_set on the TopLevel module (which varies in name and detail by Ruby).
a_new_class = Class.new(Object) do
attr_accessor :x
def initialize(x)
print #{self.class} initialized with #{x}"
#x = x
end
end
SomeModule.const_set("ClassName", a_new_class)
c = ClassName.new(10)
...
You don't really need to use const_set. The return value of Class.new can be assigned to
a constant and the block of Class.new is class_eval.
class Ancestor; end
SomeClass = Class.new(Ancestor) do
def initialize(var)
print "#{self.class} initialized with #{var}"
end
end
=> SomeClass
SomeClass.new("foo")
# SomeClass initialized with foo=> #<SomeClass:0x668b68>
Should be like this
a_new_class = Class.new(Object) do
attr_accessor :x
def initialize(x)
#x = x
end
end
SomeModule = Module.new
SomeModule.const_set("ClassName", a_new_class)
c = SomeModule::ClassName.new(10)

Is there a better way of doing class_eval() to extract class variables, in Ruby?

I personally don't have anything against this, apart from the fact that's is long, but what really bothers me is the word eval.
I do a lot of stuff in JavaScript and I run from anything resembling eval like it's the devil, I also don't fancy the fact that the parameter is a string (again, probably because it's eval).
I know I could write my own method to fix the method-name-length problem, my 'method name issue' and the parameter-being-a-string thingy, but what I really want to know is: Is there a better, shorter, fancier, yet native, way of doing class_eval to extract class variables?
Side note: I know about the existence of class_variable_get() and class_variables(), but they don't really look appealing to me; horribly long, aren't they?
EDIT: Updated the question to be more specific.
Thanks!
Use class_variable_get, but only if you must
class_variable_get is the better way, other than the fact that it is not "appealing" to you. If you are reaching inside a class and breaking encapsulation, perhaps it is appropriate to have this extra barrier to indicate that you're doing something wrong.
Create accessor methods for the variables you want to access
If these are your classes, and accessing the variables doesn't break encapsulation, then you should create class accessor methods for them to make it easier and prettier:
class Foo
def self.bar
##bar
end
end
p Foo.bar
If this is your class, however, are you sure that you need class variables? If you don't understand the implications (see below), you may actually be wanting instance variables of the class itself:
class Foo
class << self
attr_accessor :bar
end
end
Foo.bar = 42
p Foo.bar
The behavior of class variables
Class variables appear to newcomers like the right way to store information at a class level, mostly because of the name. They are also convenient because you can use the same syntax to read and write them whether you are in a method of the class or an instance method. However, class variables are shared between a class and all its subclasses.
For example, consider the following code:
class Rectangle
def self.instances
##instances ||= []
end
def initialize
(##instances ||= []) << self
end
end
class Square < Rectangle
def initialize
super
end
end
2.times{ Rectangle.new }
p Rectangle.instances
#=> [#<Rectangle:0x25c7808>, #<Rectangle:0x25c77d8>]
Square.new
p Square.instances
#=> [#<Rectangle:0x25c7808>, #<Rectangle:0x25c77d8>, #<Square:0x25c76d0>]
Ack! Rectangles are not squares! Here's a better way to do the same thing:
class Rectangle
def self.instances
#instances ||= []
end
def initialize
self.class.instances << self
end
end
class Square < Rectangle
def initialize
super
end
end
2.times{ Rectangle.new }
p Rectangle.instances
#=> [#<Rectangle:0x25c7808>, #<Rectangle:0x25c77d8>]
2.times{ Square.new }
p Square.instances
#=> [#<Square:0x25c76d0>, #<Square:0x25c76b8>]
By creating an instance variable and accesor methods on the class itself—which happens to be an instance of the Class class, similar to MyClass = Class.new—all instances of the class (and outsiders) have a common, clean location to read/write information that is not shared between other classes.
Note that explicitly tracking every instance created will prevent garbage collection on 'unused' instances. Use code like the above carefully.
Using class_eval in a cleaner manner
Finally, if you're going to use class_eval, note that it also has a block form that doesn't have to parse and lex the string to evaluate it:
Foo.class_eval('##bar') # ugh
Foo.class_eval{ ##bar } # yum

Writing Ruby Libraries - hiding methods from outside the module

I'm writing a Ruby library which has a module with a bunch of classes inside it. Many of these classes need to be usable and modifiable by calling scripts, but I don't want (some of) the initializers to be visible/callable:
module MyLib
class Control
def initialize
# They can use this
end
def do_stuff
Helper.new('things')
end
end
class Helper
# Shouldn't be visible
def initialize(what)
#what = what
end
def shout
#what
end
end
end
c = MyLib::Control.new
h = c.do_stuff
p h.shout
# => "things"
# ^ All of this is desired
# v This is undesirable
p MyLib::Helper.new('!')
# => <MyLib::Helper #what='!'>
If it's a simple thing, then I'd also appreciate the generated RDoc not even include the .new method for the Helper class either. Any ideas?
Thanks for reading!
My original answer was completely wrong, as #Matthew pointed out. But there are other workarounds. For instance, you can assign an anonymous class to a class variable on Control, and still define methods as normal by using class_eval:
module MyLib
class Control
def initialize
end
def do_stuff
##helper.new('things')
end
##helper = Class.new
##helper.class_eval do
def initialize(what)
#what = what
end
def shout
#what
end
end
end
end
The snippet
c = MyLib::Control.new
h = c.do_stuff
p h.shout
still writes "things", but now there's no way to access ##helper except through the class variable. If someone really wants to access it my reopening the Control class or using class_eval, there's nothing to stop them, but that's just something you have to deal with in a dynamic language.
I chose to assign the anonymous class to a class variable so that it would only be created once; but if you don't care about redefining the anonymous class many times, there's no reason it couldn't be an instance variable.
Ruby has access control.

Resources