Sie sind auf Seite 1von 104

Topics

Part-1

Migrations

Active Record

Part -2

Associations

Validations

Callbacks

Part -3

Testing
Migrations
What is migration?

Migration allows you to define


changes to your database
schema
Why Migration?


iteratively improving schema

keep things synchronized
Auto generated migrations

The model and scaffold


generators will create migrations
appropriate for adding a new
model.
Creating a model

ruby script/generate model Product name:string description:text


my_app/db/migrate/001_create_products.rb
Migration generated
class CreateProducts < ActiveRecord::Migration
def self.up
create_table :products do |t|
t.string :name
t.text :description
t.timestamps
end
end

def self.down
drop_table :products
end
end
self.up / self.down
Creating a Standalone
Migration

ruby script/generate migration


AddPartNumberToProducts part_number:string
my_app/db/migrate/002_AddPartNumberToProducts.rb
Migration generated

class AddPartNumberToProducts < ActiveRecord::Migration


def self.up
add_column :products, :part_number, :string
end

def self.down
remove_column :products, :part_number
end
end
rake db:migrate
also invokes the db:schema:dump task
schema_migrations table
Other Transformations
Creating a table
def self.up
create_table :people do |t|
t.string :username, :null => false
t.string :fname,lname
t.text :notes
t.timestamps
end
end

def self.down
drop_table :people
end
More transformations

rename_column :people, :fname, :first_name

rename_table old_name, new_name

change_column table_name, column_name, type

remove_column table_name, column_name

add_index table_name, column_name

drop_table table_name
More...

change_table :products do |t|


t.remove :description, :name
t.string :part_number
t.index :part_number
t.rename :upccode, :upc_code
end
Rollback

rake db:rollback

rake db:rollback STEP=3

rake db:migrate VERSION=20080906120000


ActiveRecord
ActiveRecord is a
Module
ActiveRecord (note no space) could be classed as a module, since it's an implementation of the Active Record
design pattern. (* need to ask)
One Class Per Table

