builder-7000
builder-7000

Reputation: 7627

Ruby class method to return array of emails

I made a User class containing a class method find_by_email:

class User
  attr_accessor :email
  def initialize
    @email="[email protected]"
  end
  def self.find_by_email(pattern)
    ObjectSpace.each_object(self) do |object|
      puts object.email+" "+(object.email.include? pattern).to_s        
    end
  end
end

In irb I try:

irb> user1=User.new
irb> user2=User.new
irb> user1.email="[email protected]"
irb> User.find_by_email "s"

which returns:

[email protected] false
[email protected] true

I would like find_by_email to return an array with the matching emails. So for this example it should only return ["[email protected]"]. How can I refactor the find_by_email class to achieve this?

Upvotes: 1

Views: 606

Answers (3)

Cary Swoveland
Cary Swoveland

Reputation: 110685

This is an example of the approach I believe @tadman is suggesting.

class User
  attr_accessor :email
  @users = []

  def initialize
    @email="[email protected]"
    self.class.instance_variable_get(:@users) << self
  end

  def self.select_by_email(pattern)
    instance_variable_get(:@users).select { |instance| 
      instance.email.include?(pattern) }
  end
end

user1=User.new
  #=> #<User:0x000055aaab80f6f0 @email="[email protected]"> 
user2=User.new
  #=> #<User:0x000055aaab8536c0 @email="[email protected]"> 
user1.email="[email protected]"
a = User.select_by_email "s"
  #=> [#<User:0x000055aaab80f6f0 @email="[email protected]">]

Note:

User.instance_variable_get(:@users)
  #=> [#<User:0x000055aaab80f6f0 @email="[email protected]">,
  #    #<User:0x000055aaab8536c0 @email="[email protected]">] 

We might then write:

a.map { |user| user.email }
  #=> ["[email protected]"]

I used the method Object#instance_variable_get, rather than creating a "getter" method for @users, because I do not want the content of that (class) instance variable to be accessible outside the class (though I guess I could have made that method private).

Upvotes: 2

builder-7000
builder-7000

Reputation: 7627

After reading the answer/comments I see how ObjectSpace could affect performance if there are many classes and instances to look for. So I decided to use the following class (using code from another question):

class User
  attr_accessor :email
  @@instance_collector = []
  def initialize
    @email="[email protected]"
    @@instance_collector << self
  end
  def self.find_by_email(pattern)
    @@instance_collector.collect do |instance|
      instance.email if instance.email.include? pattern
    end.compact
  end
end

Upvotes: 0

Alex Wayne
Alex Wayne

Reputation: 187044

Your method does not return but only prints out what it finds.

You need to create an array, add to it, then return it.

  def self.find_by_email(pattern)
    # Create an empty array to store the emails.
    emails = []

    # Search!
    ObjectSpace.each_object(self) do |object|

      # If it matches, put the email in the array.
      if object.email.include? pattern
        emails << object.email
      end
    end

    # Return the array.
    emails
  end

And just a word of caution. You should probably stay away from ObjectSpace. This very much gets you under the hood of a ruby application, and into some territory where you may not be ready to go. I would recommend studying a lot more ruby foundational concepts before using it. And even then, it's probably a bad idea.

Upvotes: 3

Related Questions