Forum: Ruby on Rails Database - Switching Tables

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
80159acdf89580a20e8e835772b5bf82?d=identicon&s=25 Doug Jolley (ddjolley)
on 2009-05-04 23:39
(Received via mailing list)
I have a mailing-list database that consists of several tables (e.g.,
clients table, prospects table, etc.).  How can I switch between these
tables within my mailing-list Rails application?

I have a feeling that if I better understood the role of the model I
might be able to answer my own question.  I'm hoping that getting this
question answered will help me in that regard.

Thanks for any input.

         ... doug
Ef0db53920b243d6758c2f6b1306df0d?d=identicon&s=25 Steve Ross (cwd)
on 2009-05-05 01:46
(Received via mailing list)
Hi--
On May 4, 2009, at 2:38 PM, doug wrote:

> I have a mailing-list database that consists of several tables (e.g.,
> clients table, prospects table, etc.).  How can I switch between these
> tables within my mailing-list Rails application?
>
> I have a feeling that if I better understood the role of the model I
> might be able to answer my own question.  I'm hoping that getting this
> question answered will help me in that regard.

In Rails, a model normally maps to a table. So if you have a clients
table, you have a Client model and if your have a prospects table, you
have a Prospect model. Rails infers the table name and columns from
the name of the model class. So you can do:

send_email_to :clients
send_email_to :prospects

def send_email_to(whom)
   model = whom.to_s.camelize.constantize.all.each do |recipient|
     # recipient is an object of the specified type. Send email at will!
   end
end
80159acdf89580a20e8e835772b5bf82?d=identicon&s=25 Doug Jolley (ddjolley)
on 2009-05-05 03:24
(Received via mailing list)
> In Rails, a model normally maps to a table.

Yes; but, it appears that in this case it would be really nice if I
could have a single model and then somehow simply switch between
associated tables.  (I'm not sure that I'm going to be able to do it.)

> Rails infers the table name and columns from  
> the name of the model class.

I understand that default inference. Obviously, if I am able to
succeed at what I am trying to do, I would be overriding that default.

Thanks for the input.

         ... doug
Dd2d775dea75b381edb1bbf0600a0907?d=identicon&s=25 Marnen Laibow-Koser (marnen)
on 2009-05-05 04:38
Doug Jolley wrote:
>> In Rails, a model normally maps to a table.
>
> Yes; but, it appears that in this case it would be really nice if I
> could have a single model and then somehow simply switch between
> associated tables.  (I'm not sure that I'm going to be able to do it.)

Why not just subclass?

class Person < ActiveRecord::Base
  self.abstract_class = true # this will prevent Rails from expecting a
"people" table

  # common features go here
end

class Client < Person
  ...
end

class Prospect < Person
  ...
end

That should do the trick!

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
410ff5abeb4b1c686fe49d70dbba1c8f?d=identicon&s=25 Gabriel Saravia (theperuvian)
on 2009-05-05 14:43
I've been working on something like this myself.  Depending on how
different the two are, it may also just be easier to use Single Table
Inheritance?

if you decide that abstract classes are better, then I suggest that you
do something like this to make your life easier:

class Person < ActiveRecord::Base

TypesofPeople = ["Client", "Prospect"]


def self.abstract_find (person_type, *args)
  if TypesofPeople.include?(person_type)
    person_type.constantize.find(*args)
  else
    nil
  end
end

cheers,
-Gabe

Marnen Laibow-Koser wrote:
> Doug Jolley wrote:
>>> In Rails, a model normally maps to a table.
>>
>> Yes; but, it appears that in this case it would be really nice if I
>> could have a single model and then somehow simply switch between
>> associated tables.  (I'm not sure that I'm going to be able to do it.)
>
> Why not just subclass?
>
> class Person < ActiveRecord::Base
>   self.abstract_class = true # this will prevent Rails from expecting a
> "people" table
>
>   # common features go here
> end
>
> class Client < Person
>   ...
> end
>
> class Prospect < Person
>   ...
> end
>
> That should do the trick!
>
> Best,
> --
> Marnen Laibow-Koser
> http://www.marnen.org
> marnen@marnen.org
80159acdf89580a20e8e835772b5bf82?d=identicon&s=25 Doug Jolley (ddjolley)
on 2009-05-05 17:47
(Received via mailing list)
> Why not just subclass?

I like that approach.  It was essentially what I was playing with when
I submitted my original post.  The problem with it seems to be that I
can't reference the subclass.  Extending your example, I would like to
say something like:

clients=Client.find(:all)

Rails doesn't like that.  It complains that Client is an uninitialized
constant.  It appears that Rails wants me to say something like:

clients=Person.find(:all)

IOW, it appears that Rails wants the name of the class to correspond
the base name of the model file (i.e., since the name of the model
file is person.rb, it wants the class to be Person).  Without that
correlation, I get the uninitialized constant error.  I'm not sure how
to get around the problem.  If I could; then, I think your suggestion
would work just fine.

Thanks.

       ... doug
Dd2d775dea75b381edb1bbf0600a0907?d=identicon&s=25 Marnen Laibow-Koser (marnen)
on 2009-05-05 18:10
Doug Jolley wrote:
[...]
>
> IOW, it appears that Rails wants the name of the class to correspond
> the base name of the model file (i.e., since the name of the model
> file is person.rb, it wants the class to be Person).

Yes, this is how Rails works.  Put each class definition in a separate
file (generally best), or use explicit require statements.

