how can i write this (child) class like so:
class child < parent
create_columns :name, :address
end
so that:
class parent
# Can access the create_columns set by the child class?
end
Thanks.
You can solve this by using the inherited hook method in Ruby, so you can track all the children.
class Parent
self.inherited(base)
self.children << base
end
end
class Child < Parent
def initialize
##instances << self
end
def self.instances
##instances
end
Now you can do things like Parent.children.each { |child| child.instances.collect(:&name) }. If name is accessable :-)
Hope that helps!
Related
I have this parent class:
class ListBase
##list = []
def self.list
##list
end
end
I'm trying to create two child classes like so:
class Child1 < ListBase
end
class Child2 < ListBase
end
I was under the impression that each of these child classes will have their own ##list class variable. However, I get this:
Child1.list.push(1)
Child2.list.push(2)
Child1.list # => [1, 2]
Child2.list # => [1, 2]
which means the child classes share the ##list from the parent class.
How can I create a separate class variable for each of the child classes without repeating?
As #CarySwoveland comments, in your use case, you should use a class instance variable:
class ListBase
#list = []
def self.list
#list
end
end
Not even sure why you thought of using a class variable for your use case.
You are using class variable through class method (getter/reader).
So you can override that method in derived classes and use own class variable for every derived class.
Not fun, but that how class variables works, they are shared within a class and derived classes as well.
class ListBase
def self.list
##base_list ||= []
end
end
class ListOne
def self.list
##one_list ||= []
end
end
class ListTwo
def self.list
##two_list ||= []
end
end
ListOne.list << 1
ListTwo.list << 2
puts ListOne.list
# => [1]
puts ListTwo.list
# => [2]
For example, I'd like to do something like this
class Child
#name = "Bastion, please, call my name!"
attr_reader :name
end
class Parent
#name = "I am a parent"
def rename_child(child,name)
child.name = name
end
end
bastion = Parent.new()
princess = Child.new()
bastion.rename_child(princess,"Moon Child")
I only want instances of the Parent class to be able to change the #name of a Child class.
EDIT
I only want instances of the Parent class to be able to change the #name of a Child instance.
I'm assuming you meant for #name to be an instance variable, rather than a class instance one...if my hunch is right, you can do this using #send:
class Child
def initialize
#name = "Bastion, please, call my name!"
end
attr_reader :name
private
attr_writer :name
end
class Parent
def initialize
#name = "I am a parent"
end
def rename_child(child, name)
child.send(:name=, name)
end
end
bastion, princess = Parent.new, Child.new
p princess.name
#=> "Bastion, please, call my name!"
bastion.rename_child(princess, "Moon Child")
p princess.name
#=> "Moon Child"
No you cannot do that. The problem is not the method being private. You cannot access a class instance variable by an accessor defined on an instance.
This is unfortunately not possible. The closest you can get is:
class Child
#name = "Bastion, please, call my name!"
attr_reader :name
end
class Parent
#name = "I am a parent"
def rename_child(child,name)
child.instance_variable_set(:#name, name)
end
end
Note that instance_variable_set is a public method, which together with eval makes private/public differences in ruby not to be so strict as in other languages. Those methods should be use extremely cautiously and they are usually considered to be slightly 'hacky'.
attr_accessor does not work on the following code. The error says "undefined method 'things' for Parent:Class (NoMethodError)":
class Parent
##things = []
attr_accessor :things
end
Parent.things << :car
p Parent.things
However the following code works
class Parent
##things = []
def self.things
##things
end
def things
##things
end
end
Parent.things << :car
p Parent.things
attr_accessor defines accessor methods for an instance. If you want class level auto-generated accessors you could use it on the metaclass
class Parent
#things = []
class << self
attr_accessor :things
end
end
Parent.things #=> []
Parent.things << :car
Parent.things #=> [:car]
but note that this creates a class level instance variable not a class variable. This is likely what you want anyway, as class variables behave differently than you might expect when dealing w/ inheritance. See "Class and Instance Variables In Ruby".
attr_accessor generates accessors for instance variables. Class variables in Ruby are a very different thing, and they are usually not what you want. What you probably want here is a class instance variable. You can use attr_accessor with class instance variables like so:
class Something
class << self
attr_accessor :things
end
end
Then you can write Something.things = 12 and it will work.
Just some clarification: class variables won't be accessible using attr_accessor. It's all about instance variables:
class SomeClass
class << self
attr_accessor :things
end
#things = []
end
because in Ruby, class is an instance of the class "Class" (God, I love to say that) and attr_accessor sets accessor methods for instance variables.
This is probably the simplest way.
class Parent
def self.things
##things ||= []
end
end
Parent.things << :car
p Parent.things
Аlso note that a singleton method is a method only for a single object. In Ruby, a Class is also an object, so it too can have singleton methods! So be aware of when you might be calling them.
Example:
class SomeClass
class << self
def test
end
end
end
test_obj = SomeClass.new
def test_obj.test_2
end
class << test_obj
def test_3
end
end
puts "Singleton methods of SomeClass"
puts SomeClass.singleton_methods
puts '------------------------------------------'
puts "Singleton methods of test_obj"
puts test_obj.singleton_methods
Singleton methods of SomeClass
test
Singleton methods of test_obj
test_2
test_3
Parent.class_variable_get(:##things)
That would be the built-in way. In most cases this should be sufficient I think. No need to have a class variable accessor in the instance.
class Parent
#things = []
singleton_class.send(:attr_accessor, :things)
end
This pattern is most useful when you are defining accessors dynamically or creating them inside a method:
class Foo
def self.add_accessor(name)
singleton_class.send(:attr_accessor, name)
end
end
Foo.add_accessor :things
Foo.things = [:car]
Foo.things # => [:car]
Given the following classes
class Parent
def hello
puts "I am the parent class"
end
def call_parent_hello
hello
end
end
class Child < Parent
def hello
puts "I am the child class"
end
end
When I do the following:
c = Child.new
c.hello # => Outputs: "I am the child class"
c.call_parent_hello # => Outputs: "I am the child class"
Is it possible to make Child#call_parent_hello access the Parent#hello, but without altering the Parent class?
I am looking for some kind of called_by_parent_class? implementation like this:
def hello
if called_by_parent_class?
super
else
puts "I am the child class"
end
end
You can use the super keyword:
class Child < Parent
def hello
super
end
end
I think you're looking to do something like this:
class Parent
def hello( opts = '' )
"Who's talking? The #{self.class} class is via the Parent class!"
end
end
class Child < Parent
def hello( opts = '' )
if opts == 'super'
super
else
"I have a parent and an independent voice"
end
end
def call_mom
hello( 'super' )
end
end
c1 = Child.new
puts c1.hello => "I have a parent and an independent voice"
puts c1.call_mom => "Who's talking? The Child class is via the Parent class!"
However (and I'm not trolling here) I also think you're kind of missing the point of subclassing. Generally you would subclass to get this automatic scoping of methods. If you break out of that I think you would want to instantiate an instance of Parent. But to each his own.
Good luck!
After rereading your question I see your real question is:
Is it possible to make Child#call_parent_hello access the Parent#hello, but without altering the Parent class?
Changing your child class to be:
class Child < Parent
alias_method :call_parent_hello, :hello
def hello
puts "I am the child class"
end
end
solves the problem just as you ask
Use super. super calls the same method in the parent class
class Child < Parent
def call_parent_hello
super
end
end
Use direct hierarchy call. Class#ancestors gives you the hierarchy of the inheritance.
class Child < Parent
def call_parent_hello
self.class.ancestors[1].new.hello
end
end
I cannot manage to find a nice way to implement the rails behavior of objects navigation rails has in a pure ruby script.
Say I have an object of class Parent, with an accessor to an Array of objects of class Child, I'd like, when I manipulate a Child object, to be able to get the Parent easily, like that :
class Parent
attr_accessor :children
def initialize
#children = Array.new
end
end
class Child
end
parent = Parent.new
first_child = Child.new
second_child = Child.new
parent.children << [ first_child, second_child ]
a_child = parent.children.first
get_parent_from_child = a.child.parent
The part I'm interested in is, of course, the last line in which I attempt to get the "parent" object from one of its children.
How could I implement it easily and cleanly ?
I was thinking of adding an accessor to the child object, but I'm not sure how to make sure this value is set everytime I attach a child object to a parent object.
Is there an easy and clean way of doing that in ruby ?
Thanks in advance.
You don't have to fully expose your children you know, there's nothing wrong with fully controlling access to your data.
class Parent
def initialize
#children = [ ]
end
# You get a shallow copy of the children, you can
# change the individual children but not the tree
# structure.
def children
#children.dup
end
# We take ownership of the children when you add them.
def add_children(*kids)
kids.each { |k| k.parent = self }
#children += kids
end
def remove_children(&block)
#children.delete_if(&block)
end
end
# This would probably have some sort of payload and
# a sensible "==" implementation.
class Child
attr_reader :parent
def parent=(p)
raise StandardError => 'Not allowed to change parents!' if #parent
#parent = p
end
end
Then child.parent works fine and if you want to remove children you'd tell the parent which ones to remove:
# Remove the children with even payloads.
parent.remove_children { |c| c.payload % 2 == 0 }
# Remove the children with odd payloads and copy them.
odds = [ ]
parent.remove_children do |c|
kill_it = c.payload % 2 != 0
odds << Child.new(c.payload) if(kill_it)
kill_it
end
You can achieve the has_many behavior from Rails by using a custom container class, like Rails does.
class Parent
attr_reader :children
def initialize
#children = ChildCollection.new(self)
end
end
class Child
attr_accessor :parent
end
class ChildCollection
def initialize parent
#children = []
#parent = parent
end
def new
child = Child.new
child.parent = #parent
#children << child
child
end
def << child
child.parent = #parent
#children << child
self
end
end
And some example code to add children to the parent:
parent = Parent.new
child1 = parent.children.new
child2 = Child.new
child3 = Child.new
parent.children << child2 << child3
puts "Parent is: #{parent.object_id}"
puts "Child1's parent is: #{child1.parent.object_id}"
puts "Child2's parent is: #{child2.parent.object_id}"
puts "Child3's parent is: #{child3.parent.object_id}"
You may want to throw on some other helpful methods like remove and each (so you can have ChildCollection include the Enumerable module and get all the cool stuff that comes with that) but this should be enough to get you started.
Write an own accessor for children in the class Parent in which you set a parent attribute for each child added to the array. Do the reverse when removing children from the array. This is how I would do it.