Intro to Rails ActiveRecord


Published on

An introduction to Ruby on Rails ORM ActiveRecord.

  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide
  • This definition supposes a relational data store. The active record pattern would not apply to document oriented systems such as CouchDB, Mongo, and other No-SQL databases, as they tend to store aggregates.
  • I rarely use these options. :select can lead to confusion if you select columns from more than one table because the returned objects will be read-only. I have yet to find a common use case for :group
  • Many times complex views will want to be backed by a custom finder to avoid N+1 issues. I usually place these in a finder module mixed into my model class to keep the model class clean.
  • This is where we begin joining models to other models to reflect the domain space.
  • I very rarely use this type of association. I find that my join models eventually begin to a
  • To halt the chain return false. To continue return true.
  • Intro to Rails ActiveRecord

    1. 1. Introduction to ActiveRecord <ul><li>The Rails Object Relational Mapper </li></ul><ul><li>by Mark Menard, Vita Rara, Inc. </li></ul>
    2. 2. Rails ActiveRecord <ul><li>ActiveRecord is the Object Relational Mapping library that is built into Rails. </li></ul><ul><ul><li>ActiveRecord is the default ORM used in Rails, but others can be used as well, such as: </li></ul></ul><ul><ul><ul><li>Data Mapper </li></ul></ul></ul><ul><ul><ul><li>Sequel </li></ul></ul></ul><ul><li>ActiveRecord was started by David Heinemeier Hansson the creator of Rails. </li></ul><ul><li>ActiveRecord has been enhanced and expanded by many developers. </li></ul><ul><li>ActiveRecord is an implementation of the active record pattern. </li></ul><ul><li>Rails’ ActiveRecord is a leaky SQL abstraction. </li></ul>© Vita Rara, Inc.
    3. 3. Definition © Vita Rara, Inc. Active Record: An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data. -Martin Fowler, Patterns of Enterprise Application Architecture (page 160)
    4. 4. Rails ActiveRecord <ul><li>ActiveRecord classes and the tables they wrap are referred to as models. </li></ul><ul><li>ActiveRecord encourages a Model Driven style of development. </li></ul><ul><li>ActiveRecord encourages a non-anemic model layer. </li></ul><ul><ul><li>Skinny Controllers </li></ul></ul><ul><ul><li>Fat Models </li></ul></ul><ul><ul><li>Rarely a need for a service layer. </li></ul></ul><ul><li>ActiveRecord can be used as a persistence layer in a Domain Driven Design. </li></ul>© Vita Rara, Inc.
    5. 5. A Leaky Abstraction <ul><li>ActiveRecord will not isolate you from SQL. </li></ul><ul><ul><li>It’s not Hibernate </li></ul></ul><ul><ul><li>It’s not JPA </li></ul></ul><ul><ul><li>It’s not NHibernate </li></ul></ul><ul><ul><li>It’s not your daddy’s “make SQL go away ORM”. </li></ul></ul><ul><li>A knowledge of SQL is necessary to succeeding with ActiveRecord. </li></ul><ul><li>ActiveRecord makes the easy SQL things easy, and makes the hard SQL stuff possible. </li></ul>© Vita Rara, Inc.
    6. 6. Fundamentals <ul><li>One database table maps to one Ruby class </li></ul><ul><li>Table names are plural and class names are singular </li></ul><ul><li>Database columns map to attributes, i.e. get and set methods, in the model class </li></ul><ul><ul><li>Attributes are not defined on the class. </li></ul></ul><ul><ul><li>Attributes are inferred from the underlying table schema. </li></ul></ul><ul><li>All tables have an integer primary key called id </li></ul><ul><li>Database tables are created with migrations </li></ul>© Vita Rara, Inc.
    7. 7. ActiveRecord Model Example © Vita Rara, Inc. create_table &quot;persons&quot; do |t| t.string :first_name, last_name t.timestamps end class Person < ActiveRecord::Base end p = p.first_name = ‘Mark’ p.last_name = ‘Menard’
    8. 8. Working with Legacy Schemas <ul><li>Table name can be set using set_table_name. </li></ul><ul><ul><li>Views can also be used to adapt to AR conventions </li></ul></ul><ul><li>Id column can be mapped using “configuration”. </li></ul><ul><li>Different column names can be mapped with relative ease. </li></ul><ul><ul><li>Typically attribute names are lowercase with words separated by underscores. </li></ul></ul><ul><ul><ul><li>first_name </li></ul></ul></ul><ul><ul><ul><li>updated_at </li></ul></ul></ul><ul><ul><li>I have personally mapped study caps style tables to Rails defaults. </li></ul></ul><ul><ul><ul><li>firstName => first_name </li></ul></ul></ul>© Vita Rara, Inc.
    9. 9. CRUD: Create, Read, Update, Delete <ul><li>Create </li></ul><ul><ul><li>p = Person.create(:first_name => ‘Mark’) </li></ul></ul><ul><ul><li>p = => ‘Mark’) </li></ul></ul><ul><li>Read </li></ul><ul><ul><li>p = Person.find(1) </li></ul></ul><ul><ul><li>p = Person.find_by_first_name(‘Mark’) </li></ul></ul><ul><li>Update </li></ul><ul><ul><li> </li></ul></ul><ul><ul><li>p.update_attributes(:last_name => ‘Menard’) </li></ul></ul><ul><li>Delete </li></ul><ul><ul><li>p.destroy </li></ul></ul>© Vita Rara, Inc.
    10. 10. © Vita Rara, Inc. # Instantiate a new Person, which can be persisted. p = Person. new # Instantiate a new Person, with attributes set based on a # Map, which can be persisted. p = Person. new (:first_name => 'Mark' , :last_name => 'Menard' )
    11. 11. ActiveRecord::Base.create © Vita Rara, Inc. # Immediated create a record in the database. p = Person.create(:first_name => 'Mark' , :last_name => 'Menard' ) # This sets the attributes and calls #save on the instance.
    12. 12. Finding Models <ul><li>Built in finders </li></ul><ul><ul><li>Find the first record: User.find(:first) </li></ul></ul><ul><ul><li>Find all records of a type: User.find(:all) </li></ul></ul><ul><ul><li>Find by primary id: User.find(1) </li></ul></ul><ul><li>Dynamic finders using attributes </li></ul><ul><ul><li>ActiveRecord can build basic finders by convention based on the attributes of a model. </li></ul></ul><ul><ul><ul><li>User.find_by_login(‘mark’) </li></ul></ul></ul>© Vita Rara, Inc.
    13. 13. Advanced Finding <ul><li>Because ActiveRecord is a leaky abstraction it provides straight forward ways to access SQL in its finders </li></ul>© Vita Rara, Inc. User.find(:all, :conditions => [ “login = ? AND password = ?” , login, password], :limit => 10 , :offset => 10 , :order => 'login' , :joins => 'accounts on user.account_id =' )
    14. 14. Advanced Finding (con’t) <ul><li>Finders also support: </li></ul><ul><ul><li>:select </li></ul></ul><ul><ul><li>:group </li></ul></ul><ul><ul><li>:include (optimize n+1 queries) </li></ul></ul>© Vita Rara, Inc.
    15. 15. Eager Loading: Avoid N+1 Issue © Vita Rara, Inc. <% # Don't put code like this in your view. This is for illustration only! # Find and display order summary of all pending orders for an account. orders = Order.find_pending_by_account(current_account) %> <% orders.each do |order| -%> <%= render :partial => 'order_header' %> <!-- This fires off a query for each order! BAD BAD BAD --> <% order.line_items.each do |line_item| -%> <%= render :partial => 'line_item' %> <% end -%> <% end -%> <% # Better would be orders = Order.find_pending_by_account(current_account, :include => [ :line_items ]) %>
    16. 16. Updating Models © Vita Rara, Inc. user = User.find( 1 ) user.first_name = ‘Mark’ # returns true on success user.last_name = ‘Menard’! # throws an exception if it fails # Immediately update the attributes and call #save # returns true on success user.update_attributes(:first_name => 'John' , :last_name => 'Doe' ) # Immediately update the attributes and call #save! # throws an exception on failure. user.update_attributes!(:password => ‘abccd1234’ )
    17. 17. Transactions <ul><li>Account.transaction do </li></ul><ul><li>account1.deposit(100) </li></ul><ul><li>account2.withdraw(100) </li></ul><ul><li>end </li></ul>© Vita Rara, Inc.
    18. 18. ActiveRecord Associations
    19. 19. ActiveRecord Associations <ul><li>Two primary types of associations: </li></ul><ul><ul><li>belongs_to </li></ul></ul><ul><ul><li>has_one / has_many </li></ul></ul><ul><li>There are others, but they are not commonly used. </li></ul><ul><ul><li>has_and_belongs_to_many </li></ul></ul><ul><ul><ul><li>Used to map many-to-many associations. </li></ul></ul></ul><ul><ul><ul><li>Generally accepted practice is to use a join model. </li></ul></ul></ul>© Vita Rara, Inc.
    20. 20. Association Methods <ul><li>Associations add methods to the class. </li></ul><ul><ul><li>This is an excellent example of meta-programming. </li></ul></ul><ul><li>Added methods allow for easy management of the related models. </li></ul>© Vita Rara, Inc.
    21. 21. ActiveRecord Association Examples © Vita Rara, Inc. # Has Many class Order < ActiveRecord::Base has_many :order_line_items end class OrderLineItem < ActiveRecord::Base belongs_to :order end # Has One class Party < ActiveRecord::Base has_one :login_credential end class LoginCredential < ActiveRecord::Base belongs_to :party end
    22. 22. has_many & belongs_to <ul><li>Used to model one-to-many associations. </li></ul><ul><li>The belongs_to side has the foreign key. </li></ul>© Vita Rara, Inc. class Order < ActiveRecord::Base has_many :line_items end class LineItem < ActiveRecord::Base belongs_to :order end create_table :orders do |t| t.string :number end create_table :line_items do |t| t.integer :order_id end
    23. 23. Has Many Examples © Vita Rara, Inc. has_many :comments, :order => &quot;posted_on&quot; has_many :comments, :include => :author has_many :people, :class_name => &quot;Person&quot; , :conditions => &quot;deleted = 0&quot; , :order => &quot;name&quot; has_many :tracks, :order => &quot;position&quot; , :dependent => :destroy has_many :comments, :dependent => :nullify has_many :tags, :as => :taggable has_many :subscribers, :through => :subscriptions, :source => :user has_many :subscribers, :class_name => &quot;Person&quot; , :finder_sql => 'SELECT DISTINCT people.* ' + 'FROM people p, post_subscriptions ps ' + 'WHERE ps.post_id = #{id} AND ps.person_id = ' + 'ORDER BY p.first_name'
    24. 24. has_many Methods © Vita Rara, Inc. class Firm has_many :clients end firm = Firm.find( 1 ) firm.clients firm.clients << firm.clients.delete firm.clients = firm.client_ids firm.client_ids = firm.clients.clear firm.clients.empty? firm.clients.count firm.clients.find => 'Mark' ) # Like => firm.clients.create(:first_name => 'Mark' ) # Like Party.create(:firm_id =>
    25. 25. has_and_belongs_to_many <ul><li>Used to model many-to-many associations. </li></ul>© Vita Rara, Inc. create_table :categories_posts, :id => false do t.column :category_id, :integer, :null => false t.column :post_id, :integer, :null => false end class Product < ActiveRecord::Base has_and_belongs_to_many :categories end class Category < ActiveRecord::Base has_and_belongs_to_many :products end product = Product.find_by_name(“Mac Book Pro”) category = Category.find_by_name(“Laptops”) product.categories.count # => 0 category.products.count # => 0 product.categories << category product.categories.count # => 1 category.products.count # => 1
    26. 26. Join Models vs. has_and_belongs_to_many <ul><li>Join models </li></ul><ul><ul><li>Are generally preferred. </li></ul></ul><ul><ul><li>Make the joining table explicit. </li></ul></ul><ul><ul><li>Allow domain logic to be added to the join model. </li></ul></ul><ul><ul><li>Allow a more literate style of coding. </li></ul></ul><ul><ul><li>has_many :foos, :through => :bars makes it trivial. </li></ul></ul><ul><li>Commonly has_and_belongs_to_many associations are refactored later to make the join model explicit. </li></ul><ul><ul><li>Better to just do it up front. </li></ul></ul>© Vita Rara, Inc.
    27. 27. has_many :foos, :through => :bars <ul><li>has_many :through is used to model has_many relationships through a “join” model. </li></ul>© Vita Rara, Inc. class Blog < ActiveRecord::Base has_many :subscriptions has_many :users, :through => :subscriptions end class User < ActiveRecord::Base has_many :subscriptions has_many :blogs, :through => :subscriptions end class Subscription < ActiveRecord::Base belongs_to :blog belongs_to :user end
    28. 28. Polymorphic Associations <ul><li>Easiest to illustrate by example </li></ul>© Vita Rara, Inc. class Person < ActiveRecord::Base has_one :address, :as => :addressable end class Company < ActiveRecord::Base has_one :address, :as => :addressable end class Address < ActiveRecord::Base belongs_to :addressable, :polymorphic => true end create_table :addresses do |t| # ... t.integer :addressable_id t.string :addressable_type end
    29. 29. ActiveRecord Validations <ul><li>Keeping Your Data Safe </li></ul>
    30. 30. Validation <ul><li>Validations are rules in your model objects to help protect the integrity of your data </li></ul><ul><li>Validation is invoked by the #save method. Save returns true if validations pass and false otherwise. </li></ul><ul><li>If you invoke #save! then a RecordInvalid exception is raised if the object is not valid. </li></ul><ul><li>Use save(false) if you need to turn off validation </li></ul>© Vita Rara, Inc.
    31. 31. Validation Callbacks <ul><li>#validate </li></ul><ul><ul><li>Called before a model is written to the database, either on initial save, or on updates. </li></ul></ul><ul><li>#validate_on_create </li></ul><ul><ul><li>Called before a model is inserted into the database. </li></ul></ul><ul><li>#validate_on_update </li></ul><ul><ul><li>Called before an existing model is updated in the database. </li></ul></ul>© Vita Rara, Inc.
    32. 32. Validation Callbacks (cont) © Vita Rara, Inc. class Person < ActiveRecord::Base def validate puts “validate invoked” end def validate_on_create puts “validate_on_create invoked” end def validate_on_update puts “validate_on_update invoked” end end peter = Person.create(:name => “Peter”) # => peter.validate and peter.validate_on_create invoked peter.last_name = “Forsberg” # => peter.validate_on_update invoked
    33. 33. Declarative Validations © Vita Rara, Inc. Rails contains a large number of declarative validations that are applied to classes by convention. Declarative validations free developers from the drudgery of most model validation.
    34. 34. validates_presence_of <ul><li>Used to denote required attributes. </li></ul>© Vita Rara, Inc. class Person < ActiveRecord::Base validates_presence_of :first_name validates_presence_of :last_name end p = Person. new p.valid? #=> false p.first_name = 'Mark' p.last_name = 'Menard' p.valid? #=> true
    35. 35. validates_uniqueness_of <ul><li>Ensures that the value of an attribute is unique in the database. </li></ul><ul><ul><li>Can be constrained to work subsets of the data. </li></ul></ul>© Vita Rara, Inc. class User < ActiveRecord::Base belongs_to :account validates_uniqueness_of :login, :scope => [ :account ] end account = Account.find(1) user_1 = account.users.create(:login => 'mark' ) user_2 = account.users.create!(:login => 'mark' ) #=> Throws InvalidRecord exceptoion
    36. 36. validates_numericality_of <ul><li>Ensures that an attribute is a number. </li></ul><ul><ul><li>Can be constrained to integral values. </li></ul></ul>© Vita Rara, Inc. class User < ActiveRecord::Base validates_numericality_of :number, :integer_only => true end User.create!(:number => 'some number' ) #=> Throws Invalid
    37. 37. validates_length_of <ul><li>Ensures an attribute is the proper length </li></ul>© Vita Rara, Inc. class User < ActiveRecord::Base validates_length_of :login, :within => 3 .. 20 validates_length_of :password, :is => 8 validates_length_of :name, :minimum => 3 end
    38. 38. validates_format_of <ul><li>Ensures the format of an attribute matches regular expression. </li></ul><ul><ul><li>Can be used to validate email addresses. </li></ul></ul>© Vita Rara, Inc. class User < ActiveRecord::Base validates_format_of :email, :with => /^[wd]+$/ end
    39. 39. validates_inclusion_of & validates_exclusion_of <ul><li>Ensures that a value is or is not in a collection of options. </li></ul>© Vita Rara, Inc. class User < ActiveRecord::Base validates_inclusion_of :gender, :in => %w( male female ) , :message => &quot;Oh really....&quot; validates_exclusion_of :login, :in => %w( root admin super ) , :message => &quot;Tisk tisk...&quot; end
    40. 40. validates_associated <ul><li>Ensures that an associated model is valid prior to saving. </li></ul>© Vita Rara, Inc. class User < ActiveRecord::Base belongs_to :account validates_associated :account, :on => :create end class Account < ActiveRecord::Base validates_presence_of :name end user = User. new (:login => 'mark' , :name => 'Mark Menard' ) user.account = Account. new # invalid missing name! #=> Throws RecordInvalid exception.
    41. 41. Other Declarative Validations <ul><li>validates_acceptance_of </li></ul><ul><li>validates_confirmation_of </li></ul><ul><li>validates_each </li></ul><ul><li>validates_size_of </li></ul>© Vita Rara, Inc. You can also create your own declarative validations that match your problem domain.
    42. 42. Using Validation Callbacks <ul><li>Sometimes validation is more complex than the declarative validations can handle. </li></ul><ul><ul><li>Validation may relate to the semantics of your problem domain. </li></ul></ul><ul><ul><li>Validation may relate to more than one attribute. </li></ul></ul>© Vita Rara, Inc. class Account validate :does_domain_exist private def does_domain_exist Resolv.getaddress(self.domain_name) rescue errors.add(:domain_name, 'Domain name does not exist.' ) end end
    43. 43. Using Validation Callbacks <ul><li>Class methods for defining validation call backs: </li></ul><ul><ul><li>validate :method_name </li></ul></ul><ul><ul><li>validate_on_update :method_name </li></ul></ul><ul><ul><li>validate_on_create :method_name </li></ul></ul><ul><li>Instance methods for defining validations: </li></ul><ul><ul><li>validate </li></ul></ul><ul><ul><li>validate_on_update </li></ul></ul><ul><ul><li>validate_on_create </li></ul></ul>© Vita Rara, Inc.
    44. 44. Model Life Cycle
    45. 45. New Model Callbacks <ul><li>ActiveRecord calls these methods prior to saving a new record: </li></ul><ul><ul><li>before_validation </li></ul></ul><ul><ul><li>before_validation_on_create </li></ul></ul><ul><ul><ul><li>validation is performed </li></ul></ul></ul><ul><ul><li>after_validation </li></ul></ul><ul><ul><li>after_validation_on_create </li></ul></ul><ul><ul><li>before_save </li></ul></ul><ul><ul><li>before_create </li></ul></ul><ul><ul><ul><li>ActiveRecord saves the record </li></ul></ul></ul><ul><ul><li>after_create </li></ul></ul><ul><ul><li>after_save </li></ul></ul>© Vita Rara, Inc.
    46. 46. Existing Model Callbacks <ul><li>ActiveRecord calls these methods prior to saving an existing record </li></ul><ul><ul><li>before_validation </li></ul></ul><ul><ul><ul><li>ActiveRecord performs validation </li></ul></ul></ul><ul><ul><li>after_validation </li></ul></ul><ul><ul><li>before_save </li></ul></ul><ul><ul><li>before_update </li></ul></ul><ul><ul><ul><li>ActiveRecord saves the record </li></ul></ul></ul><ul><ul><li>after_update </li></ul></ul><ul><ul><li>after_save </li></ul></ul>© Vita Rara, Inc.
    47. 47. Destroy Model Callbacks <ul><li>ActiveRecord calls these methods when destroying a model: </li></ul><ul><ul><li>before_destroy </li></ul></ul><ul><ul><ul><li>ActiveRecord performs the DELETE </li></ul></ul></ul><ul><ul><li>after_destroy </li></ul></ul>© Vita Rara, Inc.
    48. 48. Callback Use Cases <ul><li>Cleaning up attributes prior to saving </li></ul><ul><li>Starting followup processes </li></ul><ul><li>Sending notifications </li></ul><ul><li>Geocoding </li></ul><ul><li>Paranoia: Don’t delete anything just mark it deleted. </li></ul><ul><li>Clean up associated files, avatars, other assets. </li></ul>© Vita Rara, Inc.
    49. 49. Cleaning up Attributes Prior to Saving © Vita Rara, Inc. class CreditCard before_validation :cleanup_number private def cleanup_number self.number = number.gsub( /[^0-9]/ , &quot;&quot; ) true # I like to be explicit end end
    50. 50. Observers
    51. 51. Observers <ul><li>Observers allow you to create classes that observe changes in your Models. </li></ul><ul><ul><li>Observers allow you classes to focus on a single responsibility. </li></ul></ul><ul><li>Observers can hook onto the standard rails life cycle call backs. </li></ul>© Vita Rara, Inc.
    52. 52. Creating an Audit Trail with an Observer © Vita Rara, Inc. # in config/environment.rb config.active_record_observers = [ :auditor ] # in auditor.rb class Auditor < ActiveRecord::Observer observe User def after_create (model) log_info( &quot;New #{} created.&quot; , model) end def after_update (model) log_info( &quot;Update #{}&quot; , model) end def after_destroy (model) log_info( &quot;Destroy #{}&quot; , model) end private def log_info (model, info) end end
    53. 53. Shameless Self Promotion
    54. 54. Ruby and Rails Training <ul><li>One day to three day programs. </li></ul><ul><li>Introduction to Ruby </li></ul><ul><li>Advanced Ruby </li></ul><ul><li>Introduction to Rails </li></ul><ul><li>Advanced Rails </li></ul><ul><li>Test Driven Development </li></ul><ul><li>Behavior Driven Development </li></ul><ul><li>Test Anything with Cucumber </li></ul><ul><li>Advanced Domain Modeling with ActiveRecord </li></ul><ul><li>Domain Driven Development with Rails </li></ul>© Vita Rara, Inc.
    55. 55. Ruby on Rails Consulting <ul><li>Full Life Cycle Project Development </li></ul><ul><ul><li>Inception </li></ul></ul><ul><ul><li>Implementation </li></ul></ul><ul><ul><li>Deployment </li></ul></ul><ul><ul><li>Long Term Support </li></ul></ul><ul><li>Ruby on Rails Mentoring </li></ul><ul><ul><li>Get your team up to speed using Rails </li></ul></ul>© Vita Rara, Inc.
    56. 56. Contact Information <ul><li>Mark Menard </li></ul><ul><ul><li>[email_address] </li></ul></ul><ul><ul><li> / </li></ul></ul><ul><ul><li>518 369 7356 </li></ul></ul>© Vita Rara, Inc.