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>>
Related
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.
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).
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">
I can't figure out the proper block initialize
class Foo
attr_accessor :bar
end
obj = Foo.new do |a|
a.bar = "baz"
end
puts obj.bar
Expect "baz"
instead get nil
What is the proper incantation for block class initializers in ruby?
Another way to make a block initializer would be writing it yourself one:
class Foo
attr_accessor :bar
def initialize
yield self if block_given?
end
end
And later use it:
foo = Foo.new do |f|
f.bar = true
end
My two cents.
Try again:
class Foo
attr_accessor :bar
end
obj = Foo.new.tap do |a|
a.bar = "baz"
end
puts obj.bar
I don't think new can take a block. Never saw it anywhere anyway. Why do you want to initialize in a block ? You can always do obj = foo.new.tap do |a| ... If you really want a block
actually you have a constructor for these purposes:
class Foo
attr_accessor :bar
def initialize(bar = "baz")
#bar = bar
end
end
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']