In a previous post, I talked about mass assignment. Well, there's something I discovered recently. If you have a polymorphic relationship, your model will have two columns: taggable_type and taggable_id. If you want to make these available for mass assignment, you cannot do:
attr_accessible taggable_type, taggable_id
Instead, RoR treats them like one attribute like so:
attr_accessible taggable
W
Showing posts with label ActiveRecord. Show all posts
Showing posts with label ActiveRecord. Show all posts
Sunday, September 28, 2008
Thursday, September 25, 2008
RoR: Mass Assignment Security
In RoR, it is possible to do something called mass assignments. For instance, when submitting a form, all the parameters are put into a params hash so you can do the following:
Product.create(params[:product])
product.update_attributes(params[:product])
Very simple, but by default, there is a security flaw. That flaw is covered in this post. If you are concerned about this, the easiest way would be to make it so that all your models will require attr_accessible on any attributes that you want mass assignment for. This then protects any attributes not listed by default.
In your config files, preferably in your initializers folder, add the following to protect all attributes:
ActiveRecord::Base.send(:attr_accessible, nil)
Then go through each model and think about which attributes should be exposed to mass assignments. For those that are not accessible, you must do the following to assign values:
product.store_id = store.id
This makes the assignment explicit and cannot be manipulated by the users.
Now that you know, go secure your applications!
W
Product.create(params[:product])
product.update_attributes(params[:product])
Very simple, but by default, there is a security flaw. That flaw is covered in this post. If you are concerned about this, the easiest way would be to make it so that all your models will require attr_accessible on any attributes that you want mass assignment for. This then protects any attributes not listed by default.
In your config files, preferably in your initializers folder, add the following to protect all attributes:
ActiveRecord::Base.send(:attr_accessible, nil)
Then go through each model and think about which attributes should be exposed to mass assignments. For those that are not accessible, you must do the following to assign values:
product.store_id = store.id
This makes the assignment explicit and cannot be manipulated by the users.
Now that you know, go secure your applications!
W
Wednesday, September 17, 2008
Database Visualization
Keeping documentation up-to-date is always a pain in the ass. I was recently working on updating our database diagrams and thought that there may be a way to automate the process.
I found 2 solutions:
1. XMI
2. RailRoad
For solution (1), it worked, but I had to tweak it slightly. Because we are using PostgreSQL, I had a problem when UmlDumper tried to execute current_database for the adapter. This works fine for the MySQL adapter, but no such function exists for the PostgreSQL adapter. I quickly hacked it and just replaced it with a general name for our database, as we didn't have to be dead-on here.
I was quite disappointed when Dia did not know how to open up the XMI file. So, searching around, I found a program called Umbrello. It works great. Unfortunately, I had all my UML classes, but no diagram! I had to drag and drop each one individually (I couldn't figure out how to drag them all at the same time T.T). I then manually added in all the lines and made it pretty. Not too bad, but it was partly automated and partly manual.
For solution (2), it worked great. I used a rake task to generate the svg files. I had several issues with this, which was the fact that RailRoad is used to diagram models in RoR, not the database. Hence, I ended up with a lot of relations that didn't exist in the database. This meant TONS of lines. The lines are also quite hard to follow as they constantly overlap and cross each other.
2 major issues I had was that there is a 1-to-1 relationship (for efficiency purposes). However, the diagram did not reflect this. For example, we have class A and B that are in a 1-to-1 relationship. A will have has_many relationships and so will B. However, the diagram showed that all of B's has_many relationships were related to A, not B. I couldn't figure out why.
The other issue was a logged bug. If the model ends in an 's', the diagram gets messed up. The diagram will automatically drop the trailing 's'. This created incorrect relationships.
For these 2 major issues, I was unable to use solution (2) in the end. However, if more improvements are made, I may give it another chance as the whole process was completely automated. If your interested, it also does Controller diagrams.
If anyone else has a better way of producing automated diagrams of their database or models, give me a shout. I'd love to hear other solutions.
W
I found 2 solutions:
1. XMI
2. RailRoad
For solution (1), it worked, but I had to tweak it slightly. Because we are using PostgreSQL, I had a problem when UmlDumper tried to execute current_database for the adapter. This works fine for the MySQL adapter, but no such function exists for the PostgreSQL adapter. I quickly hacked it and just replaced it with a general name for our database, as we didn't have to be dead-on here.
I was quite disappointed when Dia did not know how to open up the XMI file. So, searching around, I found a program called Umbrello. It works great. Unfortunately, I had all my UML classes, but no diagram! I had to drag and drop each one individually (I couldn't figure out how to drag them all at the same time T.T). I then manually added in all the lines and made it pretty. Not too bad, but it was partly automated and partly manual.
For solution (2), it worked great. I used a rake task to generate the svg files. I had several issues with this, which was the fact that RailRoad is used to diagram models in RoR, not the database. Hence, I ended up with a lot of relations that didn't exist in the database. This meant TONS of lines. The lines are also quite hard to follow as they constantly overlap and cross each other.
2 major issues I had was that there is a 1-to-1 relationship (for efficiency purposes). However, the diagram did not reflect this. For example, we have class A and B that are in a 1-to-1 relationship. A will have has_many relationships and so will B. However, the diagram showed that all of B's has_many relationships were related to A, not B. I couldn't figure out why.
The other issue was a logged bug. If the model ends in an 's', the diagram gets messed up. The diagram will automatically drop the trailing 's'. This created incorrect relationships.
For these 2 major issues, I was unable to use solution (2) in the end. However, if more improvements are made, I may give it another chance as the whole process was completely automated. If your interested, it also does Controller diagrams.
If anyone else has a better way of producing automated diagrams of their database or models, give me a shout. I'd love to hear other solutions.
W
Labels:
ActiveRecord,
Controllers,
Database,
Solutions,
W
Sunday, March 2, 2008
Mcolumn reference "id" is ambiguous
Today I tried to improve the performance of my action by doing just one db call through eager loading. For those who have no idea what it is, please read our previous post
http://twoblinddevs.blogspot.com/search?q=eager+loading
This is what I wanted to do
Person.find(:first, :include=>[ :person_info, :messages], :conditions=>["id = :user_id", { :user_id => session[:id]}] )
when I refreshed the browser, it shows me this error message - Mcolumn reference "id" is ambiguous.
The reason rails complain is because all three tables have an id column. In order to work around this problem, you simply have to change id = :user_id to people.id = :user_id
J
http://twoblinddevs.blogspot.com/search?q=eager+loading
This is what I wanted to do
Person.find(:first, :include=>[ :person_info, :messages], :conditions=>["id = :user_id", { :user_id => session[:id]}] )
when I refreshed the browser, it shows me this error message - Mcolumn reference "id" is ambiguous.
The reason rails complain is because all three tables have an id column. In order to work around this problem, you simply have to change id = :user_id to people.id = :user_id
J
Tuesday, February 12, 2008
attachment_fu - configurations
In our model, we'll use the has_attachment command to hook into attachment_fu.
example:
has_attachment :content_type => :image,
:storage => :file_system,
:max_size => 500.kilobytes,
:resize_to => '320x200>',
:thumbnails => { :thumb => '100x100>' }
:processor => 'Rmagick'
validates_as_attachment
Let's look at each option in detail
for storage, you have three options, :db_system (default), :file_system, or :S3. If you are using :db_system, attachment_fu will convert and save the image to your db as a BLOB (binary large object). If :file_system is used, the file will be saved to your hard disk. If :S3 is used, image will be saved to Amazon's S3 server.
:max_size lets you specific the maximum size of the photo, and there's also a :min_size which works the other way
:resize_to - resize your image to an acceptable width and height like facebook :) It takes a string called the Geometry String, which has the format:
< width > x < height > + - < x > + - < y > { % @ ! < > }
where any of the field can be omitted.
By default, width and height are the maximum value. If you enter something like '300x500' the image will expand or contract to fit the width and height value while maintaining the aspect ratio of the image.
If you want to enforce the image size to be exactly the size you specify, you can append an exclamation mark in the end like so: '300x500!'
You can also specify either the width '300' or the height 'x500' where the missing parameter will be chosen to maintain the aspect ratio
You can append % to specify percentage width and height ('110%' - increase, '90%' - decrease, '110%x90%' = increase width, decrease, height)
You can use @ to specify the maximum area in pixels of an image (I don't see how this is gonna be useful)
You can use < or > to change the dimensions of the image only if its width or height exceeds the geometry specification. < resizes the image only if both of its dimensions are less than the geometry specification. For example, if you specify '300x500>' and the image size is '250x250', the image size will not change. However if the image is '1000x1000', the it'll be resized to '300x300' and vice versa.
Finally x and y are offsets for width and height. + causes x and y to be measured from the left or top edges and - measures from the right or bottom edges. And they are always measured in pixels.
:thumbnails - a set of thumbnails to generate, specified by a has of filename suffixes and resizing options. You can omitted it if you don't want thumbnails. Generating multiple thumbnails will look something like this
:thumbnails => { :thumb_big => '500x500', :thumb_small => '100x100' }
:thumbnail_class - set what class to use for thumbnails (defaulted to whatever model you are in) However, you can generate a seperate model with seperate set of validations
:processor - 'ImageScience', 'Rmagick', or 'MiniMagick'. I like Rmagick cause it has the most features, but MiniMagick is less of a memory hog.
:path_prefix - Path to store the uploaded files, which defaults to public/your_table_name
If you are using S3 backend, it defaults to just your_table_name
validates_as_attachment does all the validations for you, so you have nothing to worry about :)
J
example:
has_attachment :content_type => :image,
:storage => :file_system,
:max_size => 500.kilobytes,
:resize_to => '320x200>',
:thumbnails => { :thumb => '100x100>' }
:processor => 'Rmagick'
validates_as_attachment
Let's look at each option in detail
for storage, you have three options, :db_system (default), :file_system, or :S3. If you are using :db_system, attachment_fu will convert and save the image to your db as a BLOB (binary large object). If :file_system is used, the file will be saved to your hard disk. If :S3 is used, image will be saved to Amazon's S3 server.
:max_size lets you specific the maximum size of the photo, and there's also a :min_size which works the other way
:resize_to - resize your image to an acceptable width and height like facebook :) It takes a string called the Geometry String, which has the format:
< width > x < height > + - < x > + - < y > { % @ ! < > }
where any of the field can be omitted.
By default, width and height are the maximum value. If you enter something like '300x500' the image will expand or contract to fit the width and height value while maintaining the aspect ratio of the image.
If you want to enforce the image size to be exactly the size you specify, you can append an exclamation mark in the end like so: '300x500!'
You can also specify either the width '300' or the height 'x500' where the missing parameter will be chosen to maintain the aspect ratio
You can append % to specify percentage width and height ('110%' - increase, '90%' - decrease, '110%x90%' = increase width, decrease, height)
You can use @ to specify the maximum area in pixels of an image (I don't see how this is gonna be useful)
You can use < or > to change the dimensions of the image only if its width or height exceeds the geometry specification. < resizes the image only if both of its dimensions are less than the geometry specification. For example, if you specify '300x500>' and the image size is '250x250', the image size will not change. However if the image is '1000x1000', the it'll be resized to '300x300' and vice versa.
Finally x and y are offsets for width and height. + causes x and y to be measured from the left or top edges and - measures from the right or bottom edges. And they are always measured in pixels.
:thumbnails - a set of thumbnails to generate, specified by a has of filename suffixes and resizing options. You can omitted it if you don't want thumbnails. Generating multiple thumbnails will look something like this
:thumbnails => { :thumb_big => '500x500', :thumb_small => '100x100' }
:thumbnail_class - set what class to use for thumbnails (defaulted to whatever model you are in) However, you can generate a seperate model with seperate set of validations
:processor - 'ImageScience', 'Rmagick', or 'MiniMagick'. I like Rmagick cause it has the most features, but MiniMagick is less of a memory hog.
:path_prefix - Path to store the uploaded files, which defaults to public/your_table_name
If you are using S3 backend, it defaults to just your_table_name
validates_as_attachment does all the validations for you, so you have nothing to worry about :)
J
attachment_fu - Installation and Setup
attachment_fu plugin is the complete package in handling images for your website. It handles image upload, resize, generate thumbnails, storing image info to database and saving the file to physical disk drive
to install attachment_fu, we type
~$ .script/plugin install http://svn.techno-weenie.net/projects/plugins/attachment_fu/
if you don't have imagemagick and rmagick installed
~$ sudo apt-get install imagemagick
~$ dpkg -l | grep magick
~$ sudo apt-get install libmagick9-dev
~$ sudo gem install rmagick -v=1.15.12
window users: you can go download the imagemagick executable and install rmagick by doing gem install rmagick -v=1.15.12
now let's generate a model to test it out
~$ .script/generate model photo
open up the migration file and enter the following
class CreatePhotos < ActiveRecord::Migration
def self.up create_table :photos do |t|
t.column :user_id, :integer
t.column :parent_id, :integer
t.column :content_type, :string
t.column :filename, :string
t.column :thumbnail, :string
t.column :size, :integer
t.column :width, :integer
t.column :height, :integer
end
end
def self.down
drop_table :photos end
end
:user_id - I'm assuming these photos belong to someone, you can replace it with another model_id depending on the relationship
:parent_id - don't touch, it's reserved for attachment_fu
:content_type - specify the content type of your data, default is image, but it can also be audio, or video the rest are pretty self explanatory, and we'll revisit these fields in a moment. now save your file, and do a quick rake db:migrate
J
to install attachment_fu, we type
~$ .script/plugin install http://svn.techno-weenie.net/projects/plugins/attachment_fu/
if you don't have imagemagick and rmagick installed
~$ sudo apt-get install imagemagick
~$ dpkg -l | grep magick
~$ sudo apt-get install libmagick9-dev
~$ sudo gem install rmagick -v=1.15.12
window users: you can go download the imagemagick executable and install rmagick by doing gem install rmagick -v=1.15.12
now let's generate a model to test it out
~$ .script/generate model photo
open up the migration file and enter the following
class CreatePhotos < ActiveRecord::Migration
def self.up create_table :photos do |t|
t.column :user_id, :integer
t.column :parent_id, :integer
t.column :content_type, :string
t.column :filename, :string
t.column :thumbnail, :string
t.column :size, :integer
t.column :width, :integer
t.column :height, :integer
end
end
def self.down
drop_table :photos end
end
:user_id - I'm assuming these photos belong to someone, you can replace it with another model_id depending on the relationship
:parent_id - don't touch, it's reserved for attachment_fu
:content_type - specify the content type of your data, default is image, but it can also be audio, or video the rest are pretty self explanatory, and we'll revisit these fields in a moment. now save your file, and do a quick rake db:migrate
J
Sunday, January 20, 2008
Customized Logger
Rails framework comes with a logger for you to log activities on your application, which is pretty handy. However, there's a problem. Your development.log or production.log (depends on which environment you are running) comes with many other informations which makes is very difficult to locate your own logs.
In order to solve this issue, we'll create a custom logger which will extend the original logger and save our own logs on a seperate file.
Open environment.rb (the reason we put it there it's because you want it available as soon as your server boots up, so you can call it whenever you need it)
go to the very bottom and add the following lines:
class CustomLogger <>
end
YOUR_CUSTOM_LOG = CustomLogger.new("/path/to/your/log/file")
and you use it just like a regular logger:
YOUR_CUSTOM_LOG.info "GOT HERE"
something worth noting is that the variable that holds the CustomLogger object must be
a global variable, or else you won't be able to access it in your controllers or models.
another thing is that make sure you create the log file ahead of time, your server won't start if the log file you specified does not exist.
when everything is set, restart your server and test it out!
J
In order to solve this issue, we'll create a custom logger which will extend the original logger and save our own logs on a seperate file.
Open environment.rb (the reason we put it there it's because you want it available as soon as your server boots up, so you can call it whenever you need it)
go to the very bottom and add the following lines:
class CustomLogger <>
end
YOUR_CUSTOM_LOG = CustomLogger.new("/path/to/your/log/file")
and you use it just like a regular logger:
YOUR_CUSTOM_LOG.info "GOT HERE"
something worth noting is that the variable that holds the CustomLogger object must be
a global variable, or else you won't be able to access it in your controllers or models.
another thing is that make sure you create the log file ahead of time, your server won't start if the log file you specified does not exist.
when everything is set, restart your server and test it out!
J
Labels:
ActiveRecord,
Controllers,
Debugging,
J,
Solutions
Sunday, January 13, 2008
ActiveForm
Suppose you're doing a search form. This search form can be using fields from many different ActiveRecord models and probably doesn't correspond to any table in your database.
What's nice about being an ActiveRecord model is that in the view, you can do wonderful things like a form_for helper or error_messages_for helper. Furthermore, ActiveRecord provides you a collection of wonderful validation functions.
However, without ActiveRecord, your solution would be to do it all by hand. How very sad T.T
This is where ActiveForm is a time saver! Basically, you create an ActiveForm model exactly the way you would an ActiveRecord model. It even supports validation! Furthermore, it is supported in the view, so all those helpers will still work! Just wonderful!
You can grab it as a plugin from here:
http://www.realityforge.org/svn/code/active-form/trunk
Enjoy!
W
What's nice about being an ActiveRecord model is that in the view, you can do wonderful things like a form_for helper or error_messages_for helper. Furthermore, ActiveRecord provides you a collection of wonderful validation functions.
However, without ActiveRecord, your solution would be to do it all by hand. How very sad T.T
This is where ActiveForm is a time saver! Basically, you create an ActiveForm model exactly the way you would an ActiveRecord model. It even supports validation! Furthermore, it is supported in the view, so all those helpers will still work! Just wonderful!
You can grab it as a plugin from here:
http://www.realityforge.org/svn/code/active-form/trunk
Enjoy!
W
Labels:
ActiveRecord,
Plugins,
Solutions,
Views,
W
Friday, January 11, 2008
Locking
Usually in an application with more than 1 user, there are several resources that will be shared. And whenever there are shared resources, there are usually race conditions. Most web applications are not exceptions. Shared resources are a very old set of problems that have been solved many times in many different areas, not only for computers.
Luckily, in RoR, it provides a way to handle shared resources: locking. Locking comes in 2 flavours: optimistic and pessimistic.
Usually, optimistic locking would be used for critical sections where race conditions are not expected to happen very often (due to the abortion of the transaction if a conflict occurs), whereas pessimistic locking would be used for critical sections where race conditions are frequent.
There is, however, a huge advantage that optimistic locking has over pessimistic locking, and that is scalability. The fact that pessimistic locking for update locks the row so that no read/update/destroy can be performed on that row until it is released means that users may have to wait indefinitely. Especially when race conditions are frequent, a long queue could develop with arbitrarily long waits.
Unfortunately, with optimistic locking, there is the problem of abortion and requiring the user to redo what they just did. This is obviously not very user-friendly, as the user expects their changes to be made the first time. If the UI is thought out carefully though, the impact of this may be minimal.
RoR, being superly fantastic, supports both! And both are very simple to implement.
With optimistic locking, all you have to do is add a column to your table you want to optimistically lock. Using migrations, it would look like this:
t.column :lock_version, :integer, :null => false, :default => 0
It is important that the column name is lock_version, and the default value is 0. Now, whenever an update occurs, the lock_version is compared to the one in the database. If there are any differences, then a StaleObjectError is raised and must be handled in any fashion in which you deem appropriate. More information about RoR's optimistic locking can be found here.
With pessimistic locking, all you do is lock it down when you do a find:
Person.find(:all, :lock => true)
This will pessimistically lock any rows returned. If you already have an object, you can use the method object#lock!:
person = Person.find(1)
person.lock!
More information about RoR's pessimistic locking can be found here.
Well, hopefully that was interesting to you. One more tip about locking is to watch out for deadlocks! Good luck!
W
Luckily, in RoR, it provides a way to handle shared resources: locking. Locking comes in 2 flavours: optimistic and pessimistic.
Usually, optimistic locking would be used for critical sections where race conditions are not expected to happen very often (due to the abortion of the transaction if a conflict occurs), whereas pessimistic locking would be used for critical sections where race conditions are frequent.
There is, however, a huge advantage that optimistic locking has over pessimistic locking, and that is scalability. The fact that pessimistic locking for update locks the row so that no read/update/destroy can be performed on that row until it is released means that users may have to wait indefinitely. Especially when race conditions are frequent, a long queue could develop with arbitrarily long waits.
Unfortunately, with optimistic locking, there is the problem of abortion and requiring the user to redo what they just did. This is obviously not very user-friendly, as the user expects their changes to be made the first time. If the UI is thought out carefully though, the impact of this may be minimal.
RoR, being superly fantastic, supports both! And both are very simple to implement.
With optimistic locking, all you have to do is add a column to your table you want to optimistically lock. Using migrations, it would look like this:
t.column :lock_version, :integer, :null => false, :default => 0
It is important that the column name is lock_version, and the default value is 0. Now, whenever an update occurs, the lock_version is compared to the one in the database. If there are any differences, then a StaleObjectError is raised and must be handled in any fashion in which you deem appropriate. More information about RoR's optimistic locking can be found here.
With pessimistic locking, all you do is lock it down when you do a find:
Person.find(:all, :lock => true)
This will pessimistically lock any rows returned. If you already have an object, you can use the method object#lock!:
person = Person.find(1)
person.lock!
More information about RoR's pessimistic locking can be found here.
Well, hopefully that was interesting to you. One more tip about locking is to watch out for deadlocks! Good luck!
W
Thursday, January 10, 2008
Eager Loading
There are many instances where you need to find an object, or several objects, then require their associated objects afterwards. In the simple case, this would require (1 + n) SQL queries: one for the object(s) and n for the associated object(s) for each object. To solve this problem, RoR allows you to do eager loading of associations when performing a find. This means that the (1 + n) SQL queries can be squashed into 1 and RoR handles the placing of the data correctly. Examples of eager loading can be found here (under Eager loading of associations).
I thought I would take this one step further. Using the example of Post, I thought I would have something like:
class Post
has_many :comments, :include => :author, :order => "comments.date ASC, authors.name ASC"
end
This would eager load the authors of each comment in the post, ordered by the date created and then by alphabetical order of name.
This works great when doing:
@comments = post.comments
and later on using @comments in the view to display.
However, I started to have major problems when I attempted a find on the collection such as:
comment = post.comments.find_by_author_id(author.id)
When executing such a command, I got a database error. The error reported that the authors table was not included in the FROM clause of the SQL query (due to the order by author.name ASC). I checked the query in the output console, and sure enough, there was no join to the authors table. I tried looking around for explanations, but ended up with none.
I finally gave up and removed the :include and :order from the has_many relationship and ended up doing this instead when I required eager loading and ordering:
@comments = post.comments.find(:all, :include => :author, :order => "comments.date ASC, authors.name ASC")
Hopefully, if anyone knows the explanation as to why the previous did not work, I would love to hear it. Otherwise, I hope this solution works for you.
W
I thought I would take this one step further. Using the example of Post, I thought I would have something like:
class Post
has_many :comments, :include => :author, :order => "comments.date ASC, authors.name ASC"
end
This would eager load the authors of each comment in the post, ordered by the date created and then by alphabetical order of name.
This works great when doing:
@comments = post.comments
and later on using @comments in the view to display.
However, I started to have major problems when I attempted a find on the collection such as:
comment = post.comments.find_by_author_id(author.id)
When executing such a command, I got a database error. The error reported that the authors table was not included in the FROM clause of the SQL query (due to the order by author.name ASC). I checked the query in the output console, and sure enough, there was no join to the authors table. I tried looking around for explanations, but ended up with none.
I finally gave up and removed the :include and :order from the has_many relationship and ended up doing this instead when I required eager loading and ordering:
@comments = post.comments.find(:all, :include => :author, :order => "comments.date ASC, authors.name ASC")
Hopefully, if anyone knows the explanation as to why the previous did not work, I would love to hear it. Otherwise, I hope this solution works for you.
W
Tuesday, November 6, 2007
Validation Caution
So I was implementing validation rules on a model the other day,
and the model started acting really weird afterward :(
Sometimes it'll save the object, most of the time it wont.
After a few attempts to locate the problem, I noticed
an object can be saved if the boolean attribute
is set to true, but won't save if it's set to false.
turns out this is what's causing all the troubles
validates_presence_of
don't use validate_presence_of to evaluate any boolean or numeric type variables,
because if you a value of 0, it'll be treated as nil
instead, this is what I changed it to
validates_inclusion_of attributes, :in => [true, false]
and life is wonderful once again
J
and the model started acting really weird afterward :(
Sometimes it'll save the object, most of the time it wont.
After a few attempts to locate the problem, I noticed
an object can be saved if the boolean attribute
is set to true, but won't save if it's set to false.
turns out this is what's causing all the troubles
validates_presence_of
don't use validate_presence_of to evaluate any boolean or numeric type variables,
because if you a value of 0, it'll be treated as nil
instead, this is what I changed it to
validates_inclusion_of attributes, :in => [true, false]
and life is wonderful once again
J
Wednesday, October 17, 2007
Models
RoR, being a model-view-controller architecture, obviously has something called a model. These things are just plain out awesome. They are objects that map directly to the database tables. Their fields are the database columns. And the best part is, the mapping is implicit... no massive amounts of XML mapping files! It's clean, it's intuitive, and best of all, less code to write (which translates to less time spent and less errors)!
Inside the class definition, fields don't need to be specified, as it is implicit that all database columns of the table are part of the model, and getters and setters are all implicit. This means no long list of getters and setters for each field! If there's something special that needs to be done for getters and setters, it's very convenient to just override the defaults.
So what is specified within the class definition then? Well, one of the things is table relations. For example, there are one-to-many relations. Imagine a Store has many Items.
In the Store class definition, you would add:
has_many :items
In the Item class definition, you would add:
belongs_to :store
These two lines specify the one-to-many relation between Store and Item. If you had a Store object and you wanted the items, it would be as simple as:
@items = @store.items
This will retrieve you all of the Items associated to the particular Store object.
There are all sorts of things you can do with relations, and this is only the tip of the iceberg.
Another wonderful thing you can do in models is validation! And RoR has a set list of validation functions that can be used for just about anything. Basically, before a model is saved, RoR runs a validation check on the fields. If any of it fails, the model is not saved, and error messages are created, which can then be displayed to the user. It's absolutely wonderful! Validation methods can be checked out here:
http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html
Well, that's the general idea of models. It's a very nice way to interface with the database while keeping code writing to a minimal. Enjoy!
W
Inside the class definition, fields don't need to be specified, as it is implicit that all database columns of the table are part of the model, and getters and setters are all implicit. This means no long list of getters and setters for each field! If there's something special that needs to be done for getters and setters, it's very convenient to just override the defaults.
So what is specified within the class definition then? Well, one of the things is table relations. For example, there are one-to-many relations. Imagine a Store has many Items.
In the Store class definition, you would add:
has_many :items
In the Item class definition, you would add:
belongs_to :store
These two lines specify the one-to-many relation between Store and Item. If you had a Store object and you wanted the items, it would be as simple as:
@items = @store.items
This will retrieve you all of the Items associated to the particular Store object.
There are all sorts of things you can do with relations, and this is only the tip of the iceberg.
Another wonderful thing you can do in models is validation! And RoR has a set list of validation functions that can be used for just about anything. Basically, before a model is saved, RoR runs a validation check on the fields. If any of it fails, the model is not saved, and error messages are created, which can then be displayed to the user. It's absolutely wonderful! Validation methods can be checked out here:
http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html
Well, that's the general idea of models. It's a very nice way to interface with the database while keeping code writing to a minimal. Enjoy!
W
Subscribe to:
Posts (Atom)