Create Object from Subclass-Object - ruby

say I've got 2 Classes:
Class Foo
attr_accessor :bar
end
Class Baz < Foo
end
I'm creating an Instance of Foo and then want to have an Instance of Baz with the Data of the Foo Instance in it:
f = Foo.new(:bar => "Hi World")
# Doesnt work?
b = Baz.new(f)
How to do it?

an instance of Baz with the data of the Foo instance in it
Since your constructor already accepts attributes as a hash, you could create a method to return Foo's attributes as a hash:
class Foo
attr_accessor :bar
def initialize(attributes={})
#bar = attributes[:bar]
end
def attributes
{:bar => bar}
end
end
class Baz < Foo
end
Now you can create a Baz instance from these attributes:
f = Foo.new(:bar => "Hi World") #=> #<Foo:0x007fd09a8614c0 #bar="Hi World">
f.attributes #=> {:bar=>"Hi World"}
b = Baz.new(f.attributes) #=> #<Baz:0x007fd09a861268 #bar="Hi World">

Related

Ruby: get a reference in a method context to the object that self is an attribute of

Suppose I have a setup like this:
class Foo
attr_accessor :bar
def initialize
#bar = Bar.new
end
end
class Bar
def bar_method
self.class # => Bar
whatever???.class # => Foo
end
end
foo = Foo.new
foo.bar.bar_method
I know that I can set up the method like this:
def bar_method(selfs_self)
selfs_self.class # => Foo
end
And call the method like this: foo.bar.bar_method(foo) to get what I want. But that seems pretty redundant. Is there any way, inside of bar_method, that I can get a reference to foo, without specifically passing in a reference to it?
No.
Usually this is done by passing a reference to the parent object when initializing child objects, like:
class Foo
attr_accessor :bar
def initialize
#bar = Bar.new(self)
end
end
class Bar
attr_reader :foo
def initialize(foo)
#foo = foo
end
def bar_method
self.class # => Bar
foo.class # => Foo
end
end

Class accessor return nil

class Foo
class << self
attr_accessor :var
end
end
class Bar < Foo
var = "bar"
p var # print "bar"
end
p Bar.var # print nil
Why Bar.var not return "bar"?
how can i add getter/setter for class variables?
class Bar < Foo
var = "bar" # this is assignment to local variable, not the accessor
end
Use self to tell ruby that you want to call the method, not create local variable.
class Foo
class << self
attr_accessor :var
end
end
class Bar < Foo
self.var = "bar"
var # => "bar"
end
Bar.var # => "bar"

assign class variables for inherited classes

