As we all know well in Rails it is easy to edit a DB record (see scaffolding), where the record object can be edited by a web form. How can I do the same with not DB record? I mean I want to make customer fill in a question sheet and if he or she forgets any field I would return the same sheet remembering what he or she typed in. What is the proper way to do this in Rails? Thanks, Gábor "How do you double the value of a Nokia? Charge up the battery!"
On 2.11.2005, at 14.34, Gábor SEBESTYÉN wrote:> As we all know well in Rails it is easy to edit a DB record (see > scaffolding), where the record object can be edited by a web form. > > How can I do the same with not DB record? I mean I want to make > customer fill in a question sheet and if he or she forgets any > field I would return the same sheet remembering what he or she > typed in. > > What is the proper way to do this in Rails?You have the params hash at your disposal where you have all the values submitted by the form. You can make the form tag helpers use those values as their default when the form is re-displayed. Unfortunately you can''t take the advantage of AR validations so you have to write that functionality yourself. <%= text_field_tag "myfield", params[:myfield] %> When the param is nil, the field will automatically be empty. //jarkko> > Thanks, > > Gábor > > "How do you double the value of a Nokia? Charge up the battery!" > > > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-- Jarkko Laine http://jlaine.net http://odesign.fi _______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails
Gábor SEBESTYÉN wrote:> As we all know well in Rails it is easy to edit a DB record (see > scaffolding), where the record object can be edited by a web form. > > How can I do the same with not DB record? I mean I want to make > customer fill in a question sheet and if he or she forgets any field I > would return the same sheet remembering what he or she typed in. > > What is the proper way to do this in Rails?Create a new, empty record for the form fields initially - this does not get saved to the database. When the user submits the form, use the ''validates_*'' family of methods on the class to prevent it from being saved if any fields are incorrect. If the record isn''t saved in the form''s action, render the form view with the unsaved object. Any error messages generated by the failing validations are available as error_messages_for(object_name). In other words: class FooController < ApplicationController def new @foo = Foo.new end def create if (@foo = Foo.create(params[:foo])).new_record? render :action => ''new'' else redirect_to :action => ''show'', :id => @foo.id end end def show @foo = Foo.find(params[:id]) end end The model and view are left as an exercise for the reader :-) A good way to find out how these things work is to generate the scaffolding for a dummy class, and see how the code fits together. -- Alex
Alex Young wrote:> Gábor SEBESTYÉN wrote: > >>As we all know well in Rails it is easy to edit a DB record (see >>scaffolding), where the record object can be edited by a web form. >> >>How can I do the same with not DB record? I mean I want to make >>customer fill in a question sheet and if he or she forgets any field I >>would return the same sheet remembering what he or she typed in. >> >>What is the proper way to do this in Rails? > > > Create a new, empty record for the form fields initially - this does not > get saved to the database. > > When the user submits the form, use the ''validates_*'' family of methods > on the class to prevent it from being saved if any fields are incorrect.Isn''t this a bit hacky? I mean, the model would be a subclass of activerecord (right?), but it isn''t an activerecord, because activerecord is tied to a database table. Isn''t it possible to call the validates_* functions from just any class? Perhaps the validates_* function could be moved to some helper class so that ActiveRecord can use those and non-AR classes can use them too? Jeroen
On 2005.11.02., at 14:36, Jeroen Houben wrote:> Isn''t this a bit hacky? I mean, the model would be a subclass of > activerecord (right?), but it isn''t an activerecord, because > activerecord is tied to a database table.I agree. This is the only part of RoR that doesn''t make me happy. I had been working for years with Apple''s WebObjects which is the Crown of web application devkits and it had the Sacred Trinity: Application, Session and Component. Component is the same as our controller + view and it can be persistent so if I want to create a form with a lot of fields it will remember all of them until its session expires. I don''t need flash hash and other tricks. This is I really miss from RoR, PHP and other similar webdev stuffs. Gábor "ALERT! Windows not found! [C]heers, [P]arty or [D]ance?" _______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails
Jeroen Houben wrote:> Isn''t this a bit hacky? I mean, the model would be a subclass of > activerecord (right?), but it isn''t an activerecord, because > activerecord is tied to a database table.The hackiness depends on how closely the form is tied to a model. The lowest common denominator is one model -> one form, in which case this technique is a natural fit.> Isn''t it possible to call the validates_* functions from just any class? > Perhaps the validates_* function could be moved to some helper class so > that ActiveRecord can use those and non-AR classes can use them too?The thing that using an validates_* on AR gives you is that you don''t have to do any calling *at all*, either when creating or updating the data record. The save() call either works or it doesn''t. Of course, if you don''t want the data to go near a database, AR is still convenient because you can just call @foo.valid? to get all the validations checked. Having written that, I went away to see if ActiveRecord::Validations could just be included into a custom form model class, but it seems to be fairly deeply embedded into AR... -- Alex
Alex Young wrote:> Jeroen Houben wrote: > >>Isn''t it possible to call the validates_* functions from just any class? >>Perhaps the validates_* function could be moved to some helper class so >>that ActiveRecord can use those and non-AR classes can use them too? > > The thing that using an validates_* on AR gives you is that you don''t > have to do any calling *at all*, either when creating or updating the > data record. The save() call either works or it doesn''t. Of course, > if you don''t want the data to go near a database, AR is still convenient > because you can just call @foo.valid? to get all the validations checked.Okay but you''re still stuck with a model that has all the characteristics of an AR. You''re specifying the model as being a 1-on-1 representation of a database record but is not. That can never be a good thing IMHO. A 2nd developer could potentially call methods such as "find" or "save" which the model cannot deal with.> > Having written that, I went away to see if ActiveRecord::Validations > could just be included into a custom form model class, but it seems to > be fairly deeply embedded into AR... >Yes I saw that too. As I''m fairly new to RoR I may not be able to see how you could do certain advanced things with the AR validation, I''m more used to validation being tied to a form. Can one, for instance, easily create a form wizard and validate certain fields on each step? example: Step 1 would ask for and validate a persons email address Step 2 would ask for and validate a persons firstname Step 3 would ask for and validate a persons lastname The user cannot proceed to the next step if the current step is not valid. Simply calling person.valid? would not work because it would try and validate all attributes on the first step, which would never pass. AFAIK this is a another shortcoming of AR validation. I''m not saying AR validation is a bad thing, it''s briliant for simple "validate this model before saving it" type stuff, but it would be great if the core methods it is using could somehow be re-used independently of AR. Jeroen
On 2-nov-2005, at 16:00, Jeroen Houben wrote:> Alex Young wrote: >> Jeroen Houben wrote: >>> Isn''t it possible to call the validates_* functions from just any >>> class? >>> Perhaps the validates_* function could be moved to some helper >>> class so >>> that ActiveRecord can use those and non-AR classes can use them too? >> The thing that using an validates_* on AR gives you is that you don''t >> have to do any calling *at all*, either when creating or updating the >> data record. The save() call either works or it doesn''t. Of >> course, >> if you don''t want the data to go near a database, AR is still >> convenient >> because you can just call @foo.valid? to get all the validations >> checked. > > Okay but you''re still stuck with a model that has all the > characteristics of an AR. You''re specifying the model as being a 1- > on-1 representation of a database record but is not. That can never > be a good thing IMHO. A 2nd developer could potentially call > methods such as "find" or "save" which the model cannot deal with. > >> Having written that, I went away to see if ActiveRecord::Validations >> could just be included into a custom form model class, but it >> seems to >> be fairly deeply embedded into AR... > > Yes I saw that too. As I''m fairly new to RoR I may not be able to > see how you could do certain advanced things with the AR > validation, I''m more used to validation being tied to a form. Can > one, for instance, easily create a form wizard and validate certain > fields on each step? > example: > Step 1 would ask for and validate a persons email address > Step 2 would ask for and validate a persons firstname > Step 3 would ask for and validate a persons lastname > > The user cannot proceed to the next step if the current step is not > valid. > > Simply calling person.valid? would not work because it would try > and validate all attributes on the first step, which would never > pass. AFAIK this is a another shortcoming of AR validation. I''m > not saying AR validation is a bad thing, it''s briliant for simple > "validate this model before saving it" type stuff, but it would be > great if the core methods it is using could somehow be re-used > independently of AR.Something akin to Jango,s mediators is really needed, but for your needs conditional validations will suffice. Consult the AR docs. You can implement a virtual method signup_step which you can use while persisting the record through the session. # step 1 record.update_attributes(params[:thing]) record.signup_step = 2 # step 2 record.update_attributes(params[:thing]) record.signup_step = 3 -- Julian "Julik" Tarkhanov
Jeroen Houben wrote:> Okay but you''re still stuck with a model that has all the > characteristics of an AR. You''re specifying the model as being a 1-on-1 > representation of a database record but is not. That can never be a good > thing IMHO. A 2nd developer could potentially call methods such as > "find" or "save" which the model cannot deal with.That''s what an appropriate refactoring of ActiveRecord::Validations might achieve - the ability to call valid? without an assumption of a database table behind it. Of course, there''s no reason not to define a custom form model that has the valid? method defined completely independently from AR''s version.> Yes I saw that too. As I''m fairly new to RoR I may not be able to see > how you could do certain advanced things with the AR validation, I''m > more used to validation being tied to a form. Can one, for instance, > easily create a form wizard and validate certain fields on each step? > example: > Step 1 would ask for and validate a persons email address > Step 2 would ask for and validate a persons firstname > Step 3 would ask for and validate a persons lastnameIf it were me, I''d be worried about business logic creeping out of the models with validations tied to forms. That being said, it would be relatively simple to do: class Form attr_accessor :email attr_accessor :firstname attr_accessor :lastname attr_accessor :stage attr_reader :errors def valid? check_validity [:email, :firstname, :lastname][0 ... stage] end end and pass the stage as a hidden form field. But then, that''s putting presentation logic in the model, which is hardly any better. -- Alex
Julian ''Julik'' Tarkhanov wrote:> Something akin to Jango,s mediators is really needed, but for your > needs conditional validations will suffice. Consult the AR docs. You > can implement a virtual method signup_step which you can use while > persisting the record through the session. > > # step 1 > record.update_attributes(params[:thing]) > record.signup_step = 2 > > # step 2 > record.update_attributes(params[:thing]) > record.signup_step = 3 >Well, you learn something new every day :-) I had no idea validations could take conditions... -- Alex
Julian ''Julik'' Tarkhanov wrote:> >> As I''m fairly new to RoR I may not be able to see >> how you could do certain advanced things with the AR validation, I''m >> more used to validation being tied to a form. Can one, for instance, >> easily create a form wizard and validate certain fields on each step? >> example: >> Step 1 would ask for and validate a persons email address >> Step 2 would ask for and validate a persons firstname >> Step 3 would ask for and validate a persons lastname >> >> The user cannot proceed to the next step if the current step is not >> valid. >> >> Simply calling person.valid? would not work because it would try and >> validate all attributes on the first step, which would never pass. >> AFAIK this is a another shortcoming of AR validation. I''m not saying >> AR validation is a bad thing, it''s briliant for simple "validate this >> model before saving it" type stuff, but it would be great if the core >> methods it is using could somehow be re-used independently of AR. > > > Something akin to Jango,s mediators is really needed, but for your > needs conditional validations will suffice. Consult the AR docs. You > can implement a virtual method signup_step which you can use while > persisting the record through the session. > > # step 1 > record.update_attributes(params[:thing]) > record.signup_step = 2 > > # step 2 > record.update_attributes(params[:thing]) > record.signup_step = 3 >Okay, this is a good idea, but this would mean you could only validate the model at the end of the ride, step 3, right? I would like to validate some attributes in step one and be able to display errors immediately. Jeroen
Alex Young wrote:> Jeroen Houben wrote: > >>Okay but you''re still stuck with a model that has all the >>characteristics of an AR. You''re specifying the model as being a 1-on-1 >>representation of a database record but is not. That can never be a good >>thing IMHO. A 2nd developer could potentially call methods such as >>"find" or "save" which the model cannot deal with. > > That''s what an appropriate refactoring of ActiveRecord::Validations > might achieve - the ability to call valid? without an assumption of a > database table behind it. > > Of course, there''s no reason not to define a custom form model that has > the valid? method defined completely independently from AR''s version. > > >>Yes I saw that too. As I''m fairly new to RoR I may not be able to see >>how you could do certain advanced things with the AR validation, I''m >>more used to validation being tied to a form. Can one, for instance, >>easily create a form wizard and validate certain fields on each step? >>example: >>Step 1 would ask for and validate a persons email address >>Step 2 would ask for and validate a persons firstname >>Step 3 would ask for and validate a persons lastname > > If it were me, I''d be worried about business logic creeping out of the > models with validations tied to forms. That being said, it would be > relatively simple to do: > > class Form > attr_accessor :email > attr_accessor :firstname > attr_accessor :lastname > attr_accessor :stage > > attr_reader :errors > > def valid? > check_validity [:email, :firstname, :lastname][0 ... stage] > end > end > > and pass the stage as a hidden form field. But then, that''s putting > presentation logic in the model, which is hardly any better. >This could work, but you''re hiding the most important bit :-) What would the check_validity method look like? I guess you could not use the Form class at all but call the check_validity method straight from the controller. Jeroen
Jeroen Houben wrote:> Julian ''Julik'' Tarkhanov wrote: >> # step 1 >> record.update_attributes(params[:thing]) >> record.signup_step = 2 >> >> # step 2 >> record.update_attributes(params[:thing]) >> record.signup_step = 3 >> > > Okay, this is a good idea, but this would mean you could only validate > the model at the end of the ride, step 3, right? I would like to > validate some attributes in step one and be able to display errors > immediately.Nope. class Person < ActiveRecord::Base attr_writer :signup_step validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/, :if => signup_ver(0) validates_presence_of :firstname, :if => signup_ver(1) validates_presence_of :lastname, :if => signup_ver(2) def signup_ver(a) Proc.new{|p| p.signup_step > a} end end If I''ve read it correctly, you can take it step by step, validating each time, and only the validations relevant to that step will get called. Which is nice :-) -- Alex
Jeroen Houben wrote:>> class Form >> attr_accessor :email >> attr_accessor :firstname >> attr_accessor :lastname >> attr_accessor :stage >> >> attr_reader :errors >> >> def valid? >> check_validity [:email, :firstname, :lastname][0 ... stage] >> end >> end >> >> and pass the stage as a hidden form field. But then, that''s putting >> presentation logic in the model, which is hardly any better. >> > > This could work, but you''re hiding the most important bit :-) > What would the check_validity method look like?This margin''s too small. Oh, all right then: def check_validity(attrs) attrs.find_all{|a| !self.send(a.to_s + ''_valid?'')}.length == 0 end def email_valid? email =~ /#{email_regexp}/ end def firstname_valid? firstname.length > 0 end def lastname_valid? lastname.length < 42 end These _valid? methods are ripe for macroisation, obviously...> I guess you could not use the Form class at all but call the > check_validity method straight from the controller.Well, something''s got to know what a valid email address looks like, and it might be different for different situations - for (a contrived) example, your customers might be able to specify any RFC-valid email address, but there might only be a limited number of valid domains that internal email addresses could belong to. That''s business logic, and that should go in a model. Calling check_validity?[:email] on a specific instance of a Form would be OK, I reckon. -- Alex