Successfully reported this slideshow.
Your SlideShare is downloading. ×

Introduction to Active Record - Silicon Valley Ruby Conference 2007

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Upcoming SlideShare
Web Security - Hands-on
Web Security - Hands-on
Loading in …3
×

Check these out next

1 of 60 Ad

More Related Content

Slideshows for you (19)

Advertisement

Similar to Introduction to Active Record - Silicon Valley Ruby Conference 2007 (20)

Advertisement

Recently uploaded (20)

Introduction to Active Record - Silicon Valley Ruby Conference 2007

  1. Introduction to Active Record Evan ‘Rabble’ Henshaw-Plath evan@protest.net - Yahoo! Brickhouse anarchogeek.com - testingrails.com
  2. Active Record is a Design Pattern An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.
  3. Active Record the Pattern Person last_name first_name dependents_count insert update get_exemption is_flagged_for_audit? get_taxable_earnings? Active Record uses the most obvious approach, putting data access logic in the domain object. - Martin Fowler
  4. One Class Per Table The Model Code The Database class User < ActiveRecord::Base CREATE TABLE `users` ( `id` int(11) NOT NULL auto_increment, end `login` varchar(255), `email` varchar(255), `crypted_password` varchar(40), `salt` varchar(40), `created_at` datetime default NULL, `updated_at` datetime default NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB;
  5. One Object Per Row
  6. There Are Other Ways To Do it Active Record is just one ‘Data Source Architectural Pattern’ • Table Data Gateway • Row Data Gateway • Data Mapper • The Anti-Patterns
  7. Standard Active Record • Direct mapping to the DB • Class to table • Object to row • Simple, no relationship between objects • Just a finder method with getters and setters
  8. ActiveRecord the ruby library Active Record is a library built for Ruby on Rails. Makes CRUD Easy Create Read Update Delete
  9. ActiveRecord the ruby library I have never seen an Active Record implementation as complete or as useful as rails. - Martin Fowler
  10. Rails’ ActiveRecord • DRY Conventions & Assumptions • Validations • Before and after filters • Database Agnostic (mostly) • Migrations • Model relationships • has_many, belongs_to, etc...
  11. What Active Record Likes • mapping class names to table names • pluralized table names • integer primary keys • classname_id foreign keys • simple schemas • single table inheritance
  12. Active Record Doesn’t Like • views • stored methods • foreign key constraints • cascading commits • split or clustered db’s • enums
  13. The Basics ./app/models/user.rb Loading a user class User < ActiveRecord::Base >> user_obj = User.find(2) end => #<User:0x352e8bc @attributes= {"salt"=>"d9ef...", The SQL Log "updated_at"=>"2007-04-19 10:49:15", "crypted_password"=>"9c1...", User Load (0.003175) "id"=>"2", SELECT * FROM users "remember_token"=>"a8d...", WHERE (users.id = 2) LIMIT 1 "login"=>"rabble", "created_at"=>"2007-04-19 10:49:15", "email"=>"evan@protest.net"}>
  14. The Find Method Find is the primary method of Active Record Examples: User.find(23) User.find(:first) User.find(:all, :offset => 10, :limit => 10) User.find(:all, :include => [:account, :friends]) User.find(:all, :conditions => [“category in (?), categories, :limit => 50) User.find(:first).articles
  15. The Four Ways of Find Find by id: This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]). Find first: This will return the first record matched by the options used. Find all: This will return all the records matched by the options used. Indirectly: The find method is used for AR lookups via associations.
  16. Understanding Find Model#find(:all, { parameters hash } What Find Does: * generates sql * executes sql * returns an enumerable (array like object) * creates an AR model object for each row
  17. Find with :conditions :conditions - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. Student.find(:all, :conditions => [‘first_name = ? and status = ?’ ‘rabble’, 1]) New Style (Edge Rails Only) Student.find(:all, :conditions => {:first_name => “rabble”, :status => 1}) SQL Executed: SELECT * FROM students WHERE (first_name = 'rabble' and status = 1);
  18. Doing it Securely class User < ActiveRecord::Base def self.authenticate_unsafely(user_name, password) find(:first, :conditions => "user_name = '#{user_name}' AND password = '#{password}'") end def self.authenticate_safely(user_name, password) find(:first, :conditions => [ "user_name = ? AND password = ?", user_name, password ]) end # Edge Rails Only (Next Version of Rails) def self.authenticate_safely_simply(user_name, password) find(:first, :conditions => { :user_name => user_name, :password => password }) end end
  19. Order By :order - An SQL fragment like "created_at DESC, name". Student.find(:all, :order => ‘updated_at DESC’) SQL Executed: SELECT * FROM users ORDER BY created_at;
  20. Group By :group - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause. Student.find(:all, :group => ‘graduating_class’) SQL Executed: SELECT * FROM users GROUP BY graduating_class;
  21. Limit & Offset :limit - An integer determining the limit on the number of rows that should be returned. :offset- An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows. Student.find(:all, :limit => 10, :offset => 0) SQL Executed: SELECT * FROM users LIMIT 0, 10;
  22. Joins :joins - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed). Student.find(:all, :join => "LEFT JOIN comments ON comments.post_id = id") SQL Executed: SELECT * FROM users GROUP BY graduating_class; Returns read only objects unless you say :readonly => false
  23. Alternative Finds find_by_sql find_by_attribute_and_attribute2 find_or_create Depreciated Find’s find_first find_all find_on_conditions
  24. Associations The Four Primary Associations belongs_to has_one has_many has_and_belongs_to_many class Project < ActiveRecord::Base belongs_to :portfolio has_one :project_manager has_many :milestones has_and_belongs_to_many :categories end
  25. Associations One to One has_one & belongs_to Many to One has_many & belongs_to Many to Many has_and_belongs_to_many has_many :through
  26. One to One Use has_one in the base, and belongs_to in the associated model. class Employee < ActiveRecord::Base has_one :office end class Office < ActiveRecord::Base belongs_to :employee # foreign key - employee_id end
  27. One To One Example >> joe_employee = Employee.find_by_first_name('joe') SELECT * FROM employees WHERE (employees.`first_name` = 'joe') LIMIT 1 => #<Employee:0x36beb14 @attributes={"id"=>"1", "first_name"=>"joe", "last_name"=>"schmo", "created_at"=>"2007-04-21 09:08:59"}> >> joes_office = joe_employee.office SELECT * FROM offices WHERE (offices.employee_id = 1) LIMIT 1 => #<Office:0x36bc06c @attributes={"employee_id"=>"1", "id"=>"1", "created_at"=>"2007-04-21 09:11:44", "location"=>"A4302"}> >> joes_office.employee SELECT * FROM employees WHERE (employees.`id` = 1) => #<Employee:0x36b6ef0 @attributes={"id"=>"1", "first_name"=>"joe", "last_name"=>"schmo", "created_at"=>"2007-04-21 09:08:59"}>
  28. belongs_to One to One Relationship. Use belong to when the foreign key is in THIS table. • Post#author (similar to Author.find(author_id) ) • Post#author=(author) (similar to post.author_id = author.id) • Post#author? (similar to post.author == some_author) • Post#author.nil? • Post#build_author (similar to post.author = Author.new) • Post#create_author (similar to post.author = Author; post.author.save;
  29. Defining belongs_to class Employee < ActiveRecord::Base belongs_to :firm, :foreign_key => "client_of" belongs_to :author, :class_name => "Person", :foreign_key => "author_id" belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id", :conditions => 'discounts > #{payments_count}' belongs_to :attachable, :polymorphic => true end
  30. has_one One to One Relationship. Use has_one when the foreign key is in the OTHER table. • Account#beneficiary (similar to Beneficiary.find (:first, :conditions => "account_id = #{id}")) • Account#beneficiary=(beneficiary) (similar to beneficiary.account_id = account.id; beneficiary.save) • Account#beneficiary.nil? • Account#build_beneficiary (similar to Beneficiary.new ("account_id" => id)) • Account#create_beneficiary (similar to b = Beneficiary.new("account_id" => id); b.save; b)
  31. Defining has_one class Employee < ActiveRecord::Base # destroys the associated credit card has_one :credit_card, :dependent => :destroy # updates the associated records foreign key value to null rather than destroying it has_one :credit_card, :dependent => :nullify has_one :last_comment, :class_name => "Comment", :order => "posted_on" has_one :project_manager, :class_name => "Person", :conditions => "role = 'project_manager'" has_one :attachment, :as => :attachable end
  32. One to Many One-to-many Use has_many in the base, and belongs_to in the associated model. class Manager < ActiveRecord::Base has_many :employees end class Employee < ActiveRecord::Base belongs_to :manager # foreign key - manager_id end
  33. One to Many >> benevolent_dictator = Manager.find(:first, :conditions => ['name = "DHH"']) SELECT * FROM managers WHERE (name = "DHH") LIMIT 1 => #<Manager:0x369b7b8 @attributes={"name"=>"DHH", "id"=>"1", "created_at"=>"2007-04-21 09:59:24"}> >> minions = benevolent_dictator.employees SELECT * FROM employees WHERE (employees.manager_id = 1) => [#<Employee:0x36926a4 @attributes={"manager_id"=>"1", "id"=>"1", "first_name"=>"joe", "last_name"=>"schmo", "created_at"=>"2007-04-21 09:08:59"}>, #<Employee:0x36925f0 @attributes={"manager_id"=>"1", "id"=>"2", "first_name"=>"funky", "last_name"=>"monkey", "created_at"=>"2007-04-21 09:58:20"}>]
  34. has_many Augmenting the Model • Firm#clients (similar to Clients.find :all, :conditions => "firm_id = #{id}") • Firm#clients<< • Firm#clients.delete • Firm#client_ids • Firm#client_ids= • Firm#clients=
  35. has_many • Firm#client.clear • Firm#clients.empty? (similar to firm.clients.size == 0) • Firm#clients.size (similar to Client.count "firm_id = #{id}") • Firm#clients.find (similar to Client.find(id, :conditions => "firm_id = #{id}")) • Firm#clients.build (similar to Client.new ("firm_id" => id)) • Firm#clients.create (similar to c = Client.new ("firm_id" => id); c.save; c)
  36. has_many examples class Employee < ActiveRecord::Base has_many :comments, :order => "posted_on" has_many :comments, :include => :author has_many :people, :class_name => "Person", :conditions => "deleted = 0", :order => "name" has_many :tracks, :order => "position", :dependent => :destroy has_many :comments, :dependent => :nullify has_many :tags, :as => :taggable has_many :subscribers, :through => :subscriptions, :source => :user has_many :subscribers, :class_name => "Person", :finder_sql => 'SELECT DISTINCT people.* ' + 'FROM people p, post_subscriptions ps ' + 'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' + 'ORDER BY p.first_name' end
  37. Many to Many Simple Joiner Table has_and_belongs_to_many Joiner Model has_many :through
  38. has_and_belongs_to_many The Simple Joiner Table Way
  39. has_and_belongs_to_many neglected by rails-core
  40. has_and_belongs_to_many Augmenting the Model • Developer#projects • Developer#projects<< • Developer#projects.delete • Developer#projects= • Developer#projects_ids • Developer#projects_ids= • Developer#clear
  41. has_and_belongs_to_many • Developer#projects.empty? • Developer#projects.size • Developer#projects.find(id) # Also find(:first / :all) • Developer#projects.build #(similar to Project.new ("project_id" => id)) • Developer#projects.create (similar to c = Project.new("project_id" => id); c.save; c)
  42. habtm example create_table :developers do |t| t.column :name, :string t.column :created_at, :datetime end create_table :projects do |t| t.column :name, :string t.column :created_at, :datetime end create_table(:developers_projects, :id => false) do |t| t.column :developer_id, :integer t.column :project_id, :integer end
  43. habtm example >> d = Developer.find(1) SELECT * FROM developers WHERE (developers.`id` = 1) => #<Developer:0x32bc7dc @attributes={"name"=>"rabble", "id"=>"1", "created_at"=>nil}> >> d.projects SELECT * FROM projects INNER JOIN developers_projects ON projects.id = developers_projects.project_id WHERE (developers_projects.developer_id = 1 ) => [#<Project:0x3257cc4 @attributes= {"name"=>"ragi", "project_id"=>"1", "id"=>"1", "developer_id"=>"1", "created_at"=>nil}>, #<Project:0x3257c10 @attributes= {"name"=>"acts_as_autenticated", "project_id"=>"3", "id"=>"3", "developer_id"=>"1", "created_at"=>nil}>]
  44. has_many :through DHH’s One True Way of Many to Many
  45. has_many :through Full Joiner Model
  46. has_many :through class Appearance < ActiveRecord::Base belongs_to :dancer belongs_to :movie end class Dancer < ActiveRecord::Base has_many :appearances, :dependent => true has_many :movies, :through => :appearances end class Movie < ActiveRecord::Base has_many :appearances, :dependent => true has_many :dancers, :through => :appearances end
  47. Validations class User < ActiveRecord::Base validates_confirmation_of :login, :password validates_confirmation_of :email, :message => "should match confirmation" validates_format_of :email, :with => /A([^@s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})/i, :on = :create end
  48. Validations • Keeping Data Clean • In object validation of fields, calculated validations • Instead of key constraints • The database is for storage, the model is for the business logic • Kinds of validations, custom validations, etc...
  49. But Wait? • Aren’t format, presence, relationship validations supposed to be the database’s job? • Traditionally, yes. • ActiveRecord does constraints in the model, not the database
  50. But Why? • Validations Constraints are Business Logic • Business logic should be in the model • It makes things easy • End users can get useful error messages • Makes the postback pattern work well
  51. Data Integrity? • It’s still possible to do constraints in the db • But it’s not as necessary • Validations are constraints which make sense in terms of functionality of the app • The rails ways is to just use validations • Most DBA’s insist on foreign_key constraints
  52. What AR Returns? • Enumerable Objects (kind of like arrays) • Preselects and instantiates objects • Nifty methods: to_yaml, to_xml, to_json
  53. Output Formats ruby - inspect to_yaml #Employee:0x36926a4 --- !ruby/object:Employee @attributes= attributes: {manager_id=1, manager_id: 1 id=1, id: 1 first_name=joe, first_name: joe last_name=schmo, last_name: schmo created_at=2007-04-21 09:08:59} created_at: 2007-04-21 09:08:59 to_xml to_json ?xml version=1.0 encoding=UTF-8? {attributes: employee {manager_id: 1, created-at id: 1, type=datetime2007-04-21T09:08:59-07:00/ created-at first_name: joe, first-namejoe/first-name last_name: schmo, id type=integer1/id created_at: 2007-04-21 09:08:59}} last-nameschmo/last-name manager-id type=integer1/manager-id /employee
  54. Before After Callbacks * (-) save class Subscription ActiveRecord::Base * (-) valid? before_create :record_signup * (1) before_validation private * (2) before_validation_on_create def record_signup * (-) validate self.signed_up_on = Date.today * (-) validate_on_create end * (3) after_validation end * (4) after_validation_on_create * (5) before_save class Firm ActiveRecord::Base # Destroys the associated clients and * (6) before_create #people when the firm is destroyed * (-) create before_destroy { * (7) after_create |record| Person.destroy_all firm_id = #{record.id} } * (8) after_save before_destroy { |record| Client.destroy_all client_of = #{record.id} } end
  55. Security
  56. Special Fields * created_at * #{table_name}_count * position * created_on * parent_id * updated_at * lft * updated_on * rgt * lock_version * quote * type * template * id
  57. Active Record Tricks
  58. Drink the Kool aid?
  59. Flickr Photos Used: http://flickr.com/photos/brraveheart/114402291/ http://flickr.com/photos/ryangreenberg/57722319/ http://flickr.com/photos/bright/253175260/ http://flickr.com/photos/benandliz/11065337/ http://flickr.com/photos/good_day/63617697/ http://flickr.com/photos/gaspi/12944421/ http://flickr.com/photos/rickharris/416150393/ http://flickr.com/photos/thomashawk/221827536/ http://flickr.com/photos/babasteve/3322247/ http://flickr.com/photos/brianboulos/7707518/ http://flickr.com/photos/olivander/28058685/ http://flickr.com/photos/ross/28330560/ http://flickr.com/photos/brraveheart/44052308/ http://flickr.com/photos/emdot/45249090/ http://flickr.com/photos/ednothing/142393509/ http://flickr.com/photos/farhang/428136695/ http://flickr.com/photos/alltheaces/87505524/ http://flickr.com/photos/belljar/67877047/ http://flickr.com/photos/alfr3do/7436142/ http://flickr.com/photos/pulpolux/34545782/ http://flickr.com/photos/gdominici/57975123/ http://flickr.com/photos/monkeyc/107979135/ http://flickr.com/photos/josefstuefer/72512671/ http://flickr.com/photos/pedrosimoes7/449314732/ http://flickr.com/photos/uqbar/105440294/ http://flickr.com/photos/dincordero/405452471/ http://flickr.com/photos/auntiep/17135231/ http://flickr.com/photos/andidfl/203883534/ http://flickr.com/photos/einsame_spitze/406992131/ http://flickr.com/photos/ivanomak/434387836/ http://flickr.com/photos/beija-flor/63758047/ http://flickr.com/photos/nrvica/23858419/ http://flickr.com/photos/amerune/174617912/ http://flickr.com/photos/thespeak/137012632/ http://flickr.com/photos/hungry_i/47938311/ http://flickr.com/photos/santos/13952912/ http://flickr.com/photos/supermietzi/179962496/ http://flickr.com/photos/traveller2020/206931940/ http://flickr.com/photos/ko_an/318906221/
  60. Questions? Introduction to Active Record Evan ‘Rabble’ Henshaw-Plath evan@protest.net - Yahoo! Brickhouse anarchogeek.com - testingrails.com

×