Sunday, September 7, 2008

Rails Migration Data Type


Basic data types:


:integer
:float
:datetime
:date
:timestamp
:time
:text
:string
:binary
:boolean


Valid column options:


:limit
:null (:null => false implies NOT NULL)
:default (to specify default values)

Thursday, August 21, 2008

A new project started.



I started a new project on Aug. 20, 2008.

The project is called resume generator which will automatically generate a resume based on your input data and export in various formats like pdf, txt, xml, etc.

The project is located at http://xianese.unfuddle.com

Database schema has been done. Here is the diagram.

Friday, August 1, 2008

The two purposes of Rails Routing


Rails Routing is defined in ./config/route.rb. Routings are actually a list of rules.

Routing has two purposes.


  • Recognize URL: What to do when an URL like
    http://localhost:3000/myrecipes/apples
    is coming.

  • Generate URL: What to create when Rails command like

    link_to :controller=>'recipes',
    :action=>'show',
    :ingredient=>'apple'

    is defined.




map.connect "myrecipes/:ingredient",
:controller=>"recipes",
:action=>"show"



The static text is myrecipes.

The wildcard receptor is :ingredient which you can get by
params[:ingredient]

Wednesday, July 30, 2008

Plugin for Login and Authentication


The RESTful login plugin from Rick Olson is a handful method to implement a user login mechanism.

Some links below for future reference.

RESTful Authentication

To install this plugin, use command

ruby script/plugin install 
http://svn.techno-weenie.net/projects/plugins/restful_authentication/


For single id sign on, use OpenID authentication

OpenID Authentication

Saturday, July 26, 2008

Ruby's block


Ruby's block always make a developer like me who is being with typed language for long time confused.

In Ruby, there are some methods called Iterators like times, each, map, upto and downto and some objects called Enumerable objects like array, hash and range which contain many Iterator methods. The common feature of these methods is that they are all interacted with some custom codes.

A group of these codes for a method is called "a block".

For example:

[1,2,3].map {|x| x*x}


  • [1,2,3] is an Enumerable object(an Array)

  • "map" is an Iterator(method) which will transverse all elements in the Array

  • {|x| x*x} is a block provide by user for callback from "map" method

  • a "yield" method is provided in "map" to call block code




So, block is a group of custom codes waiting for Iterator method to call. Each time when a "yield" is met, block is called.

Here is a very simple example about an Iterator method call "twice".


def twice
yield
yield
end




When you call this iterator, it just yield twice that means the caller's block will be invoked twice.


twice {puts "hello"}

output: hello hello.


Of course you can pass value from Iterator method to yield block.


def sequence(n, m, c)
i = 0
while(i < n) # Loop n times
#Invoke the block and pass a value to it
yield m*i + c
i += 1 # Increment i each time
end
end

The Iterator method is invoked by:
sequence(3, 5, 1) {|y| puts y }



The passing block parameter ({|y| puts y}) can be explicitly declared in the Iterator. Once it is declared, "call" method is used to invoke the block instead of "yield".


def sequence(n, m, c, &b)
i = 0
while(i < n) # Loop n times
#Invoke the block and pass a value to it
b.call(m*i + c)
i += 1 # Increment i each time
end
end

The Iterator method is invoked by:
sequence(3, 5, 1) {|y| puts y }
# Note that the block is still passed outside of the parentheses

Thursday, June 26, 2008

Render a collection


Very often we want to list a collection of data in front HTML page.

Let's say we have an animal collection. Each record of this collection contains animal Specie, Age, Weight and Risk. As you can imagine, the list is retrieved by

animal_controller.rb

Class AnimalController < ApplicationController
...
def show
@animals = Animal.find :all
end
...
end


Now, how shuold we render the collection?

Usually we use 'for each' against the collection to render the table:

show.html.erb

<table>
<tr><td>Specie</td>
<td>Age</td>
<td>Weight</td>
<td>Risk</td>
</tr>
<tr>
<% for each @animals do |animal| %>
<td><%= h(animal.specie) %></td>
<td><%= h(animal.age) %></td>
<td><%= h(animal.weight) %></td>
<td><%= h(animal.risk) %></td>
<% end %>
</tr>
</table>


But there is a better solution with a bonus feature, a counter of records. Once you are used to this solution, you most likely will not turn back.

The solution is to use partial with :collection function and key is NAMING CONVENTION.

The collection partial is passed in by this way:

show.html.erb

<table>
<tr><td>Specie</td>
<td>Age</td>
<td>Weight</td>
<td>Risk</td>
</tr>
<% render :partial => 'animal', :collection => @animals %>
</table>


