Intro to Rails ActiveRecord
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

Intro to Rails ActiveRecord

  • 4,651 views
Uploaded on

An introduction to Ruby on Rails ORM ActiveRecord.

An introduction to Ruby on Rails ORM ActiveRecord.

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
4,651
On Slideshare
4,634
From Embeds
17
Number of Embeds
1

Actions

Shares
Downloads
55
Comments
0
Likes
6

Embeds 17

http://www.slideshare.net 17

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    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.

Transcript

  • 1. Introduction to ActiveRecord
    • The Rails Object Relational Mapper
    • by Mark Menard, Vita Rara, Inc.
  • 2. Rails ActiveRecord
    • ActiveRecord is the Object Relational Mapping library that is built into Rails.
      • ActiveRecord is the default ORM used in Rails, but others can be used as well, such as:
        • Data Mapper
        • Sequel
    • ActiveRecord was started by David Heinemeier Hansson the creator of Rails.
    • ActiveRecord has been enhanced and expanded by many developers.
    • ActiveRecord is an implementation of the active record pattern.
    • Rails’ ActiveRecord is a leaky SQL abstraction.
    © Vita Rara, Inc.
  • 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. Rails ActiveRecord
    • ActiveRecord classes and the tables they wrap are referred to as models.
    • ActiveRecord encourages a Model Driven style of development.
    • ActiveRecord encourages a non-anemic model layer.
      • Skinny Controllers
      • Fat Models
      • Rarely a need for a service layer.
    • ActiveRecord can be used as a persistence layer in a Domain Driven Design.
    © Vita Rara, Inc.
  • 5. A Leaky Abstraction
    • ActiveRecord will not isolate you from SQL.
      • It’s not Hibernate
      • It’s not JPA
      • It’s not NHibernate
      • It’s not your daddy’s “make SQL go away ORM”.
    • A knowledge of SQL is necessary to succeeding with ActiveRecord.
    • ActiveRecord makes the easy SQL things easy, and makes the hard SQL stuff possible.
    © Vita Rara, Inc.
  • 6. Fundamentals
    • One database table maps to one Ruby class
    • Table names are plural and class names are singular
    • Database columns map to attributes, i.e. get and set methods, in the model class
      • Attributes are not defined on the class.
      • Attributes are inferred from the underlying table schema.
    • All tables have an integer primary key called id
    • Database tables are created with migrations
    © Vita Rara, Inc.
  • 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 = Person.new p.first_name = ‘Mark’ p.last_name = ‘Menard’ p.save
  • 8. Working with Legacy Schemas
    • Table name can be set using set_table_name.
      • Views can also be used to adapt to AR conventions
    • Id column can be mapped using “configuration”.
    • Different column names can be mapped with relative ease.
      • Typically attribute names are lowercase with words separated by underscores.
        • first_name
        • updated_at
      • I have personally mapped study caps style tables to Rails defaults.
        • firstName => first_name
    © Vita Rara, Inc.
  • 9. CRUD: Create, Read, Update, Delete
    • Create
      • p = Person.create(:first_name => ‘Mark’)
      • p = Person.new(:first_name => ‘Mark’)
    • Read
      • p = Person.find(1)
      • p = Person.find_by_first_name(‘Mark’)
    • Update
      • p.save
      • p.update_attributes(:last_name => ‘Menard’)
    • Delete
      • p.destroy
    © Vita Rara, Inc.
  • 10. ActiveRecord::Base.new © Vita Rara, Inc. # Instantiate a new Person, which can be persisted. p = Person. new p.save # Instantiate a new Person, with attributes set based on a # Map, which can be persisted. p = Person. new (:first_name => 'Mark' , :last_name => 'Menard' ) p.save
  • 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. Finding Models
    • Built in finders
      • Find the first record: User.find(:first)
      • Find all records of a type: User.find(:all)
      • Find by primary id: User.find(1)
    • Dynamic finders using attributes
      • ActiveRecord can build basic finders by convention based on the attributes of a model.
        • User.find_by_login(‘mark’)
    © Vita Rara, Inc.
  • 13. Advanced Finding
    • Because ActiveRecord is a leaky abstraction it provides straight forward ways to access SQL in its finders
    © Vita Rara, Inc. User.find(:all, :conditions => [ “login = ? AND password = ?” , login, password], :limit => 10 , :offset => 10 , :order => 'login' , :joins => 'accounts on user.account_id = accounts.id' )
  • 14. Advanced Finding (con’t)
    • Finders also support:
      • :select
      • :group
      • :include (optimize n+1 queries)
    © Vita Rara, Inc.
  • 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. Updating Models © Vita Rara, Inc. user = User.find( 1 ) user.first_name = ‘Mark’ user.save # returns true on success user.last_name = ‘Menard’ user.save! # 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. Transactions
    • Account.transaction do
    • account1.deposit(100)
    • account2.withdraw(100)
    • end
    © Vita Rara, Inc.
  • 18. ActiveRecord Associations
  • 19. ActiveRecord Associations
    • Two primary types of associations:
      • belongs_to
      • has_one / has_many
    • There are others, but they are not commonly used.
      • has_and_belongs_to_many
        • Used to map many-to-many associations.
        • Generally accepted practice is to use a join model.
    © Vita Rara, Inc.
  • 20. Association Methods
    • Associations add methods to the class.
      • This is an excellent example of meta-programming.
    • Added methods allow for easy management of the related models.
    © Vita Rara, Inc.
  • 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. has_many & belongs_to
    • Used to model one-to-many associations.
    • The belongs_to side has the foreign key.
    © 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. 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 = p.id ' + 'ORDER BY p.first_name'
  • 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 firm.clients.build(:first_name => 'Mark' ) # Like Party.new(:firm_id => firm.id) firm.clients.create(:first_name => 'Mark' ) # Like Party.create(:firm_id => firm.id)
  • 25. has_and_belongs_to_many
    • Used to model many-to-many associations.
    © 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. Join Models vs. has_and_belongs_to_many
    • Join models
      • Are generally preferred.
      • Make the joining table explicit.
      • Allow domain logic to be added to the join model.
      • Allow a more literate style of coding.
      • has_many :foos, :through => :bars makes it trivial.
    • Commonly has_and_belongs_to_many associations are refactored later to make the join model explicit.
      • Better to just do it up front.
    © Vita Rara, Inc.
  • 27. has_many :foos, :through => :bars
    • has_many :through is used to model has_many relationships through a “join” model.
    © 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. Polymorphic Associations
    • Easiest to illustrate by example
    © 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. ActiveRecord Validations
    • Keeping Your Data Safe
  • 30. Validation
    • Validations are rules in your model objects to help protect the integrity of your data
    • Validation is invoked by the #save method. Save returns true if validations pass and false otherwise.
    • If you invoke #save! then a RecordInvalid exception is raised if the object is not valid.
    • Use save(false) if you need to turn off validation
    © Vita Rara, Inc.
  • 31. Validation Callbacks
    • #validate
      • Called before a model is written to the database, either on initial save, or on updates.
    • #validate_on_create
      • Called before a model is inserted into the database.
    • #validate_on_update
      • Called before an existing model is updated in the database.
    © Vita Rara, Inc.
  • 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.save # => peter.validate_on_update invoked
  • 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. validates_presence_of
    • Used to denote required attributes.
    © 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. validates_uniqueness_of
    • Ensures that the value of an attribute is unique in the database.
      • Can be constrained to work subsets of the data.
    © 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. validates_numericality_of
    • Ensures that an attribute is a number.
      • Can be constrained to integral values.
    © Vita Rara, Inc. class User < ActiveRecord::Base validates_numericality_of :number, :integer_only => true end User.create!(:number => 'some number' ) #=> Throws Invalid
  • 37. validates_length_of
    • Ensures an attribute is the proper length
    © 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. validates_format_of
    • Ensures the format of an attribute matches regular expression.
      • Can be used to validate email addresses.
    © Vita Rara, Inc. class User < ActiveRecord::Base validates_format_of :email, :with => /^[wd]+$/ end
  • 39. validates_inclusion_of & validates_exclusion_of
    • Ensures that a value is or is not in a collection of options.
    © 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. validates_associated
    • Ensures that an associated model is valid prior to saving.
    © 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 user.save! #=> Throws RecordInvalid exception.
  • 41. Other Declarative Validations
    • validates_acceptance_of
    • validates_confirmation_of
    • validates_each
    • validates_size_of
    © Vita Rara, Inc. You can also create your own declarative validations that match your problem domain.
  • 42. Using Validation Callbacks
    • Sometimes validation is more complex than the declarative validations can handle.
      • Validation may relate to the semantics of your problem domain.
      • Validation may relate to more than one attribute.
    © 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. Using Validation Callbacks
    • Class methods for defining validation call backs:
      • validate :method_name
      • validate_on_update :method_name
      • validate_on_create :method_name
    • Instance methods for defining validations:
      • validate
      • validate_on_update
      • validate_on_create
    © Vita Rara, Inc.
  • 44. Model Life Cycle
  • 45. New Model Callbacks
    • ActiveRecord calls these methods prior to saving a new record:
      • before_validation
      • before_validation_on_create
        • validation is performed
      • after_validation
      • after_validation_on_create
      • before_save
      • before_create
        • ActiveRecord saves the record
      • after_create
      • after_save
    © Vita Rara, Inc.
  • 46. Existing Model Callbacks
    • ActiveRecord calls these methods prior to saving an existing record
      • before_validation
        • ActiveRecord performs validation
      • after_validation
      • before_save
      • before_update
        • ActiveRecord saves the record
      • after_update
      • after_save
    © Vita Rara, Inc.
  • 47. Destroy Model Callbacks
    • ActiveRecord calls these methods when destroying a model:
      • before_destroy
        • ActiveRecord performs the DELETE
      • after_destroy
    © Vita Rara, Inc.
  • 48. Callback Use Cases
    • Cleaning up attributes prior to saving
    • Starting followup processes
    • Sending notifications
    • Geocoding
    • Paranoia: Don’t delete anything just mark it deleted.
    • Clean up associated files, avatars, other assets.
    © Vita Rara, Inc.
  • 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. Observers
  • 51. Observers
    • Observers allow you to create classes that observe changes in your Models.
      • Observers allow you classes to focus on a single responsibility.
    • Observers can hook onto the standard rails life cycle call backs.
    © Vita Rara, Inc.
  • 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 #{model.class.name} created.&quot; , model) end def after_update (model) log_info( &quot;Update #{model.class.name}&quot; , model) end def after_destroy (model) log_info( &quot;Destroy #{model.class.name}&quot; , model) end private def log_info (model, info) log.info(info) log.info(model.inspect) end end
  • 53. Shameless Self Promotion
  • 54. Ruby and Rails Training
    • One day to three day programs.
    • Introduction to Ruby
    • Advanced Ruby
    • Introduction to Rails
    • Advanced Rails
    • Test Driven Development
    • Behavior Driven Development
    • Test Anything with Cucumber
    • Advanced Domain Modeling with ActiveRecord
    • Domain Driven Development with Rails
    © Vita Rara, Inc.
  • 55. Ruby on Rails Consulting
    • Full Life Cycle Project Development
      • Inception
      • Implementation
      • Deployment
      • Long Term Support
    • Ruby on Rails Mentoring
      • Get your team up to speed using Rails
    © Vita Rara, Inc.
  • 56. Contact Information
    • Mark Menard
      • [email_address]
      • http://www.vitarara.net /
      • 518 369 7356
    © Vita Rara, Inc.