CREATE TABLE `people` (
`id` int(11) NOT NULL
auto_increment,
`login` varchar(255),
`email` varchar(255),
`password` varchar(40),
`created_at` datetime default NULL,
`updated_at` datetime default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
One Object / instance Per Row
Table columns map to Object
attributes
Some Key Points


mapping class names to table names.

pluralized table names.

integer primary keys.

classname_id as foreign keys.
Why Active Record


for simplicity and ease of use

hides low level implementation
The Basics

Select * from people where id='2' 
class Person < ActiveRecord::Base
limit='1'
end

person=Person.find(2)
find method

Examples:
User.find(23)
User.find(:first)
User.find(:all, :offset => 10, :limit => 10)
User.find(:first).articles
Find :select

list = Person.find(:all, :select => "fname, lname")


Find with :conditions

Student.find(:all, :conditions =>


[‘first_name = ? and status = ?’ ,‘mohit’, 1])

Why this ?
Find: Order By

Person.find(:all, :order => ‘updated_at DESC’)


SQL:
SELECT * FROM people ORDER BY created_at;
Find: Group By

Person.find(:all, :group => ‘designation’)


SQL:
SELECT * FROM people GROUP BY designation;
Find: Limit & Offset

Person.find(:all, :limit => 10, :offset => 0)


SQL:
SELECT * FROM people LIMIT 0, 10
Dynamic find methods

person = Person.find_by_fname("mohit")

all_mohit = Person.find_all_by_fname("mohit")

person =
Person.find_by_fname_and_lname("mohit",”jain”)
Creating a new record
user = User.new
user.name = "David"
user.occupation = "Code Artist“
user.save

OR

user =User.new(:name => "David", :occupation


=> "Code Artist")
user.save

OR

user =User.create(:name => "David",


:occupation => "Code Artist")
Update a record
person = Person.find(123)
person.update_attribute(:name, "Barney" )

OR

person= Person.find(123)
person.name = "mohit”
person.save

OR

person=Person.update(12, :name => "jigar" , :email


=> "jigar@vinsol.com" )

update_all and update_attribute bypass the validations.


Delete a record

Person.delete(123)

OR

person = Person.find_by_name("Mohit")
person.destroy
Named and Anonymous Scopes
Named scope

class Organization < ActiveRecord::Base


has_many :people
named_scope :active, :conditions => { :active => 'Yes' }
end

class Person < ActiveRecord::Base


belongs_to :organization
end
Usage

Organization.active

Organization.active.people
Named scope example 2

class Order < ActiveRecord::Base


named_scope :last_n_days, lambda { |days| :condition =>
['updated < ?' , days] }
named_scope :checks, :conditions => {:pay_type => :check}
end

orders = Orders.last_n_days(7)
orders = Orders.checks.last_n_days(7)
Anonymous scopes

in_house = Orders.scoped(:conditions => 'email LIKE


"%@pragprog.com"' )

in_house.checks.last_n_days(7)
Thank You.
Associations
Types of associations


belongs_to

has_one

has_many

has_many :through

has_one :through

has_and_belongs_to_many
Whats the need?
Comparison: without and with
class Customer < ActiveRecord::Base
end

class Order < ActiveRecord::Base


end

OR

class Customer < ActiveRecord::Base


has_many :orders, :dependent => :destroy
end

class Order < ActiveRecord::Base


belongs_to :customer
end
The belongs_to Association
The has_one Association
One to One
The has_many Association
One to Many
Its:

has_many :orders

and

has_one :order
has_many :through
has_and_belongs_to_many
has_and_belongs_to_many vs has_many :through

has_and_belongs_to_many

Stories can belong to many categories. Categories can have many stories.

Categories_Stories Table
story_id | category_id

has_many through:-- gives you a third model


Person can subscribe to many magazines. Magazines can have many
subscribers.

Subscriptions Table
person_id | magazine_id | subscription_type |
subscription_length | subscription_date
Many to many
has_one :through
has_one:through*

class Magazine < ActiveRecord::Base


has_many :subscriptions
end

class Subscription < ActiveRecord::Base


belongs_to :magazine
belongs_to :user
end

class User < ActiveRecord::Base


has_many :subscriptions
has_one :magazine, :through => : subscriptions, :conditions =>
['subscriptions.active = ?', true]
end
Polymorphic Associations
Self refrentional Joins

class Employee < ActiveRecord::Base


has_many :subordinates, :class_name => "Employee", :foreign_key =>
"manager_id"

belongs_to :manager, :class_name => "Employee"


end
When Things Get Saved

class Order < ActiveRecord::Base


has_one :invoice
end

class Invoice < ActiveRecord::Base


belongs_to :order
end

continue...
When things get saved (continue)

If you assign an object to a has_one/has_many


association in an existing object, that associated object
will be automatically saved.

order = Order.find(some_id)
an_invoice = Invoice.new(...)
order.invoice = an_invoice # invoice gets saved

continue...
When things get saved (continue)

If instead you assign a new object to a belongs_to


association, it will never be automatically saved.

order = Order.new(...)
an_invoice.order = order # Order will not be saved here
an_invoice.save # both the invoice and the order get saved
Validations
Validation Helpers

validates_acceptance_of

validates_confirmation_of

validates_length_of

validates_numericality_of

validates_presence_of
Example of validator helper

class Person < ActiveRecord::Base


validates_presence_of :name

validates_uniqueness_of :name,
:on => :create,
:message => "is already used"
end
When Does Validation Happen?

new_record?
instance method
Method triggers validations

* create
* create!
* save
* save!
* update
* update_attributes
* update_attributes!
Method skips validations

* update_all
* update_attribute
* update_counters
* save(false)
valid? and invalid?
Validation Errors

errors.add_to_base

errors.add

errors.clear

errors.size
Displaying Validation Errors

error_messages

error_messages_for
Showing error message

<% form_for(@product) do |f| %>


<%= f.error_messages %>
#-----
<% end %>
OR

<%= error_messages_for :product %>


Callbacks
What are callbacks

monitoring the process.


The life cycle of a model object

methods that get called at certain moments of an


object’s lifecycle
Active Record defines 20
callbacks.
18 call backs
Rest two callbacks

after_initialize and after_find


Define callbacks:- in two ways

1.Instance method
2.Handlers
callback as instance method

class class_name < ActiveRecord::Base


# ..
def before_save
# ..
end

end
Callbacks as handlers

class Order < ActiveRecord::Base

before_save:normalize_credit_card

protected

def normalize_credit_card
#......
end
end
Observers


Same as callbacks

It's about separation of concerns.

factor out code that doesn't really belong in
models
class OrderObserver < ActiveRecord::Observer
observe Order
end
Instantiating Observers

config.active_record.observers= :order_observer
( in environment.rb )
Thank you
Testing
Testing type

1. Unit
2. Functional
3. Integration
Unit testing?
What and why
rake db:test:prepare
copy the schema
rake test:units
1.copies the schema
2. runs all the tests in the test/unit
Test file?

require 'test_helper'

class ProductTest < ActiveSupport::TestCase


# Replace this with your real tests.
test "the truth" do
assert true
end
end
Running a test

ruby -I test test/unit/product_test.rb


Understanding with example of Product model
Product Model

validates_presence_of :title, :description, :image_url


validates_numericality_of :price
validate :price_must_be_at_least_a_cent
validates_uniqueness_of :title
validates_format_of :image_url,
:with => %r{\.(gif|jpg|png)$}i,
:message => 'must be a URL for GIF, JPG '
+ 'or PNG image.'
protected
def price_must_be_at_least_a_cent
errors.add(:price, 'should be at least 0.01' ) if price.nil? || price <
0.01
end
Test 1

test "invalid with empty attributes" do


product = Product.new
assert !product.valid?
assert product.errors.invalid?(:title)
assert product.errors.invalid?(:description)
assert product.errors.invalid?(:price)
assert product.errors.invalid?(:image_url)
end
Run the test case

ruby -I test test/unit/product_test.rb


#Loaded suite test/unit/product_test
#Started
#..
#Finished in 0.092314 seconds.
2 tests, 6 assertions, 0 failures, 0 errors
Test 2
test "positive price" do

product = Product.new(:title => "My Book Title" ,


:description => "yyy" ,
:image_url => "zzz.jpg" )
product.price = -1
assert !product.valid?
assert_equal "should be at least 0.01" , product.errors.on(:price)

product.price = 0
assert !product.valid?
assert_equal "should be at least 0.01" , product.errors.on(:price)

product.price = 1
assert product.valid?
end
Fixtures
sample data
YAML fixtures
ruby_book:
title: Programming Ruby
description: Dummy description
price: 1234
image_url: ruby.png

rails_book:
title: Agile Web Development with Rails
description: Dummy description
price: 2345
image_url: rails.png
Naming convention & usage

fixtures :products
Mention this in the ProductTest class
:products => name of table and YML file
Fixture loading

Loading involves three steps:

* Remove any existing data


* Load the fixture data
* Dump the fixture data into a variable in case you want to access
it directly
Test 3 (using fixture)

test "unique title1" do


product = Product.new(:title => products(:ruby_book).title,
:description => "yyy" ,
:price => 1,
:image_url => "fred.gif" )
assert !product.save
Assertions Available


assert( boolean, [msg] )

assert_equal( obj1, obj2, [msg] )

assert_not_equal( obj1, obj2, [msg] )

assert_same( obj1, obj2, [msg] )

assert_instance_of( class, obj, [msg] )
Thank you
WEB v2.0

Das könnte Ihnen auch gefallen