Setting a class instance variable of an extended class - ruby

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).

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

Get Ruby pure class name without parent name, any direct method for it?

I often need to get the pure class name of an object, as code below:
class Foo
class Bar
end
end
obj = Foo::Bar.new
puts obj.class.name # It shows "Foo::Bar", while what I want is just "Bar"
I know it can be done by obj.class.name.split('::').last, but, shouldn't be there a method just return "Bar" ?
In vanilla Ruby, I'm pretty sure class.name.split('::').last is the best way to go. But if you happen to be using ActiveSupport (if you're on Rails, this library will be loaded) there is an inflector called demodulize.
class Foo
class Bar
end
end
obj = Foo::Bar.new
puts obj.class.name.demodulize # => Bar
Classes don't really have "names" in Ruby. A class is an object like any other object, it gets assigned to variables like any other object. A class doesn't have a name, just like a number or a string doesn't have a name.
Take this example:
foo = Class.new
Bar = foo
Baz = foo
Bar = nil
remove_const :Bar
What should the "name" of the class be in this example?
Well, there is a method called name in the Module class. What it does is the following: if the class object has been assigned to a constant for the first time, the name of that constant (note that even that is a fuzzy concept!) becomes the name of the class, otherwise the name will just be nil.
Expanding on the example above:
foo = Class.new
foo.name
# => nil
Bar = foo
foo.name
# => 'Bar'
Bar.name
# => 'Bar'
Baz = foo
Baz.name
# => 'Bar'
Bar = nil
remove_const :Bar
foo.name
# => 'Bar'
Here's another example:
foo = Class.new
Bar = foo
Baz = foo
class Baz
class Quux; end
Xyzzy = Quux
end
foo::Xyzzy.name
# => 'Bar::Quux'
Note that even though Quux is defined inside Baz and accessed via foo::Xyzzy it still prints Bar::Quux as its name.
Also, two different classes can have the same name:
Foo = Class.new
Bar = Foo
Foo = nil
remove_const :Foo
Foo = Class.new
Baz = Foo
Foo = nil
remove_const :Foo
Bar.name
# => Foo
Baz.name
# => Foo
Bar == Baz
# => false
The "name" of a class is simply a debugging help for human readers, you should never use it for anything else, and never use it programmatically (or even depend on a specific structure of the string).
based on thomax answer i've researched it a bit...
require 'benchmark'
class String
def demodulize
self[(rindex('::') || -2) + 2..-1]
end
def split_last
split('::').last
end
def demodulize_vs_split_last n = 5000000
Benchmark.bm(10) do |x|
x.report('split_last') { n.times { split_last } }
x.report('demodulize') { n.times { demodulize } }
end
end
end
and got this:
> 'Core::String'.demodulize_vs_split_last
user system total real
split_last 1.960688 0.008501 1.969189 ( 1.983187)
demodulize 1.807694 0.005815 1.813509 ( 1.826771)
for short strings it mostly similar
> 'Core::Exten::sions::String::Inf::lect::ions'.demodulize_vs_split_last
user system total real
split_last 4.386797 0.024131 4.410928 ( 4.447739)
demodulize 1.875757 0.005089 1.880846 ( 1.895737)
but split('::').last becomes much slower if your class is nested deeper
No, there isn't a method to return just the name of the class. Technically, the name is only accurate if it includes any outer modules and classes.
The #name method is defined on the Module class; see the documentation for more information.

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

Create Object from Subclass-Object

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">

Can I find out if an object is being referenced?

If I have two objects, one being referenced in another. Then in the first object can I write a method which will give me which other objects it is being referenced in?
I am not sure how to do it out of the box, but maybe the following post might help you:
What is a ruby object? (introducing Memprof.dump)
Perhaps digging around in ObjectSpace could help:
#!/usr/bin/ruby1.8
include ObjectSpace
def print_references_to_foos
for klass in [Bar, Baz]
each_object(klass) do |o|
s = o.inspect
puts s if s =~ /#<Foo/
end
end
end
class Foo
end
class Bar
def initialize(foo)
#foo = foo
end
end
class Baz < Bar
end
foo1 = Foo.new
foo2 = Foo.new
foo3 = Foo.new
bar1 = Bar.new(foo1)
bar2 = Bar.new(foo1)
bar3 = Baz.new(foo2)
print_references_to_foos
# => #<Baz:0xb7e09158 #foo=#<Foo:0xb7e091a8>>
# => #<Bar:0xb7e0916c #foo=#<Foo:0xb7e091d0>>
# => #<Bar:0xb7e09180 #foo=#<Foo:0xb7e091d0>>
# => #<Baz:0xb7e09158 #foo=#<Foo:0xb7e091a8>>

Resources