Best practice to connect objects between them in ruby - ruby

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.

Related

Remove a node from its old parent's children with Ruby

BACKGROUND
I am writing a class in Ruby named PolyTreeNode which represents a node in a tree.
STEPS TAKEN
So far, I've been able to make it instantiate a new node where the parent = nil and there are no children. I have also been able to set a new parent and add the self to the new parent's children (unless the self is already included in the array).
PROBLEM
I would like to remove the self from the old parent's children when (re)assigning a new parent.
\
class PolyTreeNode
attr_reader :parent, :children, :value
def initialize(value)
#value = value
#parent = nil
#children = []
end
def parent=(parent)
if parent == nil
#parent = nil
else
#parent = parent
parent.children << self unless parent.children.include?(self)
end
end
\\
The way I wrote it was as follows but I am getting an RSPEC error.
def parent=(parent)
if parent == nil
#parent = nil
else
#parent = parent
parent.children << self unless parent.children.include?(self)
#parent.children.delete(self)
end
end
ยดยดยด
Any ideas? Thanks!

How can I use ruby flat_map to map to tuples of children AND parent

Imagine you have an array of objects called parents and each entry is an instance of a Parent. And imagine each parent has a method called children which returns an array of Child instances. Now imagine you want to flat_map from the parents to the children but in a later step you will still need access to the parent (for, say, filtering or whatever). How would you do it?
parents
.flat_map { |parent| parent.children.map { |child| {parent: parent, child: child} } }
Would give me what I need, but... eww. Surely there's a more ruby-esque way of doing this? It almost seems like an RX merge or combineLatest. Can't figure out a ruby way of doing this other than what I already have.
Here's a bit of ruby to generate the parent/child structure I'm talking about using random data:
class Child ; end
class Parent
attr_reader :children
def initialize(children)
#children = children || []
end
end
parents = 100.times.map do
Parent.new(rand(10).times.map { Child.new })
end
Consider adopting the following structure.
class Child
attr_accessor :name # create accessor for name of child
def initialize(name)
#name = name
end
end
class Parent
#families = {} # create class instance variable
class << self # change self to singleton class
attr_reader :families # create accessor for #families
end
def initialize(name, children)
self.class.families[name] = children.map { |name| Child.new(name) }
end
end
Parent.new("Bob and Diane", %w| Hector Lois Rudolph |)
#=> #<Parent:0x00005ac0ddad9aa0>
Parent.new("Hank and Trixie", %w| Phoebe |)
#=> #<Parent:0x00005ac0ddb252c0>
Parent.new("Thelma and Louise", %w| Zaphod Sue |)
#=> #<Parent:0x00005ac0ddcb2890>
Parent.families
#=> {"Bob and Diane" =>[#<Child:0x00005ac0ddadf9f0 #name="Hector">,
# #<Child:0x00005ac0ddadf388 #name="Lois">,
# #<Child:0x00005ac0ddadf1a8 #name="Rudolph">],
# "Hank and Trixie" =>[#<Child:0x00005ac0ddb251d0 #name="Phoebe">],
# "Thelma and Louise"=>[#<Child:0x00005ac0ddcb27c8 #name="Zaphod">,
# #<Child:0x00005ac0ddcb27a0 #name="Sue">]}
Parent.families.keys
#=> ["Bob and Diane", "Hank and Trixie", "Thelma and Louise"]
Parent.families["Bob and Diane"].map { |child| child.name }
#=> ["Hector", "Lois", "Rudolph"]

Confused on hierarchy in Ruby

If we can do this:
class Child
def child_method
puts "hi world"
end
end
child = Child.new
child.child_method
Which calls the method puts which is defined in Kernel and mixed in Object.
Why can't we do this:
class Parent
def test
puts "hi from parent"
end
end
class Child
def child_method
test
end
end
child = Child.new
child.child_method
Why can't we call the method on a Parent object, as we do with "puts"?
When you wirte as below :
class Child
def child_method
puts "hi world"
end
end
By defaults Object becomes super class of Child. Object mixed in the Kernel. So Kernel also in the ancestor chain of Child. Thus you are able to call the puts private method of Kernel.
child = Child.new
child.class.ancestors # =>[Child, Object, Kernel, BasicObject]
Now coming to your below class definitions:
class Parent
def test
puts "hi from parent"
end
end
p Parent.ancestors # => [Parent, Object, Kernel, BasicObject]
class Child
def child_method
test
end
end
p Parent.ancestors # => [Parent, Object, Kernel, BasicObject]
As you didn't inherit Parent to Child class, again Object becomes the default super class of Child. So the test method will not be available to the Child class.If you want it do as below :
class Parent
def test
puts "hi from parent"
end
end
class Child < Parent
def child_method
test
end
end
child = Child.new
child.child_method # => "hi from parent"
Now it is working as the Parent is now in the ancestor chain of Class.
child.class.ancestors # => [Child, Parent, Object, Kernel, BasicObject]
You forgot to inherit. class Child < Parent and it works. Just because you call the classes Child and Parent, they don't become so. :P

Ruby Metaprogramming: Check if called by parent class?

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

setup settings send to its parent class in ruby

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!

Resources