I''m a Java refugee, who is a bit of a Ruby and Rails noob, but
I''m
certainly enjoying myself and have had a bit of success in creating a
few RoR bits and pieces.. however, I think I must be missing something.
I just get the feeling that, although I''ve read through a couple of
good
books, there is something quite basic in RoR that I''m just not
''getting''. So, here is a (mildly contrived) example of the
sort of thing
I''ve been trying to do - maybe some experts out there could
metaphorically slap me on the back of the head and say "Duh!"
Firstly, I have a simple model:
create_table :uris do |t|
t.column :loc, :string, :null => false
end
create_table :links do |t|
t.column :uri_id, :integer, :null => false
t.column :text, :string, :null => false
t.column :color, :string
end
I''ve then used generate scaffold_resource to create the usual default
files, to which, I''ve made the following changes
uri.rb:
class Uri < ActiveRecord::Base
has_many :links
def to_s
loc
end
end
link.rb:
class Link < ActiveRecord::Base
belongs_to :uri
end
link_controller.rb:
def update
@link = Link.find(params[:id])
@uri = Uri.new(:loc=>params[:uri][:loc])
@link.uri = @uri
...
def create
@link = Link.new(params[:link])
@uri = Uri.new(:loc=>params[:uri][:loc])
@link.uri = @uri
... # (the rest is the same as the auto-generated code)
views/links/new.rhtml:
...
<% form_for(:link, :url => links_path) do |f| %>
<p>Text: <%= f.text_field :text %></p>
<% fields_for :uri do |u| %>
<p>URL: <%= u.text_field :loc %></p>
<% end %>
<p>Color: <%= f.text_field :color %></p>
<p><%= submit_tag "Create" %></p>
<% end %>
...
views/uris/new.rhtml:
...
<% form_for(:uri, :url => uris_path) do |f| %>
<p>URI Loc: <%= f.text_field :loc %></p>
<p><%= submit_tag "Create" %></p>
<% end %>
...
This seems to be working OK, although I''m fairly sure I''ve not
gone
about it quite the right way (in terms of the best way of writing the
views and controller to manipulate the Uri model and it''s relationship
with Link).
If anyone has any comments on that, I''m all ears! But anyway, at least
it works...!
Incidentally, my Uri view *didn''t* work when I originally named the Uri
model Url... it failed at the line <% form_for(:url, :url => links_path)
do |f| %>. I''m guess some sort of naming conflict.. any ideas what I
should have done on that, apart from changing the name of my model?
What I''m really struggling with, though, is this: I want to have
default
values for my attributes. That is, values that a new Link object would
default to when it is instantiated. These would show up on the form when
a user asks to create a new object.
For a while, I tried using the before_create filter, but had no joy
until I realised that this inserts the default values if the attributes
are empty before saving the record to the database, which is too late
for me...
So, the most obvious place, I thought, was the initialize methods:
link.rb:
def initialize
super
@color="#999999"
@text="default text"
end
This doesn''t work. I''ve no idea why, it looks like it should
work. I''m
setting the instance variables to a default value, yet when the "new"
form is displayed, the fields are blank. :(
I played around with a couple more permutations of this, but to no
avail...
The best that happens is that a) no default values are displayed, and b)
I get "wrong number of arguments (1 for 0)" when the create method is
called. Bah!
My next effort was to add my own reader methods:
link.rb:
def color
@color || "#999999"
end
def text
@text || "default text"
end
No joy here either. Again, it looks to me like these should work... I
don''t understand why they don''t. They system appears to be
acting as
though these variables aren''t used, and/or these methods
aren''t called,
but the way I understand everything I''ve read says that they are!
I''m
*sure* they are - I''ve seen "password" and
"password=" methods
overwritten in several different examples so that a hashed password can
be created and saved to the DB.
So, my next thought is that maybe whatever ActiveRecord sets @color to
does not evaluate to false, so my code always returns @color, and never
"#9999999".
But:
def color
"#999999"
end
does not work either! Argh! In fact, now the following happens: I try to
create a new link. The form is displayed with no defaults. I enter some
values, and the object seems to be created properly. However, the value
"#999999" is displayed when I use the "show" view, and the
value I
actually entered is displayed when I display the "edit" view.
OK, so IIRC, my next attempt was using something along the lines of
<p>Text: <%= f.text_field :text, :value=>"default text"
%></p>
Which looked promising until I wanted to use the same _partial files for
both the edit and create methods... in which case, "default text"
shows
up even when editing an object that already has a value for that
attribute.
After much googling, I came across another idea. For some reason not
clear to me, this should work:
link.rb:
def text
self[:text] or "default text"
end
It doesn''t seem to work for me either. I''m not sure why it
would, but
the blog I read it on seemed to think it did work, and so did the
comments on the blog, so I''ve no idea what I''m doing wrong.
Ho hum... My next idea is to set these default values in the controller.
This seems wrong to me - default values for a model ought to be defined
in the model, shouldn''t they? But by now, I just want to get the thing
working even if its not where I think it ought to be!
so, my next attempt is:
link_controller.rb
def new
@link = Link.new
@link.text=''link_controller default text''
@link.color=''#ffffff''
end
Yay! That worked. It''s not, IMO, the right place to put the code, but
it
works, so I''m putting up with it for now...
But how do I get a default value for the Uri?
I try adding this line to my "new" method:
@link.uri = Uri.new(:loc=>''www.example.com/example.html'')
It doesn''t work. At this point, I''m all out of ideas, and this
is where
I decided that my understanding just has a big whole in it that needs
filling, so I''ve written this long post!
Whatever it is that I''m not ''getting'', I think its
causing me other
problems too. For example, if have a list of languages, and want to have
a select box to assign a language to a link, I''d assume that at some
point the "language_id=" method would get called, so I write:
def language_id=(lid)
self.language=Language.find(lid)
end
or
def language_id=(lid)
@language=Language.find(lid)
end
or
def language_id=(lid)
language=lid
end
or any number of permutations of this sort of thing, none of which are
wiping the scowl off my face!
---
Thanks for taking the time to read down this far in the post, and any
advice is much appreciated!
--
Posted via http://www.ruby-forum.com/.
--~--~---------~--~----~------------~-------~--~----~
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
-~----------~----~----~----~------~----~------~--~---
Hi, After skim reading your post, is your question "How do I set default values for ActiveRecord?". Which I find a bit more concise. If so, a quick Google search indicates that you have merely used the wrong way to access the attributes, you should use their accessor as I don''t believe they are stored in "@" variables. See here: http://made-of-stone.blogspot.com/2007/01/default-values-in-your-models.html Ignore me if I''ve missed the point of your post. Regards, Andrew --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---
Andrew France wrote:> Hi, > > After skim reading your post, is your question "How do I set default > values for ActiveRecord?". Which I find a bit more concise. > > If so, a quick Google search indicates that you have merely used the > wrong way to access the attributes, you should use their accessor as I > don''t believe they are stored in "@" variables. > > See here: > http://made-of-stone.blogspot.com/2007/01/default-values-in-your-models.html > > Ignore me if I''ve missed the point of your post. > > Regards, > AndrewThanks, that''s helped me a bit.. There were a whole bunch of questions up there, but that was certainly one of them :) I still don''t understand why the things I tried don''t work. Why, for example, does it appear that either implementation of the method: def text @text || "default text" end or def text self[:text] or "default text" end don''t appear to be called by the form builders? If @text isn''t used by ActiveRecord, I''d have thought the first one would at least return the default text, and if ActiveRecord does use a hash, then I''d have thought the second one does work... From your post, I think I''ve made a bit of progress, so thank you for that :) I can understand that I should use accessor methods instead of @ variables. That makes sense and seems like good practice whatever models you are working with, since you can''t relying on the underlying implementation. However... I did this: link.rb: def initialize (params = nil) super self.text="default text from init" unless self.text self.color="#999999" unless self.color end and the "new" form now provides a default for color, but not for text. This just makes me more confused...! And, I still haven''t got this working with the Uri relationship! -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---
> def initialize (params = nil) > super > self.text="default text from init" unless self.text > self.color="#999999" unless self.color > end > > and the "new" form now provides a default for color, but not for text. > This just makes me more confused...! And, I still haven''t got this > working with the Uri relationship!I''m not sure if this has any bearing on your particular problem, but "text" may no be a very "safe" column name to use. I have had issues in the past with MySQL when using keywords as column names. It''s best to use column names that are not database specific keywords. --~--~---------~--~----~------------~-------~--~----~ 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''ve put some thought into this and have done some experimenting and
thought I would bring up a few points of discussion:
It seems to me there are multiple definitions of a "default value."
1. Database/Model level defaults:
In relation to a model object it would be values that would define the
"natural state" of an attribute. Take for example an attribute
access_level on a User object. A natural default state may be a value
representing a "regular" user (say 1 = "Regular user").
This type of default value can be provided right in your database
migration like:
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.column :created_at, :datetime #<< Notice that Rails will
automatically take care of the default value
t.column :first_name, :string
t.column :last_name, :string
t.column :access_level, :integer, :default => 1 #<< 1 = Regular
user
end
end
def self.down
drop_table :users
end
end
It is also possible (although it seems unnecessary given the above) to
set these types of defaults in the model class like:
class User < ActiveRecord::Base
def initialize(params = nil)
super
self.access_level ||= 1
end
end
In other cases the "default value" may actually be related more to a
particular view.
I recently ran into an example like this: I had a event scheduler
that I wanted a default value for the current time to be set on the
datatime selection control. In my opinion this is not a model level
default value. To create an event, expecting people to subscribe to
the event later, it is not the "natural state" of the Event object to
have a start_time = Time.now. However, it is convenient to initialize
the form''s control to default to the current date and time to make the
user''s selection process easier.
It just happens that the built-in time widgets do this (I think so
anyway). But in any case the initial state of the model object''s
start_time attribute to be nil and not Time.now.
So assuming that this behavior is not built into the form''s control
object Rails provides a convenient way to construct a new object with
some attributes initialized that can be set from with the controller
like:
# GET /people/new
def new
@person = User.new(access_level => 1)
end
Maybe some purist MVC guys would say that is breaking the MVC design
pattern, and maybe it is, but sometimes the convenience is so great
that it''s worth bending the rules a bit. However, this is similar to
many "factory constructor" design patterns that I often see in use.
It would be more "pure" if the form''s control were extended
to provide
a default option.
Something like:
<p>
<b>Access Level</b><br />
<%= f.text_field :access_level, :options => {:default => 1 }%>
#
DON''T do this it''s just made up to illustrate a point
</p>
--~--~---------~--~----~------------~-------~--~----~
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 recently ran across the problem of needing to initialize a model''s relation on creation and used the ''faux accessor'' solution because you really shouldn''t be overriding an AR initialize method. Josh outlines the technique here: http://blog.hasmanythrough.com/2007/1/22/using-faux-accessors-to- initialize-values -Michael http://javathehutt.blogspot.com On Feb 2, 2007, at 11:01 AM, Robert Walker wrote:> > I''ve put some thought into this and have done some experimenting and > thought I would bring up a few points of discussion: > > It seems to me there are multiple definitions of a "default value." > > 1. Database/Model level defaults: > In relation to a model object it would be values that would define the > "natural state" of an attribute. Take for example an attribute > access_level on a User object. A natural default state may be a value > representing a "regular" user (say 1 = "Regular user"). > > This type of default value can be provided right in your database > migration like: > > class CreateUsers < ActiveRecord::Migration > def self.up > create_table :users do |t| > t.column :created_at, :datetime #<< Notice that Rails will > automatically take care of the default value > t.column :first_name, :string > t.column :last_name, :string > t.column :access_level, :integer, :default => 1 #<< 1 = Regular > user > end > end > > def self.down > drop_table :users > end > end > > It is also possible (although it seems unnecessary given the above) to > set these types of defaults in the model class like: > > class User < ActiveRecord::Base > def initialize(params = nil) > super > self.access_level ||= 1 > end > end > > In other cases the "default value" may actually be related more to a > particular view. > > I recently ran into an example like this: I had a event scheduler > that I wanted a default value for the current time to be set on the > datatime selection control. In my opinion this is not a model level > default value. To create an event, expecting people to subscribe to > the event later, it is not the "natural state" of the Event object to > have a start_time = Time.now. However, it is convenient to initialize > the form''s control to default to the current date and time to make the > user''s selection process easier. > > It just happens that the built-in time widgets do this (I think so > anyway). But in any case the initial state of the model object''s > start_time attribute to be nil and not Time.now. > > So assuming that this behavior is not built into the form''s control > object Rails provides a convenient way to construct a new object with > some attributes initialized that can be set from with the controller > like: > > # GET /people/new > def new > @person = User.new(access_level => 1) > end > > Maybe some purist MVC guys would say that is breaking the MVC design > pattern, and maybe it is, but sometimes the convenience is so great > that it''s worth bending the rules a bit. However, this is similar to > many "factory constructor" design patterns that I often see in use. > > It would be more "pure" if the form''s control were extended to provide > a default option. > > Something like: > <p> > <b>Access Level</b><br /> > <%= f.text_field :access_level, :options => {:default => 1 }%> # > DON''T do this it''s just made up to illustrate a point > </p> > > > > >--~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---
> def text > self[:text] or "default text" > end > don''t appear to be called by the form builders? If @text isn''t used by > ActiveRecord, I''d have thought the first one would at least return the > default text, and if ActiveRecord does use a hash, then I''d have thought > the second one does work...I think the form builders use the "attribute_before_type_cast" accessor methods. Actually I tried submitting a patch for it: http:// dev.rubyonrails.org/ticket/5427> self.text="default text from init" unless self.text > self.color="#999999" unless self.color > endJust a guess, maybe self.text.empty? Best regards, Andrew --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---
Andrew France wrote:>> def text >> self[:text] or "default text" >> end >> don''t appear to be called by the form builders? If @text isn''t used by >> ActiveRecord, I''d have thought the first one would at least return the >> default text, and if ActiveRecord does use a hash, then I''d have thought >> the second one does work... > > I think the form builders use the "attribute_before_type_cast" > accessor methods. Actually I tried submitting a patch for it: http:// > dev.rubyonrails.org/ticket/5427Ah, that''s interesting. I wonder why they would do that?>> self.text="default text from init" unless self.text >> self.color="#999999" unless self.color >> end > Just a guess, maybe self.text.empty?That did the trick. Thanks! I did try Robert''s idea of changing the attribute name , but it didn''t work in this case. Thanks for all the contributions above from everyone, I''ve got lots to think about now :) -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---
Mark Coleman wrote: Dude, don''t feel bad. As a noob last March coming from J2EE-land, I, like you, foolishly thought that ActiveRecord::Base descendants behaved like _normal_ objects. But, alas, they don''t. And as far as I can tell, we all go through the same fire to discover these facts... To summarize for you, and hopefully, the next guy who runs into this: 1)> So, the most obvious place, I thought, was the initialize methods: > link.rb: > def initialize > super > @color="#999999" > @text="default text" > endYou know the answer to this is because you need the (params = nil). So: def initialize(params = nil) super self.color = "#999999" self.text = "default text" end DO not that the reason that the above method works is that initialize only get called on an ActiveRecord object when "new" is invoked, but never after it is saved (on a "find"). The point being that AR::Base descendants are not instantiated like _normal_ Ruby objects (which always call new and hence initialize). But wait, there''s more - the _recommended_ approach to handling attribute initialization is to use "after_initialize". But wait, "after_initialize" gets called after a Obj.new _and_ after a Obj.find, so you have to check and see if it''s a new object so you know when to initialize it (note the if new_record? statement to guard against re-initializing an already saved object): def after_initialize if self.new_record? self.color = "#999999" self.text = "default text" end end But wait, there''s another wrinkle - if you ever _clone_ this object, then after_initialize gets called on the clone target and thus your initialization would overwrite any attribute values in the original object (see Rails trac ticket: http://dev.rubyonrails.org/ticket/7191), so the best way to handle this is: def after_initialize if self.new_record? self.color ||= "#999999" self.text ||= "default text" end end Crazy, huh? 2)> My next effort was to add my own reader methods: > link.rb: > def color > @color || "#999999" > end > > def text > @text || "default text" > end >Form helpers consult the attribute hash in the AR::Base object (@attributes) _directly_, bypassing any overridden getters. The reasoning is that if your overridden getter coerces your attribute value (from the hash) to another type, then it would appear that a text field that you entered as a string would "disappear", and be replaced with whatever the overridden getter returned after doing something to the attribute from the attribute hash. I think an example would be a text field that represents an integer, and the getter method does a to_i on the value from the attribute hash. If you type text into that field, it would turn from "blah" to "0" on a re-render of the page. I don''t 100% get it (maybe 88%), but all I know is that bypassing the getter method is confusing for those of us who expect the ability to overload methods with ease ;). Just one of those things. 3)> OK, so IIRC, my next attempt was using something along the lines of > <p>Text: <%= f.text_field :text, :value=>"default text" %></p> > Which looked promising until I wanted to use the same _partial files for > both the edit and create methods... in which case, "default text" shows > up even when editing an object that already has a value for that > attribute.This behavior makes sense, since the approach above is just hard-coding "default text" as a value. As you now probably know, you would have to lose the :value and just make sure that the field "text" was initialized properly. 4)> After much googling, I came across another idea. For some reason not > clear to me, this should work: > link.rb: > def text > self[:text] or "default text" > endSame as #2, this method is not consulted when the page renders.> But how do I get a default value for the Uri? > I try adding this line to my "new" method: > @link.uri = Uri.new(:loc=>''www.example.com/example.html'') > > It doesn''t work. At this point, I''m all out of ideas, and this is where > I decided that my understanding just has a big whole in it that needs > filling, so I''ve written this long post!Read through all of the API docs. for ActiveRecord::Associations::ClassMethods and this will clear up a lot of confusion about how to properly create/relate association objects. I believe that either @link.build_uri = Uri.new(:loc => ...) OR @link.uris.build(:loc => ...) will work in this case. Wes -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---
Mild corrections - it''s late: Wes Gamble wrote:> DO note that the reason that the above method works is that initialize > only gets called on an ActiveRecord object once when you invoke new, but > never after it is saved (on a "find"). A find causes an "allocate" followed by some internal AR::Base initialization. A find does not create an object via "new".-- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---
Wes Gamble wrote:> To summarize for you, and hopefully, the next guy who runs into this: > ... <plus lots more> ...Brilliant! Thanks for posting this, it''s helped me lots! -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---
Great post... dragging this one to my "saved" folder. :-) b Wes Gamble wrote:> Mark Coleman wrote: > > Dude, don''t feel bad. As a noob last March coming from J2EE-land, I, > like you, foolishly thought that ActiveRecord::Base descendants behaved > like _normal_ objects. But, alas, they don''t. And as far as I can > tell, we all go through the same fire to discover these facts... > > To summarize for you, and hopefully, the next guy who runs into this: > > 1) >> So, the most obvious place, I thought, was the initialize methods: >> link.rb: >> def initialize >> super >> @color="#999999" >> @text="default text" >> end > > You know the answer to this is because you need the (params = nil). So: > > def initialize(params = nil) > super > self.color = "#999999" > self.text = "default text" > end > > DO not that the reason that the above method works is that initialize > only get called on an ActiveRecord object when "new" is invoked, but > never after it is saved (on a "find"). The point being that AR::Base > descendants are not instantiated like _normal_ Ruby objects (which > always call new and hence initialize). > > But wait, there''s more - the _recommended_ approach to handling > attribute initialization is to use "after_initialize". But wait, > "after_initialize" gets called after a Obj.new _and_ after a Obj.find, > so you have to check and see if it''s a new object so you know when to > initialize it (note the if new_record? statement to guard against > re-initializing an already saved object): > > def after_initialize > if self.new_record? > self.color = "#999999" > self.text = "default text" > end > end > > But wait, there''s another wrinkle - if you ever _clone_ this object, > then after_initialize gets called on the clone target and thus your > initialization would overwrite any attribute values in the original > object (see Rails trac ticket: http://dev.rubyonrails.org/ticket/7191), > so the best way to handle this is: > > def after_initialize > if self.new_record? > self.color ||= "#999999" > self.text ||= "default text" > end > end > > Crazy, huh? > > 2) >> My next effort was to add my own reader methods: >> link.rb: >> def color >> @color || "#999999" >> end >> >> def text >> @text || "default text" >> end >> > > Form helpers consult the attribute hash in the AR::Base object > (@attributes) _directly_, bypassing any overridden getters. The > reasoning is that if your overridden getter coerces your attribute value > (from the hash) to another type, then it would appear that a text field > that you entered as a string would "disappear", and be replaced with > whatever the overridden getter returned after doing something to the > attribute from the attribute hash. I think an example would be a text > field that represents an integer, and the getter method does a to_i on > the value from the attribute hash. If you type text into that field, it > would turn from "blah" to "0" on a re-render of the page. I don''t 100% > get it (maybe 88%), but all I know is that bypassing the getter method > is confusing for those of us who expect the ability to overload methods > with ease ;). Just one of those things. > > 3) >> OK, so IIRC, my next attempt was using something along the lines of >> <p>Text: <%= f.text_field :text, :value=>"default text" %></p> >> Which looked promising until I wanted to use the same _partial files for >> both the edit and create methods... in which case, "default text" shows >> up even when editing an object that already has a value for that >> attribute. > > This behavior makes sense, since the approach above is just hard-coding > "default text" as a value. As you now probably know, you would have to > lose the :value and just make sure that the field "text" was initialized > properly. > > 4) >> After much googling, I came across another idea. For some reason not >> clear to me, this should work: >> link.rb: >> def text >> self[:text] or "default text" >> end > > Same as #2, this method is not consulted when the page renders. > >> But how do I get a default value for the Uri? >> I try adding this line to my "new" method: >> @link.uri = Uri.new(:loc=>''www.example.com/example.html'') >> >> It doesn''t work. At this point, I''m all out of ideas, and this is where >> I decided that my understanding just has a big whole in it that needs >> filling, so I''ve written this long post! > > Read through all of the API docs. for > ActiveRecord::Associations::ClassMethods > and this will clear up a lot of confusion about how to properly > create/relate association objects. > > I believe that either > @link.build_uri = Uri.new(:loc => ...) > > OR > > @link.uris.build(:loc => ...) > > will work in this case. > > Wes >--~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---
Wes Gamble wrote:> Form helpers consult the attribute hash in the AR::Base object > (@attributes) _directly_, bypassing any overridden getters. The > reasoning is that if your overridden getter coerces your attribute value > (from the hash) to another type, then it would appear that a text field > that you entered as a string would "disappear", and be replaced with > whatever the overridden getter returned after doing something to the > attribute from the attribute hash. I think an example would be a text > field that represents an integer, and the getter method does a to_i on > the value from the attribute hash. If you type text into that field, it > would turn from "blah" to "0" on a re-render of the page. I don''t 100% > get it (maybe 88%), but all I know is that bypassing the getter method > is confusing for those of us who expect the ability to overload methods > with ease ;). Just one of those things.I misspoke here. In fact, it''s not concern about _overridden_ getters but just about the attribute getters (readers) in general. The data type for a given column in the table may not be String, and a cast is performed from String (in the attributes hash) into the appropriate column type when an attribute is set. If the cast would coerce an inappropriate different value (think String -> Integer becomes 0 if the string value is not composed of digits) for the attribute than what was attempted to be set (what was entered into a form), and if you pull the attribute value through the reader, then you might get unexpected attribute values back when submitting an incorrect form entry. So that''s why the reader methods are not consulted to fill in form values. It''s because the attributes hash is basically used as a "form model" object to mediate between a form and the database. You could argue that such a "form model" object should be exposed as it''s own object and data should be marshalled/unmarshalled between it and the AR object, but that kind of interaction is hidden _inside_ the AR object. Wes -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---
Thanks for digging into these details,
I have a similar situation, but I just hacked an rhtml to set defaults
when creating the new objects. I quickly refactored after reading
through this, but I''m left with a couple of questions.
First,
when I write...
def after_initialize
if self.new_record?
self.password ||= "password"
end
end
I get blanks when render rhtml value @user.password.
However, if I write...
def after_initialize
if self.new_record?
self.password = "password" if self.password.blank?
end
end
I get expected, pre-filled values in the view results. Curious why that
would be the case. Is Rails evaluating the ||= differently? Is it
defaulting the ruby behavior of evaluations?
Also, why is after_initialize recommended to overriding the constructor?
Both methods worked for me, although I did refactor from the overriding
constructor to the after_initialize - I do like best practices.
Is it preferred due to the cloning issue?
Regards,
/ak
Wes Gamble schrieb:> Mark Coleman wrote:
>
> Dude, don''t feel bad. As a noob last March coming from J2EE-land,
I,
> like you, foolishly thought that ActiveRecord::Base descendants behaved
> like _normal_ objects. But, alas, they don''t. And as far as I
can
> tell, we all go through the same fire to discover these facts...
>
> To summarize for you, and hopefully, the next guy who runs into this:
>
> 1)
>> So, the most obvious place, I thought, was the initialize methods:
>> link.rb:
>> def initialize
>> super
>> @color="#999999"
>> @text="default text"
>> end
>
> You know the answer to this is because you need the (params = nil). So:
>
> def initialize(params = nil)
> super
> self.color = "#999999"
> self.text = "default text"
> end
>
> DO not that the reason that the above method works is that initialize
> only get called on an ActiveRecord object when "new" is invoked,
but
> never after it is saved (on a "find"). The point being that
AR::Base
> descendants are not instantiated like _normal_ Ruby objects (which
> always call new and hence initialize).
>
> But wait, there''s more - the _recommended_ approach to handling
> attribute initialization is to use "after_initialize". But wait,
> "after_initialize" gets called after a Obj.new _and_ after a
Obj.find,
> so you have to check and see if it''s a new object so you know when
to
> initialize it (note the if new_record? statement to guard against
> re-initializing an already saved object):
>
> def after_initialize
> if self.new_record?
> self.color = "#999999"
> self.text = "default text"
> end
> end
>
> But wait, there''s another wrinkle - if you ever _clone_ this
object,
> then after_initialize gets called on the clone target and thus your
> initialization would overwrite any attribute values in the original
> object (see Rails trac ticket: http://dev.rubyonrails.org/ticket/7191),
> so the best way to handle this is:
>
> def after_initialize
> if self.new_record?
> self.color ||= "#999999"
> self.text ||= "default text"
> end
> end
>
> Crazy, huh?
>
> 2)
>> My next effort was to add my own reader methods:
>> link.rb:
>> def color
>> @color || "#999999"
>> end
>>
>> def text
>> @text || "default text"
>> end
>>
>
> Form helpers consult the attribute hash in the AR::Base object
> (@attributes) _directly_, bypassing any overridden getters. The
> reasoning is that if your overridden getter coerces your attribute value
> (from the hash) to another type, then it would appear that a text field
> that you entered as a string would "disappear", and be replaced
with
> whatever the overridden getter returned after doing something to the
> attribute from the attribute hash. I think an example would be a text
> field that represents an integer, and the getter method does a to_i on
> the value from the attribute hash. If you type text into that field, it
> would turn from "blah" to "0" on a re-render of the
page. I don''t 100%
> get it (maybe 88%), but all I know is that bypassing the getter method
> is confusing for those of us who expect the ability to overload methods
> with ease ;). Just one of those things.
>
> 3)
>> OK, so IIRC, my next attempt was using something along the lines of
>> <p>Text: <%= f.text_field :text, :value=>"default
text" %></p>
>> Which looked promising until I wanted to use the same _partial files
for
>> both the edit and create methods... in which case, "default
text" shows
>> up even when editing an object that already has a value for that
>> attribute.
>
> This behavior makes sense, since the approach above is just hard-coding
> "default text" as a value. As you now probably know, you would
have to
> lose the :value and just make sure that the field "text" was
initialized
> properly.
>
> 4)
>> After much googling, I came across another idea. For some reason not
>> clear to me, this should work:
>> link.rb:
>> def text
>> self[:text] or "default text"
>> end
>
> Same as #2, this method is not consulted when the page renders.
>
>> But how do I get a default value for the Uri?
>> I try adding this line to my "new" method:
>> @link.uri =
Uri.new(:loc=>''www.example.com/example.html'')
>>
>> It doesn''t work. At this point, I''m all out of ideas,
and this is where
>> I decided that my understanding just has a big whole in it that needs
>> filling, so I''ve written this long post!
>
> Read through all of the API docs. for
> ActiveRecord::Associations::ClassMethods
> and this will clear up a lot of confusion about how to properly
> create/relate association objects.
>
> I believe that either
> @link.build_uri = Uri.new(:loc => ...)
>
> OR
>
> @link.uris.build(:loc => ...)
>
> will work in this case.
>
> Wes
>
--~--~---------~--~----~------------~-------~--~----~
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
-~----------~----~----~----~------~----~------~--~---
Andy Koch wrote:> Thanks for digging into these details, > > I have a similar situation, but I just hacked an rhtml to set defaults > when creating the new objects. I quickly refactored after reading > through this, but I''m left with a couple of questions. > > First, > > when I write... > > def after_initialize > if self.new_record? > self.password ||= "password" > end > end > > I get blanks when render rhtml value @user.password. > > However, if I write... > > def after_initialize > if self.new_record? > self.password = "password" if self.password.blank? > end > end > > I get expected, pre-filled values in the view results. Curious why that > would be the case. Is Rails evaluating the ||= differently? Is it > defaulting the ruby behavior of evaluations?My guess is that password is defaulted to be an empty string in your DB or somehow so that when you do self.password ||= "password" self.password has a value that happens to be an empty string. If you use script/console to do an interactive command line session in the context of your app., create a new one of these objects and see what the password attribute is. I bet it''s an empty string.> Also, why is after_initialize recommended to overriding the constructor?(someone please correct me on this if I make a mistake, thanks) ActiveRecord doesn''t instantiate objects in the normal "Ruby" way. In a regular Ruby object, when "new" is called, this causes a call to "allocate" followed by the call to "initialize," which is what you would normally override. In AR::Base, "new" is never called directly. Instead, "allocate" is called, and then custom initialization is performed inside of ActiveRecord. The only time that an "initialize" method will be called on an AR::Base descendant is when you do AR_obj.new, never after that, once it''s saved, for example. So, to make a long story short, after_initialize is a hook that AR::Base gives you to allow for behavior that you would normally probably put in an initialize method, but can''t since AR::Base doesn''t invoke it most of the time.> Is it preferred due to the cloning issue?The ||= inside of the new_record? block is necessary to handle cloning, since currently, the cloning occurs as part of the instantiation of the clone, and then after_initialize is called (as usual). If you do straight assignment in after_initialize, you would re-initialize those attributes in your new, cloned, object. And chances are, since you cloned it in the first place, you probably cloned it from an object that has had state change from initialization significantly. ---------------------- I started using after_initialize after slogging through the AR::Base code and seeing that "normal" initialization doesn''t usually occur, so that I wouldn''t be lulled into the idea that AR::Base descendants actually behave like regular Ruby objects. And who knows how the custom initialization sequence will change in future releases of ActiveRecord, so I figured I''d better get on board :). Wes -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---