0

Always working with Rails but now that I am coding completely without framework I am missing a lot of methods that I am not able to write myself. So I need a little hint here.

So I've got this code

class Foo
   initialize(k)
    (1..k).each do |b|
       Boo.new(b)
    end
end

class Boo
  initialize(a)
    @a = a
  end
end

Foo.new(10)
#and now I want to access one of the Boo's with a certain a (in Rails Boo.find_by_a(2) e.g.)

How do I write a method that uses a certain value "a" to get the specific Boo back with @a = a?

Update:

Okay I added this methot to Boo

  def self.all
    ObjectSpace.each_object(self).to_a
  end

And now I get the right object by:

boo = Boo.all.select{ |b| b.a == 2 }.first

But if I want this to be a method, where would i put it?

5
  • 1
    You are not capturing these anywhere. Right now you are creating 10 Boos but they are not stored and will be quickly picked up by the GC. General rule Learn Ruby first then learn rails. this will be extremely beneficial to you and there are a ton of great resources. Commented Nov 11, 2015 at 14:31
  • I already wrote an all method that gives me back all the object in one array. Just not sure how to pick the right one now. Commented Nov 11, 2015 at 14:33
  • 1
    Your code shows none of this and is malformed as is. Please show us your actual code or at least fix what is here first. Commented Nov 11, 2015 at 14:35
  • I just read your update and you seem to misunderstand PORO vs ActiveRecord Objects. Boo.find_by_xxx will not do what you want because this relies on AR and SQL. What you have here the best way to implement would be an instance method of Foo. Commented Nov 11, 2015 at 14:40
  • Object oriented design is about responsibilities. Somebody need to be responsible to hold and maintain access to your Boos. Maybe Foo, maybe the Boo class, maybe some other class, maybe even a global variable. It depends on your problem. Just misusing ObjectSpace is the worst option. Commented Nov 11, 2015 at 15:05

3 Answers 3

3

DO NOT RELY ON ObjectSpace for this functionality when the GC collects you will lose these objects because Ruby sees them as unneeded.

This is the general concept of what you are intending to do

class Foo 
  attr_reader :boos
  def initialize(k)
    @boos = (1..k).map {|b| Boo.new(b) } 
  end
  def find_boo_with(x)
    @boos.find {|b| b.a == x }
  end
end

class Boo
  attr_reader :a
  def initialize(a)
    @a = a
  end
end

Then you can do this

f = Foo.new(10)
f.boos
#=> [#<Boo:0x256f428 @a=1>,
     #<Boo:0x256f3b0 @a=2>,
     #<Boo:0x256f368 @a=3>,
     #<Boo:0x256f338 @a=4>,
     #<Boo:0x256f2f0 @a=5>,
     #<Boo:0x256f2d8 @a=6>,
     #<Boo:0x256f2a8 @a=7>,
     #<Boo:0x256f290 @a=8>,
     #<Boo:0x256f278 @a=9>,
     #<Boo:0x256f260 @a=10>]
f.find_boo_with(9)
#=> #<Boo:0x256f278 @a=9>

I hope this helps but you really should look into something like RubyMonk, or TryRuby to get a better understanding of how PORO (Plain Old Ruby Objects) work.

You could also do it this way if you really wanted your find methodology just know that no Boo in this case will ever be collected as they will all be stored in a class instance variable called @all_boos which could casue memory issues depending on what a Boo is and how many of them you are creating.

class Boo
   class << self
      def all_boos
        @all_boos ||= []
      end
      def find(x)
        @all_boos.find {|boo| boo.a == x }
      end
      alias_method :all, :all_boos
   end
   attr_reader :a
   def initialize(a)
     @a = a
     self.class.all_boos << self
   end
end

Then you can do

10.times {|i| Boo.new(i) }
Boo.all
#=> [#<Boo:0x256f428 @a=1>,
     #<Boo:0x256f3b0 @a=2>,
     #<Boo:0x256f368 @a=3>,
     #<Boo:0x256f338 @a=4>,
     #<Boo:0x256f2f0 @a=5>,
     #<Boo:0x256f2d8 @a=6>,
     #<Boo:0x256f2a8 @a=7>,
     #<Boo:0x256f290 @a=8>,
     #<Boo:0x256f278 @a=9>,
     #<Boo:0x256f260 @a=10>] 
Boo.find(9)
#=>  #<Boo:0x256f278 @a=9>
Sign up to request clarification or add additional context in comments.

1 Comment

I used this answer in the end and it worked very well. Thanks for this!
1

If I did understand what you need then I think that you can use ObjectSpace to achieve this. This is the sample code that illustrates the use:

class Foo
  def initialize(k)
    (1..k).each do |b|
       Boo.new(b)
    end
  end
end

class Boo
  attr_reader :a
  def initialize(a)
    @a = a
  end

 def self.find(value)
   ObjectSpace.each_object(Boo).select { |b| b.a == value }.first
 end
end
Foo.new(10)
Boo.find(2).a

Let me know if this is the solution you were looking for.

6 Comments

Haha look at my update I got the smaller parts but your answer puts it together for me. Big thanks !! Small question. What if I already have the .all method as shown in my update.
Nice! I'm equally glad that you have found the answer by yourself and that my sample helped. =D
Do not rely on ObjectSpace when the GC collects you will lose these objects because Ruby sees them as unneeded.
Only if the objects are really not being used, right engineersmnky?
Try this for example 100.times { Boo.new }; ObjectSpace.each_object(Boo).count #=> possibly 100 if GC hasn't started; GC.start; ObjectSpace.each_object(Boo).count #=> 0. This is because the Boo Objects are seen as disposable and marked for GC then when GC runs it disposes of these objects. (BTW do not force GC to run in general this is just an example)
|
0

Not sure I 100% understand your question but using attr_reader in Boo will allow you to access a as if it were a function.

class Foo
  def initialize(k)
    (1..k).each do |b|
       Boo.new(b)
    end
  end
end

class Boo
  attr_reader :a

  def initialize(a)
    @a = a
  end
end

Foo.new(10)

As for the other part of your question, here is a good article explaining how dynamic finders work

1 Comment

This still does nothing as the instance of Foo creates 10 Boos but does not retain them. each will just discard these objects in Foo::initialize

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.