Intro to Rails ActiveRecord

  • 3,476 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
3,476
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
57
Comments
0
Likes
6

Embeds 0

No embeds

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.