Hey, I''m trying to use STI in my models, while writing a little cms. A have a model called Page and some subclasses like ContentPage, Sysfolder etc. Some classes share similar attributes, so I use STI and one table for all types. Now I''m writing a form for edititing a page and I want to have the opportunity to change the type of it at runtime. But that seems to be a problem: When I do: page = Page.find(x) # page.type is Sysfolder page.update_attributes(params[:record]) # with params[:record][:type] = ContentPage or additionally page.type = ContentPage # or page[:type] = ContentPage page.save! everything is saved, but not the type. When I try to debug the class of page I get Sysfolder (thanks to STI) and I think here lies the problem, because I cannot change the class at runtime. I tried a lot af things like creating a new page and try to copy it over, but none worked. Is there a workaround or option I do not see right now? Regards, Mike --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Mike wrote:> Hey, > > I''m trying to use STI in my models, while writing a little cms. > A have a model called Page and some subclasses like ContentPage, > Sysfolder etc. Some classes share similar attributes, so I use STI and > one table for all types. > Now I''m writing a form for edititing a page and I want to have the > opportunity to change the type of it at runtime. But that seems to be > a problem: > > When I do: > > page = Page.find(x) # page.type is Sysfolder > page.update_attributes(params[:record]) # with > params[:record][:type] = ContentPage > > or additionally > > page.type = ContentPage # or page[:type] = ContentPage > page.save! > > everything is saved, but not the type. When I try to debug the class > of page I get Sysfolder (thanks to STI) and I think here lies the > problem, because I cannot change the class at runtime. I tried a lot > af things like creating a new page and try to copy it over, but none > worked. > > Is there a workaround or option I do not see right now? > > Regards, > > Mike > > > > > >Try page[:type] = "ContentPage" Hope this helps Chris -- ---------------------------- http://www.autopendium.com Stuff about old cars --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
As you can see in my first post (commented out), I already tried this one out, but it''s not working. The problem seems to be that when I say page = Page.find (x), the class of page isn''t "Page", it''s Sysfolder instead (if the "type" of page is Sysfolder) and I''am not able to transform a Sysfolder into another subclass of page (why?). The same problem seems to affect the migration too: By defining a default value for the type like: t.column :type, :string, :default => ''Sysfolder'' it''s not possible to use the create-method on Page with another type. As a result of the create-method, I always get a Sysfolder! I don''t understand, why ActiveRecord doesn''t let me change the type in a normal way! Does anybody found a work around for this problem? I don''t need to use the create-method, but a manual change of the type/class would help me a lot. Maybe I''ll give it a try to use pure SQL to change the type. Thanks in advance. Mike --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
The issues you encounter are by design. Ruby will not let you change the class of an object underneath it (unless using evil.rb: http://rubyforge.org/projects/evil). Changing the type attribute of an instantiated ActiveRecord object is also generally bad practice. Consider this scenario: class Page < ActiveRecord::Base validates_presence_of :title end class SummaryPage < Page validates_presence_of :summary end page = Page.find(:first) page.summary = nil page[:type] = ''SummaryPage'' page.save! This object will pass all validations and save just fine, even though the intent was to change the type to ''SummaryPage'', which requires a ''summary'' attribute that is nil in this case. Alternative solutions could be: page = Page.find(:first) summary_page = SummaryPage.new(page.attributes) You could also write a method that looks up the right class based on a :type attribute and instantiates a new object based on that, and so forth. In ActiveRecord STI, your ''default'' class should be the one that maps to the table itself, not the one defined by the default value of the ''type'' column. - Gabriel On 5/31/07, Mike <mike.zaschka-Mmb7MZpHnFY@public.gmane.org> wrote:> > As you can see in my first post (commented out), I already tried this > one out, but it''s not working. The problem seems to be that when I say > page = Page.find (x), the class of page isn''t "Page", it''s Sysfolder > instead (if the "type" of page is Sysfolder) and I''am not able to > transform a Sysfolder into another subclass of page (why?). > The same problem seems to affect the migration too: > By defining a default value for the type like: > > t.column :type, :string, :default => ''Sysfolder'' > > it''s not possible to use the create-method on Page with another type. > As a result of the create-method, I always get a Sysfolder! > I don''t understand, why ActiveRecord doesn''t let me change the type in > a normal way! > > Does anybody found a work around for this problem? I don''t need to use > the create-method, but a manual change of the type/class would help me > a lot. > Maybe I''ll give it a try to use pure SQL to change the type. > > Thanks in advance. > > Mike > > > > > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Gabriel Gironda wrote:> The issues you encounter are by design. Ruby will not let you change > the class of an object underneath it (unless using evil.rb: > http://rubyforge.org/projects/evil). Changing the type attribute of an > instantiated ActiveRecord object is also generally bad practice. > Consider this scenario: > > class Page < ActiveRecord::Base > validates_presence_of :title > end > > class SummaryPage < Page > validates_presence_of :summary > end > > page = Page.find(:first) > page.summary = nil > page[:type] = ''SummaryPage'' > page.save! > > This object will pass all validations and save just fine, even though > the intent was to change the type to ''SummaryPage'', which requires a > ''summary'' attribute that is nil in this case. Alternative solutions > could be: > > page = Page.find(:first) > summary_page = SummaryPage.new(page.attributes) > > You could also write a method that looks up the right class based on a > :type attribute and instantiates a new object based on that, and so > forth. In ActiveRecord STI, your ''default'' class should be the one > that maps to the table itself, not the one defined by the default > value of the ''type'' column. > > - Gabriel > > On 5/31/07, Mike <mike.zaschka-Mmb7MZpHnFY@public.gmane.org> wrote: > >> As you can see in my first post (commented out), I already tried this >> one out, but it''s not working. The problem seems to be that when I say >> page = Page.find (x), the class of page isn''t "Page", it''s Sysfolder >> instead (if the "type" of page is Sysfolder) and I''am not able to >> transform a Sysfolder into another subclass of page (why?). >> The same problem seems to affect the migration too: >> By defining a default value for the type like: >> >> t.column :type, :string, :default => ''Sysfolder'' >> >> it''s not possible to use the create-method on Page with another type. >> As a result of the create-method, I always get a Sysfolder! >> I don''t understand, why ActiveRecord doesn''t let me change the type in >> a normal way! >> >> Does anybody found a work around for this problem? I don''t need to use >> the create-method, but a manual change of the type/class would help me >> a lot. >> Maybe I''ll give it a try to use pure SQL to change the type. >> >> Thanks in advance. >> >> Mike >> >> >> >> > > > > >I believe you can change the type manually as I had it in my post (providing it passes validation) -- at last it seems to work in the console. However, you will need to instantiate the item again as reload won''t work, as it''s looking for the old "type". However, I agree it is bad practice. Cheers Chris -- ---------------------------- http://www.autopendium.com Stuff about old cars --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
I think we''re defining "type" differently. You can change the type attribute, using the hash accessors, but the object''s class is still the same one it was instantiated with - when you save the object you may have bypassed validations and callbacks and so forth that are defined on the class you''re trying to change to. Reload won''t work not because it''s looking for the old type, but because you can''t change the value of "self" - and once you do reinstantiate from the database, your object is potentially invalid because it was saved as an instance of a different AR derived class than the ''type'' column reflects. - Gabriel On 5/31/07, Chris T <ctmailinglists-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org> wrote:> > Gabriel Gironda wrote: > > The issues you encounter are by design. Ruby will not let you change > > the class of an object underneath it (unless using evil.rb: > > http://rubyforge.org/projects/evil). Changing the type attribute of an > > instantiated ActiveRecord object is also generally bad practice. > > Consider this scenario: > > > > class Page < ActiveRecord::Base > > validates_presence_of :title > > end > > > > class SummaryPage < Page > > validates_presence_of :summary > > end > > > > page = Page.find(:first) > > page.summary = nil > > page[:type] = ''SummaryPage'' > > page.save! > > > > This object will pass all validations and save just fine, even though > > the intent was to change the type to ''SummaryPage'', which requires a > > ''summary'' attribute that is nil in this case. Alternative solutions > > could be: > > > > page = Page.find(:first) > > summary_page = SummaryPage.new(page.attributes) > > > > You could also write a method that looks up the right class based on a > > :type attribute and instantiates a new object based on that, and so > > forth. In ActiveRecord STI, your ''default'' class should be the one > > that maps to the table itself, not the one defined by the default > > value of the ''type'' column. > > > > - Gabriel > > > > On 5/31/07, Mike <mike.zaschka-Mmb7MZpHnFY@public.gmane.org> wrote: > > > >> As you can see in my first post (commented out), I already tried this > >> one out, but it''s not working. The problem seems to be that when I say > >> page = Page.find (x), the class of page isn''t "Page", it''s Sysfolder > >> instead (if the "type" of page is Sysfolder) and I''am not able to > >> transform a Sysfolder into another subclass of page (why?). > >> The same problem seems to affect the migration too: > >> By defining a default value for the type like: > >> > >> t.column :type, :string, :default => ''Sysfolder'' > >> > >> it''s not possible to use the create-method on Page with another type. > >> As a result of the create-method, I always get a Sysfolder! > >> I don''t understand, why ActiveRecord doesn''t let me change the type in > >> a normal way! > >> > >> Does anybody found a work around for this problem? I don''t need to use > >> the create-method, but a manual change of the type/class would help me > >> a lot. > >> Maybe I''ll give it a try to use pure SQL to change the type. > >> > >> Thanks in advance. > >> > >> Mike > >> > >> > >> > >> > > > > > > > > > > I believe you can change the type manually as I had it in my post > (providing it passes validation) -- at last it seems to work in the > console. However, you will need to instantiate the item again as reload > won''t work, as it''s looking for the old "type". However, I agree it is > bad practice. > > Cheers > Chris > > -- > ---------------------------- > http://www.autopendium.com > Stuff about old cars > > > > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
I finally did it the way you described in your first post. page = Page.find(:first) summary_page = SummaryPage.new(page.attributes) The problem I got here was, that after saving summary_page I had a new record in the database. So I extended ActiveRecord::Base with a new_record= method and now I can set this flag manually. Maybe it''s not the best way to do this, but for now it works. After working for a month with Rails now I am still impressed by its features, but I more and more find restrictions to deal with. Well... I think that''s the price to pay for beeing "conventional". Thanks for your help anyway! On May 31, 9:30 pm, "Gabriel Gironda" <gabriel.giro...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> I think we''re defining "type" differently. You can change the type > attribute, using the hash accessors, but the object''s class is still > the same one it was instantiated with - when you save the object you > may have bypassed validations and callbacks and so forth that are > defined on the class you''re trying to change to. > > Reload won''t work not because it''s looking for the old type, but > because you can''t change the value of "self" - and once you do > reinstantiate from the database, your object is potentially invalid > because it was saved as an instance of a different AR derived class > than the ''type'' column reflects. > > - Gabriel > > On 5/31/07, Chris T <ctmailingli...-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org> wrote: > > > > > Gabriel Gironda wrote: > > > The issues you encounter are by design. Ruby will not let you change > > > the class of an object underneath it (unless using evil.rb: > > >http://rubyforge.org/projects/evil). Changing the type attribute of an > > > instantiated ActiveRecord object is also generally bad practice. > > > Consider this scenario: > > > > class Page < ActiveRecord::Base > > > validates_presence_of :title > > > end > > > > class SummaryPage < Page > > > validates_presence_of :summary > > > end > > > > page = Page.find(:first) > > > page.summary = nil > > > page[:type] = ''SummaryPage'' > > > page.save! > > > > This object will pass all validations and save just fine, even though > > > the intent was to change the type to ''SummaryPage'', which requires a > > > ''summary'' attribute that is nil in this case. Alternative solutions > > > could be: > > > > page = Page.find(:first) > > > summary_page = SummaryPage.new(page.attributes) > > > > You could also write a method that looks up the right class based on a > > > :type attribute and instantiates a new object based on that, and so > > > forth. In ActiveRecord STI, your ''default'' class should be the one > > > that maps to the table itself, not the one defined by the default > > > value of the ''type'' column. > > > > - Gabriel > > > > On 5/31/07, Mike <mike.zasc...-Mmb7MZpHnFY@public.gmane.org> wrote: > > > >> As you can see in my first post (commented out), I already tried this > > >> one out, but it''s not working. The problem seems to be that when I say > > >> page = Page.find (x), the class of page isn''t "Page", it''s Sysfolder > > >> instead (if the "type" of page is Sysfolder) and I''am not able to > > >> transform a Sysfolder into another subclass of page (why?). > > >> The same problem seems to affect the migration too: > > >> By defining a default value for the type like: > > > >> t.column :type, :string, :default => ''Sysfolder'' > > > >> it''s not possible to use the create-method on Page with another type. > > >> As a result of the create-method, I always get a Sysfolder! > > >> I don''t understand, why ActiveRecord doesn''t let me change the type in > > >> a normal way! > > > >> Does anybody found a work around for this problem? I don''t need to use > > >> the create-method, but a manual change of the type/class would help me > > >> a lot. > > >> Maybe I''ll give it a try to use pure SQL to change the type. > > > >> Thanks in advance. > > > >> Mike > > > I believe you can change the type manually as I had it in my post > > (providing it passes validation) -- at last it seems to work in the > > console. However, you will need to instantiate the item again as reload > > won''t work, as it''s looking for the old "type". However, I agree it is > > bad practice. > > > Cheers > > Chris > > > -- > > ---------------------------- > >http://www.autopendium.com > > Stuff about old cars--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Gabriel Gironda wrote:> I think we''re defining "type" differently. You can change the type > attribute, using the hash accessors, but the object''s class is still > the same one it was instantiated with - when you save the object you > may have bypassed validations and callbacks and so forth that are > defined on the class you''re trying to change to. > >Yes, we''re both saying the same thing here.> Reload won''t work not because it''s looking for the old type, but > because you can''t change the value of "self" - and once you do > reinstantiate from the database, your object is potentially invalid > because it was saved as an instance of a different AR derived class > than the ''type'' column reflects. > > - Gabriel > > On 5/31/07, Chris T <ctmailinglists-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org> wrote: > >> Gabriel Gironda wrote: >> >>> The issues you encounter are by design. Ruby will not let you change >>> the class of an object underneath it (unless using evil.rb: >>> http://rubyforge.org/projects/evil). Changing the type attribute of an >>> instantiated ActiveRecord object is also generally bad practice. >>> Consider this scenario: >>> >>> class Page < ActiveRecord::Base >>> validates_presence_of :title >>> end >>> >>> class SummaryPage < Page >>> validates_presence_of :summary >>> end >>> >>> page = Page.find(:first) >>> page.summary = nil >>> page[:type] = ''SummaryPage'' >>> page.save! >>> >>> This object will pass all validations and save just fine, even though >>> the intent was to change the type to ''SummaryPage'', which requires a >>> ''summary'' attribute that is nil in this case. Alternative solutions >>> could be: >>> >>> page = Page.find(:first) >>> summary_page = SummaryPage.new(page.attributes) >>> >>> You could also write a method that looks up the right class based on a >>> :type attribute and instantiates a new object based on that, and so >>> forth. In ActiveRecord STI, your ''default'' class should be the one >>> that maps to the table itself, not the one defined by the default >>> value of the ''type'' column. >>> >>> - Gabriel >>> >>> On 5/31/07, Mike <mike.zaschka-Mmb7MZpHnFY@public.gmane.org> wrote: >>> >>> >>>> As you can see in my first post (commented out), I already tried this >>>> one out, but it''s not working. The problem seems to be that when I say >>>> page = Page.find (x), the class of page isn''t "Page", it''s Sysfolder >>>> instead (if the "type" of page is Sysfolder) and I''am not able to >>>> transform a Sysfolder into another subclass of page (why?). >>>> The same problem seems to affect the migration too: >>>> By defining a default value for the type like: >>>> >>>> t.column :type, :string, :default => ''Sysfolder'' >>>> >>>> it''s not possible to use the create-method on Page with another type. >>>> As a result of the create-method, I always get a Sysfolder! >>>> I don''t understand, why ActiveRecord doesn''t let me change the type in >>>> a normal way! >>>> >>>> Does anybody found a work around for this problem? I don''t need to use >>>> the create-method, but a manual change of the type/class would help me >>>> a lot. >>>> Maybe I''ll give it a try to use pure SQL to change the type. >>>> >>>> Thanks in advance. >>>> >>>> Mike >>>> >>>> >>>> >>>> >>>> >>> >> I believe you can change the type manually as I had it in my post >> (providing it passes validation) -- at last it seems to work in the >> console. However, you will need to instantiate the item again as reload >> won''t work, as it''s looking for the old "type". However, I agree it is >> bad practice. >> >> Cheers >> Chris >> >> -- >> ---------------------------- >> http://www.autopendium.com >> Stuff about old cars >> >> >> > > > > >-- ---------------------------- http://www.autopendium.co.uk Stuff about old cars --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Mike wrote:> I finally did it the way you described in your first post. > > page = Page.find(:first) > summary_page = SummaryPage.new(page.attributes)Be aware that protected attributes won''t be copied. So if you are using attr_protected or attr_accessible, you''ll have to write a variant of AR''s clone method that is able to instantiate an object of a different class. -- We develop, watch us RoR, in numbers too big to ignore. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---