[...]
>        ... doug

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
80159acdf89580a20e8e835772b5bf82?d=identicon&s=25 Doug Jolley (ddjolley)
on 2009-05-05 18:20
(Received via mailing list)
> 6:   else
> 7:     nil
> 8:   end
> 9: end

I really wish that I understood your suggestion especially because it
may be what works.  I have taken the liberty of adding line numbers.
Basically, I understand the code except for line 5.  I have no idea
what you are doing there.  I'm not sure what the person_type and *args
are that you are passing to abstract_find.  Part of my confusion may
stem from the fact that there does not seem to be and 'end' for your
class definition.  So, I'm not altogether clear on whether lines 3
through 9 are suggested model code or suggested controller code.
Could you please clarify?  I do appreciate your suggestion and I have
high hopes for it being my salvation. Thanks.

       ... doug
80159acdf89580a20e8e835772b5bf82?d=identicon&s=25 Doug Jolley (ddjolley)
on 2009-05-05 20:24
(Received via mailing list)
> Put each class definition in a separate file

By "separate file" you mean "separate model", right?  I guess I could
do that.  My concern is that if I do I may be opening and closing the
connection to the database?  Maybe I am and maybe I am not.  If I am,
maybe I don't need to worry about it; but, I do. That's why I wanted
to let Rails be in charge of handling the connection to the database.
I just want to swap tables.

Maybe I should ask a couple of more basic questions:  What, if any,
are the adverse affects associated with switching models from with an
application?  If I swap models from within an application, does that
result in closing and re-opening the database?

Thanks.

      ... doug
E163d36afbbcae05d1b6408265fd616d?d=identicon&s=25 Maximiliano Guzman (mguzman)
on 2009-05-05 20:51
Doug Jolley wrote:
>> Put each class definition in a separate file
>
> By "separate file" you mean "separate model", right?  I guess I could
> do that.  My concern is that if I do I may be opening and closing the
> connection to the database?  Maybe I am and maybe I am not.  If I am,
> maybe I don't need to worry about it; but, I do. That's why I wanted
> to let Rails be in charge of handling the connection to the database.
> I just want to swap tables.
>
> Maybe I should ask a couple of more basic questions:  What, if any,
> are the adverse affects associated with switching models from with an
> application?  If I swap models from within an application, does that
> result in closing and re-opening the database?
>
> Thanks.
>
>       ... doug

yes, you'll need to have client.rb (containing the Client class) and
prospect.rb (containing the Prospect class) files in app/models .

No, the amount of models is not related to connections/disconnections
from the database. No matter the amount of models, Rails is handling the
connection to the database. The way to swap tables is using one model
per table.

There are no adverse effects of switching models, the database
connection is created when you start the app server (webrick, mongrel,
phusion) and closed when you end the server.

 hope it helps,

Maximiliano Guzman
80159acdf89580a20e8e835772b5bf82?d=identicon&s=25 Doug Jolley (ddjolley)
on 2009-05-05 21:21
(Received via mailing list)
>  hope it helps,

Great answer.  Love it! :)  I think that I'm a happy camper.

Thanks to all who contributed.

       ... doug
410ff5abeb4b1c686fe49d70dbba1c8f?d=identicon&s=25 Gabriel Saravia (theperuvian)
on 2009-05-07 01:27
Hey Doug,

i believe this is the line that was confusing you?

person_type.constantize.find(*args)


first, all of what i wrote was model code, and yes, you should break up
the two classes into two separate model files, or really, three:
Person.rb, Client.rb, and Prospect.rb.

Ok, so in that one line, person_type is a string.  rails mixes in a
function to the String class called "constantize" that changes a string
into an actual program token,

so, if person_type is "Client" then person_type.constantize is the
actual Programming token Client.  As long as you define the Client
class, rails will then interpret the above line as

Client.find(*args)

OR

Prospect.find(*args)

depending on the string passed into abstract_find.


The reason i have *args is because it is what the ActiveRecord API
specificies as the argument to find - (basically all arguments passed to
ActiveRecord find are rolled up into an array, which it then figures out
what to do with)

so, the above code would allow you to, in your controller, say something
like,


@person = Person.abstract_find("Client", [normal find arguments go
here])

or

@person = Person.abstract_find("Prospect", [normal find arguments go
here])


the real power comes when you basically let the actual type be
dynamically determined by the program/user at run time:

@person = Person.abstract_find(specified_person_type, [normal find
arguments go here])


I know you had said that you had pretty much figured it out, but i
thought this might still be useful. hope it is!

-Gabe


Doug Jolley wrote:
>> 6: � else
>> 7: � � nil
>> 8: � end
>> 9: end
>
> I really wish that I understood your suggestion especially because it
> may be what works.  I have taken the liberty of adding line numbers.
> Basically, I understand the code except for line 5.  I have no idea
> what you are doing there.  I'm not sure what the person_type and *args
> are that you are passing to abstract_find.  Part of my confusion may
> stem from the fact that there does not seem to be and 'end' for your
> class definition.  So, I'm not altogether clear on whether lines 3
> through 9 are suggested model code or suggested controller code.
> Could you please clarify?  I do appreciate your suggestion and I have
> high hopes for it being my salvation. Thanks.
>
>        ... doug
80159acdf89580a20e8e835772b5bf82?d=identicon&s=25 Doug Jolley (ddjolley)
on 2009-05-07 18:01
(Received via mailing list)
> i believe this is the line that was confusing you?
>
> person_type.constantize.find(*args)

Thanks, Gabe, for the very thorough explanation.

     ... doug
This topic is locked and can not be replied to.