Partial's name 'animal' will be passed into partial as a varible. It represents one record in the collection. So, 'animal' can be used to reference one record in the collection.

Here is the partial:

_animal.html.erb

<tr>
<td><%= h(animal.specie) %></td>
<td><%= h(animal.age) %></td>
<td><%= h(animal.weight) %></td>
<td><%= h(animal.risk) %></td>
</tr>


You can also add a new column to count the rows. There is a 0 based counter avaiable in partial. By naming convention, the counter variable is called animal_counter (partial-name_counter).

_animal.html.erb

<tr>
<td><%= animal_counter %></td>
...
</tr>


Don't forget using h(...) function to sanitize your passed in variable.

Thursday, June 19, 2008

Now I can access the WEBrick server

As you know you can access a Rails project via browser by calling
 http://localhost:3000/.../... 
But when I try to access this project from other computers by calling
http://[computer_name]:3000/.../...

or
http://[ip_address]:3000/.../...
, IE or FireFox just can't reach the web page.

This problem bothered me for a while. I read some articles about how WEBrick works but did find how to make visible to other computers.

Today I have to ask helps from Rails community and finally found the solution. There is nothing to with how to config the WEBrick. It is simple as this:


Ruby script/server -b [ip_address] -p [port]


Now I can access the pages from other computers. Pretty happy.

Thursday, June 12, 2008

Polymorphic Raccoon


Yesterday night, my mom was terribly scared by a raccoon. She said it made a funny face when she was watching TV.

This brought a question to me. Is raccoon offensive? If not, why was my mom scared?

So, if I create an label and attach it to anything (I mean anything, not just animals) which are offensive, my mom should NOT be scared.

Since the label is going to be attached to ANYTHING, it brings me an association dilemma. If a Offend label is attached to Raccoon class, an association from Raccoon to Offend is established. If another Offend label is attached to Dog class, another association to Offend is also established. So, Offend must hold both 'foreign keys' to Man and Dog. But each record of Offend class always has one foreign key with Null value. For example, if Offend record 1 points to Raccoon, the foreign key to Dog must be Null. Vise versa.

In Ruby On Rails, 'polymorphic has_many association' can solve the problem. 'polymorphic has_many association' hooks a particular class to any kind and number of sources classes. These source classes may not be related.

First, let's define an INTERFACE called offensive (you can use any name) and declare it is polymorphic.

Second, let's create a Offend class which will be attached as a label to other classes. Then, we make Offend class belong to the INTERFACE.

offend.rb
class Offend < ActiveRecord::BASE
belongs_to :offensive, :polymorphic => true
end


Third, create two columns in table offends: offensive_id and offensive_type

002_create_offends.rb
class CreateOffends < AcriveRecord::Migration
self.up
create_table :offends do |t|
t.column :offensive_level, :integer
t.column :offensive_id, :integer
t.column :offensive_type, :string
t.timestamps
end

self.down
drop_table :offends
end
end


In Rails 2.0+, a new macro style method 'references' is introduced to automatically create the INTERFACE's id and type.

002_create_offends.rb
class CreateOffends < AcriveRecord::Migration
self.up
create_table :offends do |t|
t.column :offensive_level, :integer
t.references :offensive, polymorphic => true
t.timestamps
end

self.down
drop_table :offends
end
end


But I found only offensive_id was generated, not offensive_type. The INTERFACES's type field is used to hold the classes' type of which the Offend class will be attached to, such as Raccoon or Dog. So more research on 'references' method is needed in the future.

Now we are ready to attach this class to any source classes.

First, simply declare has_many offends via offensive INTERFACE.
dog.rb
class Dog < ActiveRecord::BASE
has_many :offends, :as => :offensive
...
end

raccoon.rb
class Raccoon < ActiveRecord::BASE
has_many :offends, :as => :offensive
...
end


Second, create a Offend object and update its :offensive attribute by passing the source objects (like instance of Dog or Raccoon) in.


>>d = Dog.find :first
>>r = Raccoon.find :first
>>o1 = Offend.new offensive_level => 3
>>o2 = Offend.new offensive_level => 5
>>o1.update_attribute(:offensive, d)
>>o2.update_attribute(:offensive, r)



Notice, you can't do o1.save. It won't work. o1.update_attribute(:offensive, d) will automatically save the newly created o1 into database and set o1.offensive_type to Dog.


Now we can see, by attaching the class Offend, Raccoon is more dangerous then Dog.

Wednesday, June 11, 2008

I am a VIP


A problem I found is how Model class inheritance reflects in table relationship. When I saw the Single-Table Inheritance (STI) introduced by Martin Fowler a couple of years ago, I thought it was really stupid.

