This is one way I've managed to accomplish this.
class Test
class << self
attr_accessor :stuff
def thing msg
#stuff ||= ""
#stuff += msg
end
end
def initialize
#stuff = self.class.stuff
puts #stuff
end
end
# Is there a better way of accomplishing this?
class AThing < Test
thing "hello"
thing "world"
end
AThing.new
# Prints "helloworld"
The interface in AThing is what I would like as a final result. What I really hate (and I feel there must be a better way of accomplishing) is #stuff = self.class.stuff.
Is there a better way to use the eigenclass to set the default dataset for all instances of itself while maintaining a "pretty" interface?
What I want to accomplish with code like this is to have a class method, say add_something that adds something to an array stored in a class variable.
When the class is instantiated, it will use this array in its' initialize method to setup the state of that instance.
class Test
##stuff = ""
class << self
def thing msg
##stuff.concat(msg)
end
end
def initialize
puts ##stuff
end
end
class AThing < Test
thing "hello"
thing "world"
end
AThing.new
# Prints "helloworld"
Related
In Rails we can define a class like:
class Test < ActiveRecord::Base
before_initialize :method
end
and when calling Test.new, method() will be called on the instance. I'm trying to learn more about Ruby and class methods like this, but I'm having trouble trying to implement this in plain Ruby.
Here's what I have so far:
class LameAR
def self.before_initialize(*args, &block)
# somehow store the symbols or block to be called on init
end
def new(*args)
## Call methods/blocks here
super(*args)
end
end
class Tester < LameAR
before_initialize :do_stuff
def do_stuff
puts "DOING STUFF!!"
end
end
I'm trying to figure out where to store the blocks in self.before_initialize. I originally tried an instance variable like #before_init_methods, but that instance variable wouldn't exist in memory at that point, so I couldn't store or retrieve from it. I'm not sure how/where could I store these blocks/procs/symbols during the class definition, to later be called inside of new.
How could I implement this? (Either having before_initialize take a block/proc/list of symbols, I don't mind at this point, just trying to understand the concept)
For a comprehensive description, you can always check the Rails source; it is itself implemented in 'plain Ruby', after all. (But it handles lots of edge cases, so it's not great for getting a quick overview.)
The quick version is:
module MyCallbacks
def self.included(klass)
klass.extend(ClassMethods) # we don't have ActiveSupport::Concern either
end
module ClassMethods
def initialize_callbacks
#callbacks ||= []
end
def before_initialize(&block)
initialize_callbacks << block
end
end
def initialize(*)
self.class.initialize_callbacks.each do |callback|
instance_eval(&callback)
end
super
end
end
class Tester
include MyCallbacks
before_initialize { puts "hello world" }
end
Tester.new
Left to the reader:
arguments
calling methods by name
inheritance
callbacks aborting a call and supplying the return value
"around" callbacks that wrap the original invocation
conditional callbacks (:if / :unless)
subclasses selectively overriding/skipping callbacks
inserting new callbacks elsewhere in the sequence
... but eliding all of those is what [hopefully] makes this implementation more approachable.
One way would be by overriding Class#new:
class LameAR
def self.before_initialize(*symbols_or_callables, &block)
#before_init_methods ||= []
#before_init_methods.concat(symbols_or_callables)
#before_init_methods << block if block
nil
end
def self.new(*args, &block)
obj = allocate
#before_init_methods.each do |symbol_or_callable|
if symbol_or_callable.is_a?(Symbol)
obj.public_send(symbol_or_callable)
else
symbol_or_callable.(obj)
end
end
obj.__send__(:initialize, *args, &block)
end
end
class Tester < LameAR
before_initialize :do_stuff
def do_stuff
puts "DOING STUFF!!"
end
end
I can take a block of code, instance_exec it, and get the proper result. I would like to take a method off a different object and call one of it's methods in my scope. When I take a method from a different object, turn it into a proc, and then instance_exec it, I don't get the expected result. Code follows.
class Test1
def ohai(arg)
"magic is #{#magic} and arg is #{arg}"
end
end
class Test2
def initialize
#magic = "MAGICAL!"
end
def scope_checking
#magic
end
def do_it
ohai = Test1.new.method(:ohai)
self.instance_exec("foobar", &ohai)
end
end
describe "Test2 and scopes" do
before do
#t2 = Test2.new
end
it "has MAGICAL! in #magic" do
#t2.scope_checking.should == "MAGICAL!"
end
# This one fails :(
it "works like I expect converting a method to a proc" do
val = #t2.do_it
val.should == "magic is MAGICAL! and arg is foobar"
end
it "should work like I expect" do
val = #t2.instance_exec do
"#{#magic}"
end
val.should == "MAGICAL!"
end
end
It seems that, in Ruby, methods defined using def some_method are bound permanently to the class they're defined in.
So, when you call .to_proc on them they keep the binding of their original implementation, and you cannot rebind them. Well, you can, but only to an object of the same type as the first one. It's possible I could do some fancyness with inheritance, but I don't think so.
The solution becomes instead of using methods, I just put actual Procs into variables and use them then, as they're not bound until execution time.
not sure how good of an idea this is, but this passes your tests:
class Test1
def ohai(arg, binding)
eval('"magic is #{#magic} "', binding).to_s + "and arg is #{arg}"
end
end
class Test2
def initialize
#magic = "MAGICAL!"
end
def scope_checking
#magic
end
def get_binding
return binding()
end
def do_it
self.instance_exec(get_binding) {|binding| Test1.new.ohai("foobar", binding) }
end
end
I think I'm going a bit crazy when trying to understand instance variables in Ruby. My only aim here is to make sure that every object created for a given class has a variable with a predetermined value without writing an initialize method for that class. Something like:
class Test
#my = []
attr_accessor :my
end
t = Test.new
t.my # I want [] but this shows nil
Is it possible to achieve this without touching initialize ? Thanks.
EDIT: To clarify, I'm writing some piece of code which will be executed similar to attr_accessor in the sense that it'll add an instance variable to the class in which it is executed. If I write my own initialize, I will end up clobbering the one written by the user.
What you are doing is defining an instance variable on the class level (Since classes are instances of the Class class, this works just fine).
And no, there is no way around initialize.
Edit: You have a little misconception in your edit. attr_accessor doesn't add an instance variable to the class. What it does, literally, is this (using your example of my):
def my; #my; end
def my=(value); #my = value; end
It doesn't actively create/initialize any instance variable, it just defines two methods. And you could very well write your own class method that does similar things, by using define_method.
Edit 2:
To further illustrate how one would write such a method:
class Module
def array_attr_accessor(name)
define_method(name) do
if instance_variable_defined?("##{name}")
instance_variable_get("##{name}")
else
instance_variable_set("##{name}", [])
end
end
define_method("#{name}=") do |val|
instance_variable_set("##{name}", val)
end
end
end
class Test
array_attr_accessor :my
end
t = Test.new
t.my # => []
t.my = [1,2,3]
t.my # => [1, 2, 3]
# as instance variable without initialize
class Test1
def my; #my ||= [] end
attr_writer :my
end
t = Test1.new
t.my
# as class instance variable
class Test2
#my = []
class << self; attr_accessor :my end
end
Test2.my
I don't think it is, why are you so hesitant to just write a quick initialize method?
obj = SomeObject.new
def obj.new_method
"do some things"
end
puts obj.new_method
> "do some things"
This works ok. However, I need to do same thing inside an existing method:
def some_random_method
def obj.new_method
"do some things"
end
end
Works ok as well, but having a method inside a method looks pretty horrible. The question is, is there any alternate way of adding such a method?
In ruby 1.9+, there's a better way of doing this using define_singleton_method, as follows:
obj = SomeObject.new
obj.define_singleton_method(:new_method) do
"do some things"
end
Use a Mixin.
module AdditionalMethods
def new_method
"do some things"
end
end
obj = SomeObject.new
obj.extend(AdditionalMethods)
puts obj.new_method
> "do some things"
There are several ways to achieve this, and they are all related to the singleton class:
You can use class << idiom to open the singleton class definition:
obj = Object.new
class << obj
def my_new_method
...
end
end
Or you can use define_singleton_method on the obj:
obj = Object.new
obj.define_singleton_method(:my_new_method) do
...
end
You can also use define_method from the singleton class:
obj = Object.new
obj.singleton_class.define_method(:my_new_method) do
...
end
Or you can use def directly:
obj = Object.new
def obj.my_new_method
...
end
Pay attention to example 3, I think the concept of a singleton class becomes clearer on that one. There is a difference between these two examples:
a = Object.new
b = Object.new
# -- defining a new method in the object's "class" --
a.class.define_method(:abc) do
puts "hello abc"
end
a.abc # prints "hello abc"
b.abc # also prints "hello abc"
# -- defining a new method in the object's "singleton class" --
a.singleton_class.define_method(:bcd) do
puts "hello bcd"
end
a.bcd # prints "hello bcd"
b.bcd # error undefined method
This is because every object has its own singleton class:
a = Object.new
b = Object.new
p a.class # prints "Object"
p a.singleton_class # prints "#<Class:#<Object:0x000055ebc0b84438>>"
p b.class # also prints "Object"
p b.singleton_class # prints "#<Class:#<Object:0x000055ebc0b84410>>" (a different reference address)
Just an interesting point to note:
if you had instead gone:
def my_method
def my_other_method; end
end
Then my_other_method would actually be defined on the CLASS of the object not withstanding that the receiver ofmy_method is an instance.
However if you go (as you did):
def my_method
def self.my_other_method; end
end
Then my_other_method is defined on the eigenclass of the instance.
Not directly relevant to your question but kind of interesting nonetheless ;)
You can use modules.
module ObjSingletonMethods
def new_method
"do some things"
end
end
obj.extend ObjSingletonMethods
puts obj.new_method # => do some things
Now if you need to add more methods to that object, you just need to implement the methods in the module and you are done.
Use instance_eval:
obj = SomeObject.new
obj.instance_eval do
def new_method
puts 'do something new'
end
end
obj.new_method
> "do something new"
class Some
end
obj = Some.new
class << obj
def hello
puts 'hello'
end
end
obj.hello
obj2 = Some.new
obj2.hello # error
Syntax class << obj means that we are opening definition of the class for an object. As you probably know we can define Ruby class methods using syntax like this:
class Math
class << self
def cos(x)
...
end
def sin(x)
...
end
end
end
Then we can use those methods like this:
Math.cos(1)
In Ruby, everything is an object - even classes. self here is an object of Math class itself (you can access that object with Math.class). So syntax class << self means we are opening class for Math class object. Yes, it means that Math class has class too (Math.class.class).
Another way to use a Mixin
obj = SomeObject.new
class << obj
include AnotherModule
end
This includes all of the methods from AnotherModule into the current object.
I would like to write a method in ruby that takes a class with certain methods and modifies its behavior by adding methods or changing how existing methods work. I would like to do this in a way that doesn't modify the base class so basically I want a function that takes a class and returns a new modified class without harming the initial class. I'm pretty sure this is possible but I'm not sure where to start.
You have a couple options:
you can use x = Class.new(Parent) { def meth; puts "hello"; super; puts "bye"; end } to dynamically define a class and override methods (& define new ones)
you can use a Delegator
So for instance, if you wanted to dynamically create classes that logged certain method calls:
class Class
def logging_subclass(*methods)
Class.new(self) do
methods.each do |method|
define_method(method) do |*args,&blk|
puts "calling #{method}"
ret = super(*args,&blk)
puts "#{method} returned #{ret.inspect}"
ret
end
end
end
end
end
class One
def foo
"I'm foo!"
end
end
# this prints nothing
One.new.foo #=> returns :foo
# this prints:
# > calling foo
# > foo returned "I'm foo!"
One.logging_subclass(:foo).new.foo #=> returns :foo
Note that you need ruby 1.9 to support capturing do |&blk| (capturing blocks in block arguments).
I'd suggest using inheritance or a mixin; in my opinion, the use of a mixin would be a wiser idea though using inheritance is easier for a newbie.
Remember, you can always inherit from the class and change behavior or wrap it with new code as desired.
class Mammal
def speak
"..."
end
end
class Cat < Mammal
def speak
"meow"
end
end
class Lion < Cat
def speak
"get ready for a big " + super + "!"
end
end
module Asexual_Critter
def reproduce(critter_list)
puts "*poink!*"
critter_list << self.clone
end
end
class Mutated_Kitty < Cat
include Asexual_Critter # inane example I know, but functional...
end
Just remember that if you want to play with this not to do:
critters = [Mutated_Kitty.new]
begin
critters.each { |c| c.reproduce(critters) }
end while critters.length > 0
Or else you'll be in for a long wait until you run out of RAM, or perhaps segfault.