When inheriting a class, I want to save class variables to the inheriting class instead of the parent class... like so...
class Foo
def self.inherited klass
klass.title = klass.to_s
end
def self.title= title
##title = title
end
def self.title
##title
end
end
class Bar < Foo
end
class Baz < Foo
end
Bar.title # returns `Baz` instead of `Bar`
Baz.title
This is a contrived example because after playing around with it, I more want to just understand than anything.
You need to change the class variable to instance variable.
class Foo
def self.inherited klass
klass.title = klass.to_s
end
def self.title= title
#title = title
end
def self.title
#title
end
end
class Bar < Foo
end
class Baz < Foo
end
Bar.title # => "Bar"
Baz.title # => "Baz"
##title class variable is a shared variable. Baz, Bar and Foo all are using the same class variable ##title. When you did class Baz < Foo ;end, then ##title has been updated to the name of Baz, thus all call to the getter method #title is giving currently updated value of the variable #title.
Follow the code below :-
class Foo
def self.inherited klass
klass.title = klass.to_s
end
def self.title= title
##title = title
end
def self.title
##title
end
end
class Bar < Foo
end
# see the output here
Bar.title # => "Bar"
class Baz < Foo
end
# here the output changed, as ##title value is holding the currently updated
#value
Bar.title # => "Baz"
Baz.title # => "Baz"
You can do that using Module#class_variable_get and Module#class_variable_set:
class Foo
def self.title= title
class_variable_set(:##title, title)
end
def self.title
class_variable_get(:##title)
end
end
class Bar < Foo
end
class Baz < Foo
end
Bar.title = 'Bar'
Baz.title = 'Baz'
Bar.title #=> "Bar"
Bar.title = "Cat"
Baz.title #=> "Baz"
Bar.class_variables #=> [:##title]
Baz.class_variables #=> [:##title]
Foo.class_variables #=> []
Foo.methods(false) #=> [:title=, :title]
Bar.methods(false) #=> []
Baz.methods(false) #=> []
If we were to execute Bar's a getter before its setter, we'd get:
Bar.title #=> NameError: uninitialized class variable ##title in Bar
If desired, the class variables can be initialized (to nil, say) by adding the following to the class definition:
def self.inherited klass
klass.class_variable_set(:##title, nil)
end

Setting a class instance variable of an extended class

How can Foo return the value of #baz. At the moment #baz returns nil.
module Bar
#baz = 'baz'
def get_baz
#baz
end
end
class Foo
extend Bar
end
Foo.get_baz # Currently returns nil
If this is not possible, is there a better way to implement this logic?
Try this:
module Bar
attr_accessor :baz
def get_baz
#baz
end
end
class Foo
extend Bar
end
Foo.baz = 'baz'
Foo.get_baz # => 'baz'
Foo.baz # => 'baz'
If we look at Foo's class methods, sure enough:
Foo.singleton_methods # => [:get_baz, :baz, :baz=]
Let's wander a bit (with module Bar unchanged):
class Foo
extend Bar
include Bar
end
foo = Foo.new
foo.methods.sort # => [:!,..., :baz, :baz=,..., :get_baz,...]
Foo.baz = 'baz'
foo.baz = 'cat'
Foo.baz # => "baz"
Foo.baz = 'dog'
foo.baz # => "cat"
goo = Foo.new
goo.baz = 'squid'
goo.baz # => 'squid'
We could instead use extend to bring in Bar's methods for just a particular instance of Foo:
class Foo
extend Bar
end
foo = Foo.new
foo.extend Bar
goo = Foo.new
We get the same results as above with Foo and foo, but
goo.baz = 'rhino' # => # NoMethodError: undefined method 'baz='
Can't keep extend and include straight? Maybe this will help: here extend Bar within the class definition is equivalent to Foo.singleton_class.send(:include, Bar).

ruby how to define writer method with "<<"

I have a skeleton class:
class Foo
def bar
# returns some sort of array
end
end
but how can one add the 'writer' method to 'bar' so to enable the Array#push behavior?
Foo.new.bar<<['Smile']
_.bar #=> ['Smile']
EDITED:
I should expand my question further.
There are two classes. Foo, and Bar, much like the ActiveRecord has_many relation where Foo has_many Bars
But I am actually storing the ids of Bar inside a method of Foo. I name that method bar_ids
so #foo = Foo.new(:bar_ids => [1,2,3])
As you can imagine, if I ever want to look up what Bars belong to #foo, I have to actually do something like Bar.where(:id => #foo.bar_ids)
So I decided to make another method just named bar to do just that
class Foo
#...
def bar
Bar.where(:id => bar_ids)
end
end
That worked out. now I can do #foo.bar #=> all the bars belonging to #foo
Now I also want to have that kind of push method like ActiveRecord associations, just to cut out the "id" typing when associating another bar object to a foo object
Currently, this works:
#foo.bar_ids << Bar.new.id
#foo.save
But I want:
#foo.bar << Bar.new #where the new bar's id will get pushed in the bar_ids method of #foo
#foo.save
Thanks for all of your help, I really appreciate your thoughts on this!
class Foo
attr_reader :bar
def initialize
#bar = Array.new
def #bar.<< arg
self.push arg.id
end
end
end
class Bar
attr_accessor :id
def initialize id
self.id = id
end
end
f = Foo.new
bars = (1..5).map{|i| Bar.new i}
f.bar << bars[2]
f.bar << bars[4]
p f.bar #=> [3, 5]
Return an object that has the << method defined.
Unless I'm misunderstanding what you're wanting, why not just make the bar method a getter for an internal array member?
class Foo
attr_reader :bar
def initialize
#bar = []
end
end
f = Foo.new
f.bar << 'abc'
# f.bar => ['abc']

Resources