The whole idea of STI is simple: include all super/sub classes' attributes in one big fat single table and use an extra column (may be called 'type') to distinguish different type of classes.

But the default rails way is STI. :(

Now I have a Model class User and want to have another Model class called VIPUser which is a subclass of User. VIPUser has a new attribute called vip_number which User doesn't have.

OK, let's do the magic.

First, create a VIPUser Model

ruby script/generate model VIPModel

exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/vip_user.rb
create test/unit/vip_user_test.rb
create test/fixtures/vip_users.yml
exists db/migrate
create db/migrate/008_create_vip_users.rb


Second, edit the migration script for VIPUser

008_create_vip_users.rb
class CreateVipUser < ActiveRecord::Migration
def self.up
add_column :users, :vip_number, :integer, :default => 0
add_column :users, :type, :string
end

def self.down
remove_column :users, :vip_number
remove_column :users, :type
end
end


Notice that two new columns are created in the users table (which is for Model class User). One is the attribute of VIPUser, 'vip_number' and another is 'type' which is used to distinguish the type of classes.

If you've already had a column called 'type' or you don't like the name, you can use any other names and later declare it in VIPUser Model class.

vip_user.rb
set_inheritance_column 'object_type'


Third, make VIPUser a subclass of User

vip_user.rb
class VIPUser < User
...
end


That is it!

Now if you create a new VIPUser, you will see Rails automatically populate a string 'VIPUser' in type field.


>>VIPUser.new
VIPUser id: nil,
user_name: nil,
status: "active",
created_at: nil,
updated_at: nil,
vip_number: 0,
type: "VIPUser"

Monday, June 9, 2008

No, you can't delete accounts!

In many cases, bank managers ask you to keep an account information even it is closed or removed. Usually we keep the record in database but mark it by a flag column like 'delete_at' or 'status'.

To provent the false deletion, a method is registered to callback before_destroy.


class Account < ActiveRecord::BASE
...

protected

def before_destroy
update_attribute(:delete_at, Time.now) and return false
end
end


Notice that return false is very important. It halts the execution of the further deletion action.

Also notice that delete and delete_all ActiveRecord model methods will bypass the before_destroy callback methods.

for example:


>>account=Account.find 1
>>account.destroy #this will trigger the callback

>>account=Account.find 1
>>Account.delete(account) #this will not trigger the callback

Saturday, June 7, 2008

Hey yo! one class per kitty


Kitty students start to register courses. A student can have many courses and a course can contain many students. Students and courses are many to many relationship via registration.







class Kitty < ActiveRecord::BASE
has_many :registration
has_many :courses, :through => :registration
end

class Registration < ActiveRecord::BASE
belongs_to :kitty
belongs_to :course
end

class Course < ActiveRecord::BASE
has_many :registration
has_many :kitty, :though => :registration
end


Now, the problem is, how to make sure each kitty is not registered more once for a particular course.

To do this, use validates_uniqueness_of

Validate the uniqueness of a kitty student id (kitty_id) against a course_id:


class Registration < ActiveRecord::BASE
belongs_to :kitty
belongs_to :course

validates_uniqueness_of :kitty_id,
:scope => :course_id,
:message => "one kitty per course!"
end


Remember! when validating the uniqueness via has_many :through, use specific attribute name instead of model name.


validates_uniqueness_of :kitty_id,
:scope => :course_id,
:message => "one kitty per course!"


not


validates_uniqueness_of :kittt,
:scope => :course,
:message => "one kitty per course!"

Tuesday, June 3, 2008

You better accept it!

Time after time you will find all kind of term of services which ask you to click 'I Agree'

Well, you better agree. Otherwise, you will be kicked out!

Here is the rails way to enforce this:

Model side:


class User < ActiveRecord::BASE
validates_acceptance_of :term_of_service
...
attr_accessible :term_of_service
end


RHTML side:


<%form_for :user, @user, :url => users_path do f %>
<p>
... other controls ...
</p>
<p>
<%= f.check_box :terms_of_service %>
I accept the terms of service.
</p> <p>
<%= submit_tag "Save" %>
</p>
<% end %>

The :term_of_service is actually virtual. It has not direct value in database. ActiveRecord temperately hold it in memory.

Saturday, May 31, 2008

Give me :five

Yes, give me :five! Enough :first and :all, I want to find :five.

In order to do this, I checked the source code of find :first and find :all in base.rb

base.rb

def find(*args)
options = args.extract_options!
validate_find_options(options)
set_readonly_option!(options)

case args.first
when :first then find_initial(options)
when :all then find_every(options)
else find_from_ids(args, options)
end
end


OK, the next step is easy, override this method by defining a class method of a Model class, let's say, Blog class and add :five argument in.

Use class << self to open the Blog class object and add the find method in to override the original one. You have to implement :first and :all again. Otherwise the "Blog.find :all" will not work.

blogs.rb

class Blog < ActiveRecord::Base
has_many :posts

class << self
def find(*args)
options = args.extract_options!
validate_find_options(options)
set_readonly_option!(options)

case args.first
when :first then find_initial(options)
when :all then find_every(options)
when :five then find :first,
:limit => 1,
:offset => 4 #call :first instead
else find_from_ids(args, options)
end
end
end
end


Now, you can use find :five to retrive the fifth elements:


>>b5=Blog.find :five


Bingo, I got a :five!

Friday, May 30, 2008

The one I love most

If a Model class has a "has_many" relationship available, it can specify a "has_one" relationship pointing to one record for future convenient reference.

:conditions and other SQL options can be used to narrow the search.

Blog has many Post


class Blog < ActiveRecord::BASE
has_many :posts #has_many must be present
has_one :first_post, #no naming convention.
:class_name => 'Post', #must specify!
:order => 'created_at asc'
end

class Post < ActiveRecord::BASE
belongs_to :blog #one belongs_to is enough
end


Later, you can reference the first record easily by:

>>blog1 = Blog.find :first
>>blog1.first_post


Yes! this is the one I love most, give her some special treat.

To be singular or to be plural

ActiveRecord's relationship is sensitive to the plurality of the database table name.

Let's say, blogs contains many posts, then blogs should be on singular side and posts on plural side.


class Blog < ActiveRecord::BASE
has_many :posts #a plural declare
end


class Post < ActiveRecord::BASE
belongs_to :blog #a singular declare
end


For has_one and belongs_to relationship, it should be singular. Suppose each user has only one avatar:


class Avator < ActiveRecord::BASE
belongs_to :user #a singular declare
end


class User < ActiveRecord::BASE
has_one :avatar #a singular declare
end


If you don't follow the convention, it won't work.

Wednesday, May 28, 2008

Use YAML to load sample data into database

When reading the Active Record, I was wondering if I can create a rake to automatically populate some data into database.

After some research, I have the following code samples.

Suppose I have three tables: blogs, posts and comments.

And I have three prepared yaml files: blogs.yml, posts.yml and comments.yml. They are saved at /#{RAILS_ROOT}/lib/tasks/sample_data/

blogs.yml looks like:

B1000:
id: 1000
blog_name: "Xianese Photo"

B2000:
id: 2000
blog_name: "Mark's World"

B3000:
id: 3000
blog_name: "May's Beautiful World"




Then the rake file "sample_data.rake" is saved under
/#{RAILS_ROOT}/lib/tasks/.

I use TABLES array to hold the table names.

TABLES = %w-blogs comments posts-


From table names, I use Inflector's classify function to convert the table names to Model Class names and then use const_get function to get the ActiveRecord Class instance. You can't directly use a string name to refer to the Class instance. So this step is necessary.


path = DATA_DIRECTORY + table_name.to_s + '.yml'
model_name = Inflector.classify table_name
model_class = ActiveRecord.const_get(model_name)


Next step is easy, ust YAML.load_file to load each yml files:


data = YAML.load_file(path)


Notice that the data is actually a hash like "B1000" => <...>. The <...> is another hash with real data: blog_name => "Xianese's blog", etc. So you have to tranverse the hash then use model_class to create a new instance of the Model object and save it:


data = YAML.load_file(path)
data.each do |key, value|
model_instance = model_class.create(value)
model_instance.save
end


That is it! It is pretty simple.

Removing sample data is even simpler. I use ruby's eval method to pass in a class string name and delete all records for each Model.

Here is the final code:


require 'yaml'

namespace :db do
DATA_DIRECTORY = "#{RAILS_ROOT}/lib/tasks/sample_data/"

namespace :sample_data do
TABLES = %w-blogs comments posts-

desc 'Load the sample data'
task :load => :environment do |t|
TABLES.each do |table_name|
path = DATA_DIRECTORY + table_name.to_s + '.yml'
model_name = Inflector.classify table_name
model_class = ActiveRecord.const_get(model_name)

data = YAML.load_file(path)
data.each do |key, value|
model_instance = model_class.create(value)
model_instance.save
end

puts "Loaded data from #{table_name}.yml"
end
end

desc 'Remove the sample data'
task :remove => :environment do |t|
TABLES.each do |table_name|
model_name = Inflector.classify table_name
eval "#{model_name}.delete_all"
end
end

end
end



I hope this helps.

Happy blogging to myself!

I am going to use this blog to build my rails. A rails to sucess!