Rapid Application Development using Ruby on Rails

3,208 views

Published on

Rapid Application Development using Ruby on Rails

Published in: Technology
0 Comments
5 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,208
On SlideShare
0
From Embeds
0
Number of Embeds
30
Actions
Shares
0
Downloads
108
Comments
0
Likes
5
Embeds 0
No embeds

No notes for slide

Rapid Application Development using Ruby on Rails

  1. 1. Rapid Application Development using Ruby on Rails Presented By: Hisham Malik http://www.arkhitech.com 1
  2. 2. Day 1 - Overview/Quickstart • Overview of RoR • Setting up Environment • RoR Quick-start Demo • Ruby Language Fundamentals • Rails MVC • Live Demo • Under the Hood 2
  3. 3. Day 2 - Details • DB Migrations • MVC – ActiveRecord, ActionController, ActionView • Form Helpers • Routing 3
  4. 4. Day 3 - Optimizations • Testing • Ruby Language – More Details. • Active Records - More Details. • Live Demo Contd. • AJAX – Unobtrusive Javascripting (UJS) • Security 4
  5. 5. Day 4 – Supporting Gems • Cookies/Session • Remember me Example • Capistrano • Caching • Testing Contd. • Database Optimizations • Amazon S3 Storage – AWS::S3 • Attachments – Paperclip • Master/Slave Replication – Octopus • Grids – WiceGrid • Full-text Searching – Thinking Sphinx • Message Queues/Delayed Jobs • AJAX Contd. • Security Contd. • Testing Contd. • Quick Overview of Active Resource 5
  6. 6. Day 1 6
  7. 7. Ruby on Rails • Ruby on Rails is an open-source web framework that’s optimized for programmer happiness and sustainable productivity. • Lets you write beautiful code by favoring Convention over Configuration (CoC) and rapid development principle of Don't Repeat Yourself (DRY). 7
  8. 8. Ruby on Rails • Ruby • less and more readable code, • shorter development times, • simple but powerful, • no compilation cycle • Convention over configuration • almost no config files, • predefined directory structure, • naming conventions • less code, • easier maintenance 8
  9. 9. Ruby on Rails • Best practices: • MVC, • DRY, • Testing • Almost everything in Rails is Ruby code (SQL and JavaScript are abstracted) • Unobtrusive AJAX support. • Web services with REST. • Good community, tools, and documentation 9
  10. 10. Environment Check! • Ruby - 2.0.0 • ruby -v • Rails - 4.0.0 • rails -v 10
  11. 11. Installing Rails • Installing Rails • gem install rails #usually run as root user • If you’re using windows, it is suggested that you install a Linux virtual machine and use that for Rails development. • While Ruby and Rails themselves install easily, the supporting ecosystem often assumes you are able to build C-based rubygems and work in a command window. 11
  12. 12. Getting Up and Running Quickly - Demo 12
  13. 13. Creating Sample Application • rails new sample • You can see all of the switches that the Rails application builder accepts by running rails -h. 13
  14. 14. Generating User Resource • The argument of the scaffold command is the singular version of the resource name (in this case, User), together with optional parameters for the data model’s attributes 14
  15. 15. Generated User Pages/URL 15
  16. 16. Ruby “I always thought Smalltalk would beat Java. I just didn’t know it would be called ‘Ruby’ when it did”- Kent Beck 16
  17. 17. Ruby Principles • Focusing on the human • Dynamic Programming • Principle of Least Surprise • Principle of Succinctness • Lightweight Language • Choosing good names • Embedding hidden messages 17
  18. 18. Focusing on the human • Lets you focus on problems • Convention over Configuration • Lets you stay at a higher level of abstraction • Extensive class libraries • Provides garbage collection 18
  19. 19. Principle of Least Surprise • diff = ary1 – ary2 • union = ary1 + ary2 19
  20. 20. Principle of Succinctness • Developers shouldn't get stressed by time that is spent. • Quicker we program, the more we program • Faster we finish, better programmer we can be. • Writing less code, introducing less bugs, and hence be more productive! 20
  21. 21. The Ruby Language • Generic, interpreted, reflective, with garbage collection • Optimized for people rather than computers • More powerful than Perl, more object oriented than Python • Everything is an object. There are no primitive types. • Strong dynamic typing 21
  22. 22. Assignment • a, b = b, a # swapping values • a = 1; b = 1 • a = b = 1 • a += 1 # a = a + 1 • a, b = [1, 2] • a = b || c • a ||= b 22
  23. 23. Boolean Expressions • All objects evaluate to true except false and nil • false and true are the only instances of FalseClass and TrueClass • Boolean expressions return the last evaluated object • The "word" versions have lower precedence than the "symbol" versions. In fact, they have even lower precedence than assignment. • a and b or c <=> (a and b) or c • a = b and c <=> (a = b) and c • a = b && c <=> a = (b && c) • puts a if a = b # Using assignments in boolean expressions • a = true; b = false; a and b and c() # => c() is never inoked 23
  24. 24. Class Definition and Instantiation • class Person • def initialize(name) • @name = name • end • def say_hi • puts "#{@name} says hi" • end • end • andreas = Person.new("Andreas") • andreas.say_hi 24
  25. 25. Class Inheritance • class Programmer < Person • def initialize(name, favorite_ide) • super(name) • @favorite_ide = favorite_ide • end • # We are overriding say_hi in Person • def say_hi • super • puts "Favorite IDE is #{@favorite_ide}" • end • end • peter = Programmer.new("Peter", "TextMate") • peter.say_hi 25
  26. 26. Naming Conventions • class MyClass • method_name, dangerous_method!, • question_method?, setter_method= • MY_CONSTANT = 3.14 • local_variable = 3.14 • @instance_variable • @@class_variable • $global_variable 26
  27. 27. Methods • When invoking a method argument parenthesis are optional • Methods always have a receiver. The implicit receiver is self. • Methods are identified by their name only. No overloading on Argument signatures. • There are class methods and instance methods • Methods can be public, protected, or private • The last evaluated expression in a method is the return value • Arguments can have default values: def my_method(a, b = {}) 27
  28. 28. Instance Methods/Getters and Setters • class Person • def initialize(name) • self.name = name • end • def name • @name • end • def name=(name) • @name = name • end • end • person = Person.new("Andreas") • puts person.name • person.name = "David" • puts person.name 28
  29. 29. attr_accessor • class Person • attr_accessor :name • def initialize(name) • self.name = name • end • end • person = Person.new("Andreas") • puts person.name • person.name = "David" • puts person.name 29
  30. 30. attr_reader • class Person • attr_reader :name • def initialize(name) • @name = name • end • end • person = Person.new("Andreas") • puts person.name 30
  31. 31. Variable/Method Ambiguity Gotcha • class Person • attr_accessor :paid • def initialize • @paid = false • end • def make_payment • puts "making payment..." • paid = true • end • end • person = Person.new • person.make_payment • puts "paid=#{person.paid}" 31
  32. 32. Class Methods • class Person • def self.class_method • puts “class method invoked” • end • class << self • def class_method2; puts “class_method2”; end • def class_method3; puts “class_method3”; end • end • end • class << User • def class_method4; puts “class_method4”; end • end 32
  33. 33. Class Instance Methods • andreas = Person.new(“Andreas”) • def andreas.andreas_says_hi • “Andreas says hi” • end • andreas.andreas_says_hi 33
  34. 34. Idiom: Assignment with Boolean Operators • # Overly verbose: • user_id = nil • if comments • if comments.first • if comments.first.user • user_id = comments.first.user.id • end • end • end • # Idiomatic: • user_id = comments && comments.first && • comments.first.user && comments.first.user.id 34
  35. 35. Module • # Mixins - instead of multiple inheritance • module FullName • def full_name • "#{first_name} #{last_name}" • end • end • class Person • include FullName • end • Person.new("Peter", "Marklund").full_name 35
  36. 36. Modules as Namespaces • # Namespaces - to avoid name collissions • module MyApp • class Person • attr_accessor :name • def initialize(name) • self.name = name • end • end • end • MyApp::Person.new("Peter Marklund") 36
  37. 37. Modules vs Classes • Modules model characteristics or properties of entities or things. • Modules can’t be instantiated! • Module names tend to be adjectives (Comparable, • Enumerable etc.). • A class can mix in several modules. • Classes model entities or things. • Class names tend to be nouns. • A class can only have one super class • (Enumeration, Item etc.). 37
  38. 38. String Class • “ruby”.upcase + “ on “ + “rails”.capitalize • “Current time is: #,Time.now-n second line” • “I“ << “like” << “Ruby” • <<-END • Here we are creating multi-line document • document created at #{Time.now} • END • "Ruby is Great"*8,5+ # => “Great” • "Ruby is Great"*0..3+ == “Ruby” # => true • sprintf(“value of %s is %.2f”,”PI”,3.1416) 38
  39. 39. Array Class • a = *“Ruby”, 99, 3.14+ #Elements of array need not be of same type • a[1] == 99 • a << “Rails” #append element to the end of the array • *‘C’, ‘Java’, ‘Ruby’+ == %w,C Java Ruby- #same • *1, 2, 3+.map , |x| x**2 -.join(“, “) • [1, 2, 3].select { |x| x % 2 == 0 } # • [1, 2, 3].reject { |x| x < 3 } • [1, 2, 3].inject { |sum, i| sum + i } • [1, [2, 3]].flatten! # => [1, 2, 3] • ['C', 'Java', 'Ruby'].include?(language) • fruits = *‘apple’, ‘banana’+ • fruits += *‘apple’+ • fruits |= *‘apple’+ 39
  40. 40. Symbol Class • A Ruby symbol looks like a colon followed by characters. (:mysymbol) • A Ruby symbol is a thing that has both a number (integer) and a string. • The value of a Ruby symbol's string part is the name of the symbol, minus the leading colon. • A Ruby symbol cannot be changed at runtime. • Neither its string representation nor its integer representation can be changed at runtime. • Like most other things in Ruby, a symbol is an object. • When designing a program, you can usually use a string instead of a symbol. • Except when you must guarantee that the string isn't modified. • Symbol objects do not have the rich set of instance methods that String objects do. • After the first usage of :mysymbol all further useages of :mysymbol take no further memory -- they're all the same object. • Ruby symbols save memory over large numbers of identical literal strings. • Ruby symbols enhance runtime speed to at least some degree. 40
  41. 41. Hash Class • h = ,lang: ‘Ruby’, framework: ‘Rails’- • h*:lang+ == ‘Ruby’ • h[:perl] == nil • ENV = ,‘USER’ => ‘peter’, ‘SHELL’ => ‘/bin/bash'- • ENV.each ,|k, v| puts “#,k-=#,v-” - 41
  42. 42. Range Class • Two dots is inclusive, i.e. 1 to 5 • (1..5).each { |x| puts x**2 } • Three dots excludes the last item, i.e. 1 to 4 • (1...5).each { |x| puts x } • (1..3).to_a == [1, 2, 3] 42
  43. 43. Easier Conditional Statements • puts “Good Morning” if Time.now.hour < 12 • puts “Good Night” unless Time.now.hour < 20 43
  44. 44. Iterators: while, until, and for. Keywords: break and next• while count < 100 • puts count • count += 1 • end • # Statement modifier version of while • payment.make_request while (payment.failure? and payment.tries < 3) • for user in @users • next if user.admin? • if user.paid? • puts user • break • end • end • until count > 5 • puts count • count += 1 • end • # Statement modifier version of until • puts(count += 1) until count > 5 44
  45. 45. Case Statement • case x • when 0 • when 1, 2..5 • puts "Second branch" • when 6..10 • puts "Third branch" • when *[11, 12] • puts “Fourth branch” • when String: puts “Fifth branch” • when /d+.d+/ • puts “Sixth branch” • when x.downcase == “peter” • puts “Seventh branch” • else • puts "Eight branch" • end • end • end 45
  46. 46. Regular Expressions • puts 'matches' if ‘Ruby’ =~ /^(ruby|python)$/i • “GonRuby” =~ /Gos+(w+)/m; $1 == 'Ruby' • pattern = “.”; Regexp.new(Regexp.escape(pattern)) • 'I like Ruby'[/(like)/i, 1] == 'like' • 'I like Ruby'[/(like).*(ruby)/i,2] == 'Ruby' • 'I like Ruby'.gsub(/ruby/i) { |lang| lang.upcase } • line = 'I Go Ruby' • m, who, verb, what = *line.match(/^(w+)s+(w+)s+(w+)$/) • s, d, [0-9], w - space, digit, and word character classes • ?, *, +, {m, n}, {m,}, {m} - repetition 46
  47. 47. Exceptions • begin • raise(ArgumentError, “No file_name provided”) if !file_name • content = load_blog_data(file_name) • raise “Content is nil” if !content • rescue BlogDataNotFound • STDERR.puts "File #{file_name} not found" • rescue BlogDataConnectError • @connect_tries ||= 1 • @connect_tries += 1 • retry if @connect_tries < 3 • STDERR.puts "Invalid blog data in #{file_name}" • rescue Exception => exc • STDERR.puts "Error loading #{file_name}: #{exc.message}" • raise • end 47
  48. 48. Rails MVC 48
  49. 49. Models • A model represents the information (data) of the application and the rules to manipulate that data. • In Rails, models are primarily used for managing the rules of interaction with a corresponding database table. • In most cases, one table in your database will correspond to one model in your application. • The bulk of your application’s business logic will be concentrated in the models. 49
  50. 50. Views • Views represent the user interface of your application. • In Rails, views are often HTML files with embedded Ruby code that perform tasks related solely to the presentation of the data. • Views handle the job of providing data to the web browser or other tool that is used to make requests from your application. 50
  51. 51. Controllers • Controllers provide the “glue” between models and views. • In Rails, controllers are responsible for processing the incoming requests from the web browser, interrogating the models for data, and passing that data on to the views for presentation. 51
  52. 52. Rails - MVC 52
  53. 53. MVC Request Cycle in Detail The browser issues a request for the /users URL. Rails routes /users to the index action in the Users controller. The index action asks the User model to retrieve all users (User.all). The User model pulls all the users from the database. The User model returns the list of users to the controller. The controller captures the users in the @users variable, which is passed to the indexview. The view uses embedded Ruby to render the page as HTML. The controller passes the HTML back to the browser. 53
  54. 54. Creating Blog Project 54
  55. 55. Creating Blog Application • rails new blog • You can see all of the switches that the Rails application builder accepts by running rails -h. • Rails applications manage gem dependencies with Bundler. • bundle install • #create stubs in bin/ folder • bundle install --binstubs 55
  56. 56. Configuring Database • The database to use is specified in a configuration file, config/database.yml. By default SQLite3 is supported which is a serverless database. • The file contains sections for three different environments in which Rails can run by default: • The development environment is used on your development computer as you interact manually with the application • The test environment is used to run automated tests • The production environment is used when you deploy your application for the world to use. • Creating the Database • bundle exec rake db:create 56
  57. 57. Using MySQL (optional) • Add Mysql in Gemfile and run bundle install • gem "mysql", "~> 2.9.1" • Update Contents of database.yml file: • development: • adapter: mysql • encoding: utf8 • reconnect: false • encoding: utf8 • database: blog_development • pool: 5 • username: root • password: • socket: /tmp/mysql.sock 57
  58. 58. Hello Rails • rails server 58
  59. 59. Rake Rake is Ruby make, a make-like language written in Ruby. Rails uses Rake extensively, especially for the innumerable little administrative tasks necessary when developing database-backed web applications. Rake lets you define a dependency tree of tasks to be executed. Rake tasks are loaded from the file Rakefile You can put your own tasks under lib/tasks bundle exec rake -T bundle exec rake -T db #See a list of database tasks 59
  60. 60. Useful Rake Tasks • about • rake about gives information about version numbers for Ruby, RubyGems, Rails, the Rails subcomponents, your application's folder, the current Rails environment name, your app's database adapter, and schema version. It is useful when you need to ask for help, check if a security patch might affect you, or when you need some stats for an existing Rails installation. • assets • You can precompile the assets in app/assets using rake assets:precompile and remove those compiled assets using ‘rake assets:clean’. • db • The most common tasks of the db: Rake namespace are ‘migrate’ and ‘create’, and it will pay off to try out all of the migration rake tasks (‘up’, ‘down’, ‘redo’, ‘reset’). ‘rake db:version’ is useful when troubleshooting, telling you the current version of the database. • doc • If you want to strip out or rebuild any of the Rails documentation, the doc:namespace has the tools. Stripping documentation is mainly useful for slimming your codebase, like if you’re writing a Rails application for an embedded platform. 60
  61. 61. Useful Rake Tasks • notes • These tasks will search through your code for commented lines beginning with “FIXME”, “OPTIMIZE”, or “TODO”. • stats • Gives summary statistics about your code • routes • Lists all your defined routes • secret • Will give you a pseudo-random key to use for your session secret. • time:zones:all • Lists all the timezones Rails knows about. 61
  62. 62. Live Demo 62
  63. 63. Setting up Home Page • Create Home controller with index action • rails generate controller home index • Edit app/views/home/index.html.erb • <h1>Hello Rails Workshop Participants!</h1> • Remove public/index as static files have precedence over dynamic content generated. • rm public/index.html • Setup Rails to point to your home. • Open the file config/routes.rb and uncomment/add the following line: • root :to => "home#index" 63
  64. 64. Scaffolding • Rails scaffolding is a quick way to generate some of the major pieces of an application. • If you want to create the models, views, and controllers for a new resource in a single operation, scaffolding is the tool for the job. • rails generate scaffold Post name:string title:string content:text 64
  65. 65. Creating Post Resource • rails generate scaffold Post name:string title:string content:text • bundle exec rake db:migrate 65
  66. 66. Linking Posts to Home Page • Open app/views/home/index.html.erb and modify it as follows: • <h1>Hello, Rails Workshop Participants!</h1> <%= link_to "My Blog", posts_path %> • The link_to method is one of Rails’ built-in view helpers. It creates a hyperlink based on text to display and where to go – in this case, to the path for posts. 66
  67. 67. Under The Hood 67
  68. 68. User Resource Limitations • No data validations. Our User model accepts data such as blank names and invalid email addresses without complaint. • No authentication. We have no notion signing in or out, and no way to prevent any user from performing any operation. • No layout. There is no consistent site styling or navigation. • No real understanding. 68
  69. 69. Posts Model • #app/models/post.rb • class Post < ActiveRecord::Base • end 69
  70. 70. Post Model • Adding some basic validation • class Post < ActiveRecord::Base • validates :name, presence: true • validates :title, presence: true, length: { minimum: 5 } • end • More on validation later! 70
  71. 71. Post Controller app/controller/posts_controller.rb 71
  72. 72. Index Method • Result stored in an instance variable • @posts = Post.all • The respond_to block handles both HTML and XML calls to this action • The HTML format looks for a view in app/views/posts/ with a name that corresponds to the action name, e.g, app/views/posts/index.html.erb 72
  73. 73. View: Index • app/views/posts/index.html.erb • View iterates over the contents of the @posts array to display content and links. • link_to builds a hyperlink to a particular destination • edit_post_path and new_post_path are helpers that Rails provides as part of RESTful routing. • A application specific layout is used for all the controllers and can be found in app/views/layouts/application.html.erb 73
  74. 74. New Method • Result stored in an instance variable • @posts = Post.all • The respond_to block handles both HTML and XML calls to this action • The HTML format looks for a view in app/views/posts/ with a name that corresponds to the action name, e.g, app/views/posts/new.html.erb 74
  75. 75. View: New • app/views/posts/new.html.erb • View renders a form residing in app/views/posts/_form.html.erb • <%= render 'form' %> • app/views/posts/_form.html.erb • The form_for block is used to create an HTML form • The form_for block is also smart enough to work out if you are doing a New Post or an Edit Post action, and will set the form action tags and submit button names appropriately in the HTML output. 75
  76. 76. Comments can be given for Post? 76
  77. 77. Generating a Referencing Model • To add a connection between your tables we want to add references in our model. • References are comparable to foreign keys • rails generate model Comment commenter:string body:text post:references • Files generated • app/models/comment.rb – The model • db/migrate/20100207235629_create_comments.rb – The migration • test/unit/comment_test.rb and • test/fixtures/comments.yml – The test harness. 77
  78. 78. Linking Models • Edit app/models/post.rb to add the other side of the association • has_many :comments • Add route for comments in config/routes.rb • resources :posts do • resources :comments • end • More on associations and routes later! 78
  79. 79. Comments Controller? 79
  80. 80. Generating Comments Controller • rails generate controller Comments • This creates six files and one empty directory: • app/controllers/comments_controller.rb – The controller • app/helpers/comments_helper.rb – A view helper file • test/controllers/comments_controller_test.rb – The functional tests for the controller • test/helpers/comments_helper_test.rb – The unit tests for the helper • app/views/comments/ – Views of the controller are stored here • app/assets/javascripts/welcome.js.coffee - CoffeeScript for the controller • app/assets/stylesheets/welcome.css.scss - Cascading style sheet for the controller 80
  81. 81. Add Comments to Post • Comments should be added where the post is shown. 81
  82. 82. Add Comments to Post • Append comments form to Post show template (app/views/posts/show.html.erb) • <h2>Add a comment:</h2> • <%= form_for([@post, @post.comments.build]) do |f| %> • <div class="field"> • <%= f.label :commenter %><br /> • <%= f.text_field :commenter %> • </div> • <div class="field"> • <%= f.label :body %><br /> • <%= f.text_area :body %> • </div> • <div class="actions"> • <%= f.submit %> • </div> • <% end %> • • <%= link_to 'Edit Post', edit_post_path(@post) %> | • <%= link_to 'Back to Posts', posts_path %> | 82
  83. 83. Handle Comments to Post • Add create method in CommentsController • class CommentsController < ApplicationController • def create • @post = Post.find(params[:post_id]) • @comment = @post.comments.create(params[:comment]) • redirect_to post_path(@post) • end • end 83
  84. 84. Show Comments • Add section to show comments for posts to Post show template (app/views/posts/show.html.erb) • <h2>Comments</h2> • <% @post.comments.each do |comment| %> • <p> • <b>Commenter:</b> • <%= comment.commenter %> • </p> • <p> • <b>Comment:</b> • <%= comment.body %> • </p> • <% end %> 84
  85. 85. Day 2 85
  86. 86. Rails Console • command-line tool that lets you execute Ruby code in the context of your application. • Allows you to work with your models/functions • >> p = Post.new(:content => "A new post") • => #<Post id: nil, name: nil, title: nil, • content: "A new post", created_at: nil, • updated_at: nil> • >> p.save • => false • >> p.errors • => #<OrderedHash { :title=>["can't be blank", • "is too short (minimum is 5 characters)"], • :name=>["can't be blank"] }> • >> helper.text_field_tag(:person,:name) • => "<input id="person" name="person" type="text" value="name" />" 86
  87. 87. Migrations A way to evolve your database schema over time Migrations use a database independent Ruby API Migration classes extend ActiveRecord::Migration and have an up and a down method Migrations are stored in files in db/migrate, one for each migration class. Running migrations: bundle exec rake db:migrate 87
  88. 88. ActiveRecord::Migration • class CreatePosts < ActiveRecord::Migration • def self.up • create_table :posts do |t| • t.string :name • t.string :title • t.text :content • t.timestamps • end • end • def self.down • drop_table :posts • end • end 88
  89. 89. Migration Methods • create_table(name, options) • Creates a table called name and makes the table object available to a block that can then add columns to it, following the same format as add_column. See example above. The options hash is for fragments like “DEFAULT CHARSET=UTF-8” that are appended to the create table definition. • drop_table(name) • Drops the table called name. • rename_table(old_name, new_name) • Renames the table called old_name to new_name. • add_column(table_name, column_name, type, options): • Adds a new column to the table called table_name named column_name specified to be one of the following types: :primary_key, :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean. A default value can be specified by passing an options hash like { :default => 11 }. Other options include :limit and :null (e.g. { :limit => 50, :null => false }) 89
  90. 90. Migration Methods • rename_column(table_name, column_name, new_column_name) • Renames a column but keeps the type and content. • change_column(table_name, column_name, type, options) • Changes the column to a different type using the same parameters as add_column. • remove_column(table_name, column_name) • Removes the column named column_name from the table called table_name. • add_index(table_name, column_names, options) • Adds a new index with the name of the column. Other options include :name and :unique (e.g. { :name => "users_name_index", :unique => true }). • remove_index(table_name, index_name) • Removes the index specified by index_name. 90
  91. 91. Generating Migrations w/Model • rails generate model Product name:string description:text • 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 91
  92. 92. Generating Migration to Add Columns • rails generate migration • Add<Desc>To<Model> - Add given columns to table specified by Model • rails generate migration AddDetailsToProducts part_number:string price:decimal • class AddDetailsToProducts < ActiveRecord::Migration • def self.up • add_column :products, :part_number, :string • add_column :products, :price, :decimal • end • def self.down • remove_column :products, :price • remove_column :products, :part_number • end • end 92
  93. 93. Generating Migrations to Remove Columns • rails generate migration • Remove<Desc>To<Model> - Remove given columns from table specified by Model • rails generate migration RemovePartNumberFromProducts part_number:string • class RemovePartNumberFromProducts < ActiveRecord::Migration • def self.up • remove_column :products, :part_number • end • def self.down • add_column :products, :part_number, :string • end • end 93
  94. 94. Creating Table • create_table :products do |t| • t.string :name • end • The types supported by Active Record are :primary_key, :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean. • You may use a type not in this list as long as it is supported by your database (for example, “polygon” in MySQL), but this will not be database agnostic and should usually be avoided. • create_table :products do |t| • t.column :name, 'polygon', :null => false • end • Available options are (none of these exists by default): • :limit - Requests a maximum column length. This is number of characters for :string and :text columns and number of bytes for :binary and :integer columns. • :default - The column’s default value. Use nil for NULL. • :null - Allows or disallows NULL values in the column. This option could have been named :null_allowed. • :precision - Specifies the precision for a :decimal column. • :scale - Specifies the scale for a :decimal column. 94
  95. 95. Updating Tables • change_table :products do |t| • t.remove :description, :name • t.string :part_number • t.index :part_number • t.rename :upccode, :upc_code • end • remove_column :products, :description • remove_column :products, :name • add_column :products, :part_number, :string • add_index :products, :part_number • rename_column :products, :upccode, :upc_code 95 functio nally equival ent
  96. 96. Managing Migrations • Running the db:migrate also invokes the db:schema:dump task, which will update your db/schema.rb file to match the structure of your database. • If you specify a target version, Active Record will run the required migrations (up or down) until it has reached the specified version • rake db:migrate VERSION=20080906120000 • To rollback migrations: • rake db:rollback #run the down method from latest migration. • rake db:rollback STEP=3 #run the down method from the last 3 migrations. • To rollback and redo migrations: • rake db:migrate:redo #run the down method from latest migration. • rake db:migrate:redo STEP=3 #run the down method from the last 3 migrations. • Load database from current schema: • db:setup # • db:reset #drops existing database first 96
  97. 97. Handling Model Changes • Information about model columns is cached • You can force Active Record to re-read the column information with the reset_column_information method • class AddPartNumberToProducts < ActiveRecord::Migration • def self.up • add_column :product, :part_number, :string • Product.reset_column_information • ... • end • def self.down • ... • end • end 97
  98. 98. Seed Data • Adding seed data into migrations isn’t the best way to add initial data. • Put all initial data in db/seeds.rb • *‘Lahore’, ‘Karachi’, ‘Islamabad’+.each do |city| • City.find_or_create_by_name(city) • end • rake db:seed 98
  99. 99. Boolean Attributes Everything except nil and false is true in Ruby However, in MySQL boolean columns are char(1) with values 0 or 1, both of which are true in Ruby. Instead of saying user.admin, say user.admin? When you add the question mark, false is the number 0, one of the strings ‘0’, ‘f ’, ‘false’, or ‘’, or the constant false 99
  100. 100. Active Record 100
  101. 101. Fundamentals One database table maps to one Ruby class Ruby classes live under app/models and extend ActiveRecord::Base Table names are plural and class names are singular Database columns map to attributes, i.e. get and set methods, in the model class All tables have an integer primary key called id Database tables are created with migrations 101
  102. 102. Methods where select group order limit offset joins includes lock readonly from having 102
  103. 103. Retrieving Single Object • Using Primary Key • client = Client.find(10) • SELECT * FROM clients WHERE (clients.id = 10) • first • client = Client.first • SELECT * FROM clients LIMIT 1 • last • client = Client.last • SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 103
  104. 104. Retrieving Multiple Objects • Using Multiple Primary Keys • client = Client.find(1, 10) # Or even Client.find([1, 10]) • SELECT * FROM clients WHERE (clients.id IN (1,10)) • find_each • find_each fetches rows in batches of 1000 and yields them one by one • User.find_each(:batch_size => 5000, :start => 2000) do |user| • NewsLetter.weekly_deliver(user) • end • find_in_batches • Analogous to find_each, but it yields arrays of models instead • Invoice.find_in_batches() do |invoices| • export.add_invoices(invoices) • end 104
  105. 105. Conditions • Pure String Conditions • Client.where("orders_count = '2'") • Array Conditions • Client.where("orders_count = ? AND locked = ?", params[:orders], false) • Client.where("created_at >= :start_date AND created_at <= :end_date", {:start_date => params[:start_date], :end_date => params[:end_date]}) 105
  106. 106. Action Controller • Controllers are Ruby classes that live under app/controllers • Controller classes extend ActionController::Base • An action is a public method and/or a corresponding view template 106
  107. 107. The Rails Configuration Object # Defined in railties/lib/initializer.rb Rails.root Rails.env Rails.version Rails.configuration.load_paths Rails.configuration.gems Rails.configuration.plugins Rails.configuration.time_zone Rails.configuration.i18n 107
  108. 108. Controller Environment • cookies*:login+ = , :value => “peter”, :expires => 1.hour.from_now • headers*‘Content-Type’+ = ‘application/pdf; charset=utf-8’ • params • request: env, request_uri, get?, post?, xhr?, remote_ip • response • session • logger.warn(“Something pretty bad happened”) 108
  109. 109. Rendering Response • A response is rendered with the render command • An action can only render a response once • Rails invokes render automatically if you don’t • Redirects are made with the redirect_to command • You need to make sure you return from an action after an invocation of render or redirect_to 109
  110. 110. Render Examples • render :text => “Hello World” • render :action => “some_other_action” • render :partial => “top_menu” • render :xml => xml_string • Options: :status, :layout, :content_type • send_file(“/files/some_file.pdf ”) 110
  111. 111. Redirect • Tells the browser to send a new request for a different URL redirect_to :back • redirect_to(“/help/order_entry.html”) • redirect_to :controller => ‘blog’, :action => ‘list’ • redirect_to photos_path, :status => 301 111
  112. 112. Flash • The flash is a way to set a text message to the user in one request and then display it in the next (typically after a redirect) • The flash is stored in the session • flash[:notice], flash[:error] • flash.now*:notice+ = “Welcome” unless flash*:notice+ • flash.keep(:notice) 112
  113. 113. Flash.now • There’s a subtle difference between flash and flash.now. • The flash variable is designed to be used before a redirect, and it persists on the resulting page for one request—that is, it appears once, and disappears when you click on another link. • If we don’t redirect, and instead simply render a page, the flash message persists for two requests: • it appears on the rendered page but is still waiting for a “redirect” (i.e., a second request), and • thus appears again if you click a link. • To avoid this weird behavior, when rendering rather than redirecting we use flash.now instead of flash. • The flash.now object is specifically designed for displaying flash messages on rendered pages. • If you ever find yourself wondering why a flash message is showing up where you don’t expect it, chances are good that you need to replace flash with flash.now. 113
  114. 114. Displaying Flash • #app/views/layouts/application.html.erb • <!DOCTYPE html> • <html> • ... • <%= render 'layouts/header' %> • <section class="round"> • <% flash.each do |key, value| %> • <div class="flash <%= key %>"><%= value %></div> • <% end %> • <%= yield %> • </section> • ... • </html> 114
  115. 115. ActionView Basics • The controller decides which template and/or partial and layout to use in the response • Templates use helper methods to generate links, forms, and JavaScript, and to format text. • A response is rendered with the render command • An action can only render a response once • Rails invokes render automatically if you don’t • Redirects are made with the redirect_to command • You need to make sure you return from an action after an invocation of render or redirect_to 115
  116. 116. Templates • Each action in the controller can have an associated template. • Templates belonging to a certain controller are under app/view/controller_name. For example, templates for GiftController would be under app/views/gift • Templates shared across controllers are put under app/views/shared. You can render them with render :template => ‘shared/my_template’ 116
  117. 117. Template Environment • Templates have access to the controller objects flash, headers, logger, params, request, response, and session. • Instance variables (i.e. @variable) in the controller are available in templates • The current controller is available as the attribute controller. 117
  118. 118. Partials • Partials are templates that render a part of a page, such as a header or footer, or a menu, or a listing of articles • Partials help promote reuse of page elements • Partials work just like page templates (views) and run in the same environment. They also live in the same directory as page templates. • The filenames of partials always start with an underscore. 118
  119. 119. Passing Variables to Partials • Controller instance variables are available in partials • If you pass :object => @an_article to the render command then that variable will be available in a local variable in the partial with the same name as the partial. • If there is an instance variable with the same name as the partial then it will be available as a local variable in the partial with the same name, i.e. @article = Article.find(1); render :partial =>‘article’. • You can pass any objects into local variables in the partial with the :locals argument: render :partial => ‘article’, :locals => , :author => @author, :options => @options } 119
  120. 120. Partials and Collections • <% for article in @articles %> • <%= render :partial => ‘article’, :object => article %> • <% end %> • Can be written more concisely with the :collections argument: • <%= render :partial => ‘article’, :collection => @articles %> 120
  121. 121. Layouts • Layouts are templates under app/views/layouts that contain common page elements around pages such as headers, footers, menus etc. • The layout template contains the invocation <%=yield %> which will render the action output. 121
  122. 122. Layout Selection • To find the current layout, Rails • first looks for a file in app/views/layouts with the same base name as the controller. For example, rendering actions from the PhotosController class will use app/views/layouts/photos.html.erb (or app/views/layouts/photos.builder). • If there is no such controller-specific layout, Rails will use app/views/layouts/application.html.erb or app/views/layouts/application.builder. • If there is no .erb layout, Rails will use a .builder layout if one exists. Rails also provides several ways to more precisely assign specific layouts to individual controllers and actions. 122
  123. 123. Specifying Layout • Layout can be specified on a per-controller basis • class ProductsController < ApplicationController • layout "inventory" • #... • end • Conditional Layouts • class ProductsController < ApplicationController • layout "product", :except => [:index, :rss] • end 123
  124. 124. Selecting Layout at Runtime • class ProductsController < ApplicationController • layout :products_layout • def show • @product = Product.find(params[:id]) • end • private • def products_layout • @current_user.special? ? "special" : "products" • end • end 124
  125. 125. Selecting Layout at Runtime • class BlogController < ActionController::Base • layout :determine_layout • private • def determine_layout • user.admin? ? “admin” : “standard” • end • end 125
  126. 126. Yield/Content_for • Layout: • <html> • <head> • <%= yield :head %> • </head> • <body> • <%= yield %> • </body> • </html> 126
  127. 127. Yield/Content_for/Provide • <%= content_for :head do %> • <title>A simple page</title> • <% end %> • <%= provide :head %> • <p>Hello, Rails!</p> 127
  128. 128. Best Practice • Don’t put SQL and too much code in your controllers/views - it’s a code smell, and maybe the most common design mistake Rails developers make. Actions should be 3-4 lines that script business objects. • Goal is fat models and skinny controllers. • Always access data via the logged in user object (i.e. current_user.visits.recent). 128
  129. 129. Helpers • Helpers are Ruby modules with methods that are available in your templates. • Helpers can avoid duplication and minimize the amount of code in your templates. • By default each controller has a corresponding helper file at app/helpers/controller_name_helper.rb • To test ActionView helpers in rails console, use • include ActionView::Helpers 129
  130. 130. AssetTagHelper • Provide methods for generating HTML that links views to feeds, JavaScript, stylesheets, images, videos and audios. • http://api.rubyonrails.org/classes/ActionView/Helpers/AssetTagH elper.html • auto_discovery_link_tag • <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/action" /> • javascript_include_tag "xmlhr" • <script type="text/javascript" src="/javascripts/xmlhr.js"></script> • stylesheet_link_tag("application") • <link href="/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" type="text/css" /> 130
  131. 131. AutoDiscovery/Javascript Tags - Examples • <%= auto_discovery_link_tag(:rss, {:action => "feed"},{:title => "RSS Feed"}) %> • <%= javascript_include_tag "main", "columns", :cache => true %> • <%= javascript_include_tag "http://example.com/main.js" %> • <%= javascript_include_tag :defaults %> • <%= javascript_include_tag :all, :recursive => true %> 131
  132. 132. Stylesheet Asset Tag - Examples • <%= stylesheet_link_tag "main", "/photos/columns" %> • <%= stylesheet_link_tag "http://example.com/main.css" %> • <%= stylesheet_link_tag "main_print", :media => "print" %> • <%= stylesheet_link_tag :all, :recursive => true %> • <%= stylesheet_link_tag "main", "columns", :cache => true %> 132
  133. 133. Image/Video/Audio Asset Tags Examples • <%= image_tag "home.gif", :onmouseover => "menu/home_highlight.gif", :alt => “Home” %> • <%= video_tag "movie.ogg", :poster =>'movie_start.png', :autoplay => false, :loop => false, :controls => true, :autobuffer => true %> • <%= audio_tag "music/first_song.mp3", :autoplay => false, :controls => true, :autobuffer => true %> 133
  134. 134. AssetTagHelper • image_tag("rails.png") • <img alt="Rails" src="/images/rails.png?1230601161" /> • video_tag("trailer") • <video src="/videos/trailer" /> • audio_tag("sound.wav") • <audio src="/audios/sound.wav" /> 134
  135. 135. TextHelper • Provides common-use text manipulation methods • http://api.rubyonrails.org/classes/ActionView/Helpers/TextHelper.ht ml • truncate("Once upon a time in a world far far away", :length => 17) • highlight('You searched for: rails', 'rails') • excerpt('This is an example', 'an', :radius => 5) • pluralize(2, 'person') • word_wrap('Once upon a time', 4) • simple_format("Here is some basic text...n...with a line break.") • auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com") • <tr class="<%= cycle("even", "odd") -%>"> 135
  136. 136. UrlHelper • Provides an easy way to create dynamic urls based on • http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.ht ml • url_for(:controller => 'posts',:action => 'show', :only_path => false) • http://localhost:3000/posts/show • link_to "Other Site", "http://www.rubyonrails.org/", :confirm => "Sure?" • link_to "Post", :controller => "posts", :action => "show", :id => @post • <a href="/posts/show/1">Post</a> • link_to "Delete Post", , :controller => ‘posts’, :action => "delete", :id => 1 }, :method=> :delete • <a href='/posts/delete' rel="nofollow" data-method="delete" data-confirm="Are you sure?">Delete Post</a> • link_to_unless_current("Home", { :action => "index" }) 136
  137. 137. UrlHelper • button_to "New", :action => "new" • <form method="post" action="/controller/new" class="button_to"> • <div><input value="New" type="submit" /></div> • </form>" 137
  138. 138. Protect Email Addresses • Encoding emails makes it more difficult for web spiders to lift email addresses off of a site • mail_to "me@domain.com", "My email", :encode => "javascript" • mail_to "me@domain.com", "My email", :encode => "hex" 138
  139. 139. Form Helpers • Rails deals away with complexities of form controls naming and their attributes by providing view helpers for generating form markup. • Developers should know all the differences between similar helper methods before putting them to use as each has a different use case. 139
  140. 140. form_tag • Without any params, the default path is current page and default method used is post. • First parameter can be a path, or a hash of URL parameters • Second parameter is a set of options • <%= form_tag(search_path, :method => "get") do %> • <%= label_tag(:q, "Search for:") %> • <%= text_field_tag(:q) %> • <%= submit_tag("Search") %> • <% end %> • <%= form_tag({:controller => "people", :action => "search"}, :method => "get", :class => "nifty_form") do %>...<% end %> 140
  141. 141. *_tags • text_field_tag(:query) • <input id="query" name="query" type="text" /> • check_box_tag(:married) • <input id="married" name="married" type="checkbox" value="1" /> • radio_button_tag(:age, "child") • <input id="age_child" name="age" type="radio" value="child" /> • label_tag(:age_child, "I am younger than 21") • <label for="age_child">I am younger than 21</label> • text_area_tag(:message, "Hi, nice site", :size => "24x6") • <textarea id="message" name="message" cols="24" rows="6">Hi, nice site</textarea> • password_field_tag(:password) • <input id="password" name="password" type="password" /> • hidden_field_tag(:parent_id, "5") • <input id="parent_id" name="parent_id" type="hidden" value="5" /> 141
  142. 142. Form Helpers for Model • Editing or creating a particular model object is a common task for a form. • Rails provides helpers tailored to ensure the correct parameter name is used for consumption by controller. • These helpers lack the _tag suffix, for example text_field, text_area. • The first argument is the name of an instance variable and the second is the name of a method (usually an attribute) to call on that object. • text_field(:person, :name) #assuming controller has defined @person • <input id="person_name" name="person[name]" type="text" value="Hisham"/> • Upon form submission the value entered by the user will be stored in params[:person][:name]. • The params[:person] hash is suitable for passing to Person.new or, if @person is an instance of Person, @person.update_attributes. 142
  143. 143. Parameter Naming Convention • Rails converts name-value pairs of html forms to Hashes/Arrays • <input id="person_name" name="person[name]" type="text" value="Hisham"/> • {'person' => {'name' => 'Henry'}} • <input id="person_address_city" name="person[address][city]" type="text" value="Lahore"/> • {'person' => {'address' => {'city' => 'Lahore'}}} • <input name="person[phone_number][]" type="text" value="0321"/><input name="person[phone_number][]" type="text" value="87654321"/> • {'person' => {'phone_number' => ['0321', '87654321']}} • <input name="addresses[][line1]" type="text"/><input name="addresses[][line2]" type="text"/><input name="addresses[][city]" type="text"/> 143
  144. 144. Binding Form to Model Object: form_for • <%= form_for @article, :url => { :action => "create" }, :html => {:class => "nifty_form"} do |f| %> • <%= f.text_field :title %> • <%= f.text_area :body, :size => "60x12" %> • <%= submit_tag "Create" %> • <% end %> • First parameter can be name of model (:article), or model object itself • :url and :html are optional params • If :url is not specified, then Rails automatically adjust for between editing or creating based on whether model object is new or existing record (@article.new_record?) 144
  145. 145. fields_for • Generates proper names for nested attributes • <%= form_for @person do |person_form| %> • <%= person_form.text_field :name %> • <% for address in @person.addresses %> • <%= person_form.fields_for address, :index => address do |address_form|%> • <%= address_form.text_field :city %> • <% end %> • <% end %> • <% end %> • #output • <form action="/people/1" class="edit_person" id="edit_person_1" method="post"> • <input id="person_name" name="person[name]" type="text" /> • <input id="person_address_23_city" name="person[address][23][city]" type="text" /> • <input id="person_address_45_city" name="person[address][45][city]" type="text" /> • </form> 145
  146. 146. Select/Options • select_tag(:city_id, '<option value="1">Lisbon</option>...') • select_tag(:city_id, options_for_select(...)) • options_for_select([['Lahore', 1], ['Karachi', 2], ...], 1) # last parameter is the selected option • options_from_collection_for_select(City.all, :id, :name) • select(:person, :city_id, [['Lahore', 1], ['Karachi', 2], ...]) • selected option is the one that model object has • collection_select(:person, :city_id, City.all, :id, :name) 146
  147. 147. Date/Time/Timezone Helpers • time_zone_select(:person, :time_zone) • time_zone_options_for_select • select_date Date.today, :prefix => :birth_date • <select id="birth_date_year" name="birth_date[year]"> ... </select> • <select id="birth_date_month" name="birth_date[month]"> ... </select> • <select id="birth_date_day" name="birth_date[day]"> ... </select> • date_select :person, :birth_date • select_time Time.now, :prefix => :arrival_time • time_select :flight, :arrival_time • select_datetime, datetime_select 147
  148. 148. Uploading Files • The most important thing to remember with file uploads is that the form’s encoding MUST be set to “multipart/form-data”. • #view • <%= form_tag({:action => :upload}, :multipart => true) do %> • <%= file_field_tag 'picture' %> • <% end %> • <%= form_for @person, :html => {:multipart => true} do |f| %> • <%= f.file_field :picture %> • <% end %> • #controller • def upload • uploaded_io = params[:person][:picture] • File.open(Rails.root.join('public', 'uploads', uploaded_io.original_filename), 'w') do |file| • file.write(uploaded_io.read) • end • end 148
  149. 149. Routing 149
  150. 150. Routing Options • Resource Routing • Non-Resourceful Routes 150
  151. 151. Resource Routing • Allows you to quickly declare all of the common routes for a given resourceful controller. • Instead of declaring separate routes for your index, show, new, edit, create, update and destroy actions, a resourceful route declares them in a single line of code. • resources :photos • resources :photos, :books, :videos 151
  152. 152. Paths and URLs • Creating a resourceful route will also expose a number of helper functions in your application. • photos_path returns /photos • new_photo_path returns /photos/new • edit_photo_path(id) returns /photos/:id/edit (for instance, edit_photo_path(10) returns /photos/10/edit) • photo_path(id) returns /photos/:id (for instance, photo_path(10) returns /photos/10) • Each of these helpers has a corresponding _url helper (such as photos_url) which returns the same path prefixed with the current host, port and path prefix. 152
  153. 153. Singular Resources • Sometimes, you have a resource that clients always look up without referencing an ID. • For example, you would like /profile to always show the profile of the currently logged in user. • In this case, you can use a singular resource to map /profile (rather than /profile/:id) to the show action. • resource :geocoder 153
  154. 154. Singular Resources Paths and URLs • A singular resourceful route generates these helpers: • new_geocoder_path returns /geocoder/new • edit_geocoder_path returns /geocoder/edit • geocoder_path returns /geocoder • As with plural resources, the same helpers ending in _url will also include the host, port and path prefix. 154
  155. 155. Nested Resources • Common to have resources that are logically children of other resources. • class Magazine < ActiveRecord::Base • has_many :ads • end • class Ad < ActiveRecord::Base • belongs_to :magazine • end • resources :magazines do • resources :ads • end 155
  156. 156. Restricting Routes • You can use the :only and :except options to fine-tune this behavior. • resources :photos, :only => [:index, :show] • resources :photos, :except => :destroy 156
  157. 157. Resource Scope and Name Collisions • Namespace allows you to to group a number of similar controllers together to in a sub-folder and avoid name collisions. • For example, you might group a number of administrative controllers under an Admin:: namespace. You would place these controllers under the app/controllers/admin directory, and you can group them together in your router: • namespace :admin do • resources :posts • end • functionality equivalent to: • scope :path => :admin, :module => :admin do • resources :posts • end • Following snippets will create route helpers such as admin_photos_path, new_admin_photo_path avoiding name collisions: • scope "admin" do • resources :photos, :as => "admin_photos" • end • resources :photos • scope "admin", :as => "admin" do • resources :photos, :accounts • end 157
  158. 158. Constraints • You can use the :via option to constrain the request to one or more HTTP method • match 'photos/show' => 'photos#show', :via => [:get, :post] • You can use the :constraints option to enforce a format for a dynamic segment • match 'photos/:id' => 'photos#show', :constraints => { :id => /[A-Z]d{5}/ } • match 'photos/:id' => 'photos#show', :id => /[A-Z]d{5}/ • You can also constrain a route based on any method on the Request object that returns a String • match "photos", :constraints => {:subdomain => "admin"} 158
  159. 159. Non-Resourceful Routes • While you should usually use resourceful routing, there are still many places where the simpler routing is more appropriate. • match ':controller/:action/:id/:user_id' • match ':controller(/:action(/:id))', :controller => /admin/[^/]+/ • Anything other than :controller or :action will be available to the action as part of params • Possible to supply default :controller and :action • match 'photos/:id' => 'photos#show' 159
  160. 160. Representational State Transfer (REST) • The uniform interface that any REST interface must provide is considered fundamental to the design of any REST service. • Identification of resources • Individual resources are identified in requests, for example using URIs in web-based REST systems. The resources themselves are conceptually separate from the representations that are returned to the client. For example, the server does not send its database, but rather, perhaps, some HTML, XML or JSON that represents some database records expressed. • Manipulation of resources through these representations • When a client holds a representation of a resource, including any metadata attached, it has enough information to modify or delete the resource on the server, provided it has permission to do so. • Self-descriptive messages • Each message includes enough information to describe how to process the message. For example, which parser to invoke may be specified by an Internet media type (previously known as a MIME type). Responses also explicitly indicate their cacheability. • Hypermedia as the engine of application state • Clients make state transitions only through actions that are dynamically identified within hypermedia by the server (e.g. by hyperlinks within hypertext). Except for simple fixed entry points to the application, a client does not assume that any particular actions will be available for any particular resources beyond those described in representations previously received from the server. 160
  161. 161. RESTful Resources • Resources are typically ActiveRecord models and each model has a controller with seven actions: index, create, new, show, update, edit, destroy • We are constrained to four types of operations: • Create, Read, Update, and Delete (CRUD) • The four operations correspond to the HTTP verbs • GET, POST, PUT, DELETE • In REST we strive to have associations be join models so that they can be exposed as resources. 161
  162. 162. REST <-> CRUD ? • In REST, it’s the HTTP verb (POST, GET, PUT, DELETE) that changes which action a particular request is routed to. • Just because an application is RESTful does not mean it has to have a strict adherence to CRUD! You can map your actions however you like. 162 Resource GET PUT POST DELETE Collection URI, such as http://examp le.com/resour ces/ List the URIs and perhaps other details of the collection's members. Replace the entire collection with another collection. Create a new entry in the collection. The new entry's URL is assigned automatically and is usually returned by the Delete the entire collection.
  163. 163. Handling of PUT and DELETE methods • Most browsers don’t support methods other than “GET” and “POST” when it comes to submitting forms • Rails works around this issue by emulating other methods over POST with a hidden input named"_method", which is set to reflect the desired method. • When parsing POSTed data, Rails will take into account the special _method parameter and acts as if the HTTP method was the one specified inside it. • form_tag(search_path, :method => "put") • <form action="/search" method="post"> • <div style="margin:0;padding:0"> • <input name="_method" type="hidden" value="put" /> • <input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" /> • </div> • </form> 163
  164. 164. Route Globbing • Specify that a particular parameter should be matched to all the remaining parts of a route. • match 'photos/*other' => 'photos#unknown' • This route would match photos/12 or /photos/long/path/to/12, setting params[:other] to "12" or "long/path/to/12". 164
  165. 165. Optional Segments • match 'posts(/new)', :to => 'posts#create' • Here, both /posts/new and /posts will be redirected to the create action in posts controller 165
  166. 166. Empty Route • The root of the web site is the empty route. • root :to => 'welcome#show' 166
  167. 167. Redirection • match "/stories" => redirect("/posts") • match "/stories/:name" => redirect("/posts/%{name}") • match "/stories/:name" => redirect {|params| "/posts/#{params[:name].pluralize}" } • match "/stories" => redirect {|p, req| "/posts/#{req.subdomain}" } 167
  168. 168. Day 3 168
  169. 169. Testing 169
  170. 170. Testing • Testing is about asserting that certain expectations come true – that code works the way you expect it to when it runs. • Writing good tests means that you’ll find yourself • catching more bugs, • re-thinking your approach when you realize your code isn’t easily testable (a red flag that maintenance down the line is going to be rough), and • having something to tell you that your application isn’t completely broken after you’ve changed or added a feature. 170
  171. 171. Testing Workflow • The typical workflow for testing looks like this: • Write a test or refactor an existing one • Run the test, watch it fail • Make your code pass all tests • Polishing and refactoring your application becomes a matter of repeating these steps, with the added bonus of staying focused while you write code (all you have to do is pass the test) as well as keeping your code tested. 171
  172. 172. Rails Testing Terminology • Unit (Model) • These test business logic in your models. • A well-written Rails application should have the bulk of its code in its models, so the bulk of your tests should be these. • Functional (Controller) • These test individual controller actions in isolation. • Integration (Controller to Controller) • These test state mutations between/over multiple actions and routing, i.e.ensuring that things don’t totally explode as a user clicks through a typical work flow. • Fixtures # will not be using these, will work with Factories instead • Used to hold example model data used to easily instantiate those models in tests, avoiding the tedious process of manually creating model objects. • Unit/Helpers # will not be using these • These test helpers used in views. 172
  173. 173. RSpec • RSpec is a Behaviour-Driven Development tool for Ruby programmers. • RSpec uses the general malleability of Ruby to define a domain- specific language (DSL) built just for testing. • Add rspec to development and test environment of project in Gemfile • group :development do • gem 'webrat' • gem 'rspec-rails' • end • group :test do • gem 'webrat' • gem 'rspec-rails' • end • run bundle install 173
  174. 174. Sample RSpec Example • # bowling_spec.rb • require 'bowling' • describe Bowling, "#score" do • it "returns 0 for all gutter game" do • bowling = Bowling.new • 20.times { bowling.hit(0) } • bowling.score.should == 0 • end • end • #run the test through command line • rspec bowling_spec.rb 174
  175. 175. Controller Testing • Allows testing of controllers functions and expected responses • Generate a user controller with new action: • rails generate controller Users new • Develop test: • When your action :new is called, the response should be a success • Title should be ‘Sign up’ 175
  176. 176. User Controller Testing • #spec/controllers/users_controller_spec.rb • require 'spec_helper' • describe UsersController do • render_views #to render the associated view • describe "GET 'new'" do • it "should be successful" do • get 'new' • response.should be_success • end • it "should have the right title" do • get 'new' • response.should have_selector("title", :content => "Sign up") • end • end • end 176
  177. 177. Pages Controller Testing • When home method is called, response should be successful • When home is called, title should be ‘Ruby on Rails Workshop Sample App | Home’ • When contact page is called, response should be successful. • When contact page is called, title should be ‘Ruby on Rails Workshop Sample App | Contact’ • When about page is called, response should be successful. • When about page is called, title should be ‘Ruby on Rails Workshop Sample App | About’ 177
  178. 178. Do it yourself? 178
  179. 179. Pages Controller Testing • #Rails Tutorial Chapter 3 • #spec/controllers/pages_controller_spec.rb • require 'spec_helper' • describe PagesController do • render_views #necessary for titles test to work • describe "GET 'home'" do • it "should be successful" do • get 'home' • response.should be_success • end • it "should have the right title" do • get 'home' • response.should have_selector("title", • :content => "Ruby on Rails Workshop Sample App | Home") • end • end • describe "GET 'contact'" do • it "should be successful" do • get 'contact' • response.should be_success • end • it "should have the right title" do • get 'contact' • response.should have_selector("title", • :content => • "Ruby on Rails Workshop Sample App | Contact") • end • end • describe "GET 'about'" do • it "should be successful" do 179
  180. 180. Code for Page Testing • #app/controllers/pages_controller.rb • #remove app/views/layouts/application.html.erb • class PagesController < ApplicationController • :layout false • def home • end • def contact • end • def about • end • end • #app/views/pages/about.html.erb • <h1>Pages#about</h1> • <p>Find me in app/views/pages/about.html.erb</p> • #config/routes.rb • SampleApp::Application.routes.draw do • get "pages/home" • get "pages/contact" • get "pages/about" • ... • end 180
  181. 181. Code for Title Testing • #app/views/pages/home.html.erb • <!DOCTYPE html> • <html> • <head> • <title>Ruby on Rails Workshop Sample App | Home</title> • </head> • <body> • <h1>Sample App</h1> • <p> • This is the home page for the Ruby on Rails Workshop sample application. • </p> • </body> • </html> • #app/views/pages/contact.html.erb • <!DOCTYPE html> • <html> • <head> • <title>Ruby on Rails Workshop Sample App | Contact</title> • </head> • <body> • <h1>Contact</h1> • <p> • This is the contact page for the Ruby on Rails Workshop sample application. • </p> • </body> • </html> • #Similar approach for app/views/pages/about.html.erb as well 181
  182. 182. Integration Tests • Give us a way to simulate a browser accessing our application and thereby test it from end to end. • rails generate integration_test layout_links • Generates spec/requests/layout_links_spec.rb • Controller tests only know about URLs defined for that exact controller whereas Integration Tests are bound by no such restriction. 182
  183. 183. Integration Testing Example • When /home is called, title should be ‘Ruby on Rails Workshop Sample App | Home’ • When /contact page is called, title should be ‘Ruby on Rails Workshop Sample App | Contact’ • When /about page is called, title should be ‘Ruby on Rails Workshop Sample App | About’ 183
  184. 184. Code for Integration Testing • #spec/requests/layout_links_spec.rb • require 'spec_helper' • describe "LayoutLinks" do • it "should have a Home page at '/'" do • get '/' • response.should have_selector('title', :content => "Home") • end • it "should have a Contact page at '/contact'" do • get '/contact' • response.should have_selector('title', :content => "Contact") • end • it "should have an About page at '/about'" do • get '/about' • response.should have_selector('title', :content => "About") • end • it "should have a Help page at '/help'" do • get '/help' • response.should have_selector('title', :content => "Help") • end • end • #config/routes.rb • SampleApp::Application.routes.draw do • match '/contact', :to => 'pages#contact' • match '/about', :to => 'pages#about' • match '/help', :to => 'pages#help' • ... • end 184
  185. 185. ModelTesting • These test business logic in your models. • A well-written Rails application should have the bulk of its code in its models, so the bulk of your tests should be these 185
  186. 186. RSpec before(:each/all) • before(:each) runs the code inside the block before each test defined in the describe block • before(:all) runs the code inside the block before all test defined in the describe block • before(:all) shares some (not all) state across multiple examples. • Examples become bound together, which is an absolute no- no in testing. • Only ever use before(:all) to set up things that are global collaborators (expensive operation/setup) but not the things that you are describing in the examples. 186
  187. 187. User Model Testing • User should be created successfully when passed valid attributes 187
  188. 188. User Model Testing • #spec/models/user_spec.rb • require 'spec_helper' • describe User do • before(:each) do • @attr = , name: "Hisham Malik", email: “hisham@arkhitech.com”, password: "arkhitech", password_confirmation: "confiz" } • end • it "should create a new instance given valid attributes" do • User.create!(@attr) • end • end 188
  189. 189. Have a test to write, but not ready yet(requirements missing)? 189
  190. 190. Pending/TODO Test • To indicate that we should fill the spec with something useful • #spec/models/user_spec.rb • require 'spec_helper' • describe User do • pending "add some examples to (or delete) #{__FILE__}" • it "should require a name" • end • #command-line • $ bundle exec rspec spec/models/user_spec.rb • Finished in 0.01999 seconds • 1 example, 0 failures, 1 pending • Pending: • User add some examples to (or delete) • /Users/hisham/projects/sample_app/spec/models/user_spec.rb • (Not Yet Implemented) • User should require a name (Not Yet Implemented) • /Users/hisham/projects/sample_app/spec/models/user_spec.rb:5 190
  191. 191. Ruby - Advance Topics 191
  192. 192. More On Methods • Arbitrary number of arguments: def my_methods(*args) • Converting Array to arguments: my_method([a, b]*) • Dynamic method invocation: object.send(:method_name) • Duck typing: object.respond_to?(:method_name) 192
  193. 193. Method Aliasing • class Peter • def say_hi • puts "Hi" • end • end • class Peter • alias_method :say_hi_orig, :say_hi • def say_hi • puts "Before say hi" • say_hi_orig • puts "After say hi" • end • end 193
  194. 194. Reflection • “abc”.class # => String • “abc”.class.superclass # => Object • “abc”.class.ancestors # Lists implemented modules • “abc”.methods # => methods available for this instance • String.instance_methods(false) # => methods implemented in String class only (not super classes) • 10.5.kind_of?(Numeric) # => true • 10.5.instance_of?(Float) # => true 194
  195. 195. Reflection • method_missing • const_missing • instance_variable_get • instance_variable_set 195
  196. 196. Method Overriding/Overwriting • class Peter • def say_hi • puts "Hi" • end • end • class Peter • alias_method :say_hi_orig, :say_hi • def say_hi • puts "Before say hi" • say_hi_orig • puts "After say hi" • end • end 196
  197. 197. blocks, closures, and proc objects • def invoke_block • puts "before block" • yield 5 • puts "after block" • end • invoke_block { |n| puts "In block and received #{n}"} • my_proc = Proc.new { |n| puts "In proc, received #{n}"} • my_proc.call 2 # => Procedure called with 2 as param • invoke_block &my_proc # Procedure called with 5 as param 197
  198. 198. Blocks – Usage Examples • Iteration • [1, 2, 3].each {|item| puts item } • Resource Management • file_contents = open(file_name) { |f| f.read } • Callbacks • widget.on_button_press do • puts “Got button press” • end • Convention: • one-line blocks use {...} • multiline blocks use do...end 198
  199. 199. Active Records Contd. 199
  200. 200. Conditions • Hash Conditions • Client.where(:locked => true) • Client.where(:created_at => (params[:start_date].to_date)..(params[:end_date].to_date) ) • Client.where(:orders_count => [1,3,5]) 200
  201. 201. Order, Select, Limit, Offset, Group, and Having • Order • Client.order(‘created_at’) • Client.order(‘created_at DESC’) • Select • Client.select(‘DISTINCT(name)’) • Limit • Client.limit(5) • SELECT * FROM clients LIMIT 5 • Offset • Client.limit(5).offset(30) • SELECT * FROM clients LIMIT 5, 30 • Group • Order.group(‘date(created_at)’).order(‘created_at’) • SELECT * FROM orders GROUP BY Date(created_at) ORDER BY created_at • Having • Order.group("date(created_at)").having("created_at > ?", 1.month.ago) • SELECT * FROM orders GROUP BY date(created_at) HAVING created_at > '2009-01-15' • This will return single order objects for each day, but only for the last month. 201
  202. 202. Dynamic Finder • ActiveRecord provides a finder method for every field (also known as an attribute) that you define in your table. • Client.find_by_first_name(‘Hisham’) • Client.find_by_company!(‘Confiz’) #throws an RecordNotFound Exception if no record is returned • Client.find_all_by_company(‘Confiz’) • Allow searching over multiple fields. • Client.find_by_first_name_and_company(‘Hisham’,‘Confiz’) 202
  203. 203. Dynamic Finders/Initializers • ActiveRecord allows you to find or create/initialize objects if they aren’t found. • Client.find_or_create_by_first_name(‘Hisham’) • SELECT * FROM clients WHERE (clients.first_name = 'Hisham') LIMIT 1 • BEGIN • INSERT INTO clients (first_name, updated_at, created_at, orders_count, locked) • VALUES('Hisham', '2008-09-28 15:39:12', '2008-09-28 15:39:12', 0, '0') • COMMIT • Client.find_or_initialize_by_first_name('Hisham') • You can modify other fields in client by calling the attribute setters on it: client.company = ‘Confiz’ and when you want to write it to the database just call save on it. 203
  204. 204. find_by_sql/select_all • Client.find_by_sql("SELECT * FROM clients INNER JOIN orders ON clients.id = orders.client_id ORDER clients.created_at desc") • Returns an array of clients • Client.connection.select_all("SELECT * FROM clients WHERE id = '1'") • Returns an array of Hashes, where each hash represents a record 204
  205. 205. Find with Conditions Member.all(:conditions => {:last_name => 'malik'}) Member.all(:conditions => *“expiry_data <= ?”, Time.now) Member.all(:conditions => *“nick_name like ?”,”#,params*:nick_name+-%”+) Member.all(:conditions => *“dob <= ?”, 20.years.ago+, :limit => 10, :offset => 100, :order => :id) 205
  206. 206. Locking account = Account.lock.find(id) account = Account.find(id, :lock => true) SELECT * FROM accounts WHERE (account.`id` = 1) FOR UPDATE account.balance -= 10 account.save! 206
  207. 207. Update/Create Member.create(:nick_name => 'Salaar', expiry_date => Time.now + 1.year) member.nick_name = 'Mikael' member.save! 207
  208. 208. Destroy/Delete Destroy instantiates object and calls all hooks Delete simply executes the delete query 208
  209. 209. Available Callbacks • Creating/Updating an Object • before_validation • after_validation • before_save • after_save • Destroying an Object • before_destroy • after_destroy • around_destroy 209
  210. 210. Transactions •Account.transaction do account1.deposit(100) account2.withdraw(100) end Account.transaction(account1, account2) do account1.deposit(100) account2.withdraw(100) end 210
  211. 211. Calculations Person.minimum(‘age’) Person.maximum(‘age’) Person.sum(‘age’) Person.where(*“age > ?”, 25+).count Person.average(‘age’) 211
  212. 212. ActiveRecord Associations • has_one: User has one profile • has_many: User has many posts • belongs_to: Post belongs to user • has_and_belongs_to: Post belongs to many categories, each category has many posts 212
  213. 213. Why Associations? • Allows to define associations between different models. For example: • Firm has many Customers • Customer has many Orders • Order belongs to Customer 213
  214. 214. Types of Associations • belongs_to • has_one • has_many • has_many :through • has_one :through • has_and_belongs_to_many 214
  215. 215. Example • class Customer < ActiveRecord::Base • has_many :orders, :dependent => :destroy • end • class Order < ActiveRecord::Base • belongs_to :customer • end • @order = @customer.orders.create(:order_date => Time.now) • @customer.destroy 215
  216. 216. Belongs To • A belongs_to association sets up a one-to-one connection with another model, such that each instance of the declaring model “belongs to” one instance of the other model. 216
  217. 217. Has One • A has_one association also sets up a one-to-one connection with another model, but with somewhat different semantics (and consequences). This association indicates that each instance of a model contains or possesses one instance of another model. 217
  218. 218. Difference Between Belongs to and Has One? • The distinction is in where you place the foreign key. • Foreign key goes on the table for the class declaring the belongs_to association. 218
  219. 219. Has Many • Indicates a one-to-many connection with another model. Indicates that each instance of the model has zero or more instances of another model. 219
  220. 220. Has Many :Through • Often used to set up a many-to-many connection with another model. • Indicates that the declaring model can be matched with zero or more instances of another model by proceeding through a third model. 220
  221. 221. Has One :Through • Sets up a one-to-one connection with another model. • This association indicates that the declaring model can be matched with one instance of another model by proceeding through a third model. 221
  222. 222. Has and Belongs to Many • Creates a direct many-to-many connection with another model, with no intervening model. 222
  223. 223. Has and Belongs to Many DB Migration • class CreateAssemblyPartJoinTable < ActiveRecord::Migration • def self.up • create_table :assemblies_parts, :id => false do |t| • t.integer :assembly_id • t.integer :part_id • end • end • • def self.down • drop_table :assemblies_parts • end • end 223 Passed :id => false to create_table because that table does not represent a model. Required for the association to work properly.
  224. 224. Polymorphic Associations • A model can belong to more than one other model, on a single association • For example, a picture can belong to a user or a product. 224
  225. 225. Creating Polymorphic DB Migration • class CreatePictures < ActiveRecord::Migration • def self.up • create_table :pictures do |t| • t.string :name • t.references :imageable, :polymorphic => true • t.timestamps • end • end • • def self.down • drop_table :pictures • end • end • class CreatePictures < ActiveRecord::Migration • def self.up • create_table :pictures do |t| • t.string :name • t.integer :imageable_id • t.string :imageable_type • t.timestamps • end • end • def self.down • drop_table :pictures • end • end 225 functio nally equival ent
  226. 226. Methods Added by Belongs To/Has One • association(force_reload = false) • Returns the associated object, nil if none found. • Set force_reload = true, to not use cached object • association=(associate) • Assigns an associated object to this object. • build_association(attributes = {}) • Returns a new object of the associated type instantiated with passed attributes. • Foreign key relation is also set, but not saved yet. • create_association(attributes = {}) • Returns a new object of the associated type instantiated with passed attributes. • Foreign key relation is also set, and object is saved if it passes validations. 226
  227. 227. Auto-Saving by Has One • When you assign an object to a has_one association, that object is automatically saved (in order to update its foreign key). In addition, any object being replaced is also automatically saved, because its foreign key will change too. • If either of these saves fails due to validation errors, then the assignment statement returns false and the assignment itself is cancelled. • If the parent object (the one declaring the has_one association) is unsaved (that is, new_record? returns true) then the child objects are not saved. They will automatically when the parent object is saved. • If you want to assign an object to a has_one association without saving the object, use the association.build method. 227
  228. 228. Methods Added by Has Many/Has and Belongs to Many• collection(force_reload = false) • Returns the associated collection, empty array if none found. • Set force_reload = true, to not use cached collection • collection<<(object, …) • Adds one more object to the collection • collection.delete(object, …) • Deletes one or more objects by setting their foreign keys to NULL • collection=objects • Makes the collection contain only the supplied objects, by adding and deleting as appropriate. • collection_ids • Returns array of ids of collection, empty array if none found. • collection_ids=ids • Makes the collection contain only the objects identified by the ids, by adding and deleting as appropriate. 228
  229. 229. Methods Added by Has Many/Has and Belongs to Manycollection.clear Removes every object from the collection collection.empty? collection.size collection.find(…) Finds objects within the collection. collection.exists?(…) Checks for existence within collection. collection.build(attributes = ,-, …) Returns one or more new objects of the associated type. collection.create(attributes = {}) Returns a new object of the associated type 229
  230. 230. Auto-Saving by Has Many/Has and Belongs to ManyWhen you assign an object to a has_many association, that object is automatically saved (in order to update its foreign key). If you assign multiple objects in one statement, then they are all saved. If any of these saves fails due to validation errors, then the assignment statement returns false and the assignment itself is cancelled. If the parent object (the one declaring the has_many association) is unsaved (that is, new_record? returns true) then the child objects are not saved when they are added. All unsaved members of the association will automatically be saved when the parent is saved. If you want to assign an object to a has_many association without saving the object, use the collection.build method. 230
  231. 231. Joins • Join using String • Client.joins('LEFT OUTER JOIN addresses ON addresses.client_id = clients.id') • SELECT clients.* FROM clients LEFT OUTER JOIN addresses ON addresses.client_id = clients.id • Using Array/Hash of Named Associations (Works with INNER JOIN Only) • Post.joins(:category, :comments) • SELECT posts.* FROM posts • INNER JOIN categories ON posts.category_id = categories.id • INNER JOIN comments ON comments.post_id = posts.id • Category.joins(:posts => [{:comments => :guest}, :tags]) 231
  232. 232. Joining and Searching • Group.joins(:group_members).where(:name=>'ror- training',:group_members=>{:is_muted => false}) 232
  233. 233. Validation Validations are used to ensure that only valid data is saved into your database. 233
  234. 234. Client-Side Validations • Client-side validations can be useful, but are generally unreliable if used alone. • If they are implemented using JavaScript, they may be bypassed if JavaScript is turned off in the user’s browser. • When combined with other techniques, client-side validation can be a convenient way to provide users with immediate feedback as they use your site. 234
  235. 235. Controller-level Validations • Controller-level validations can be tempting to use, but often become unwieldy and difficult to test and maintain. • Whenever possible, it’s a good idea to keep your controllers skinny, as it will make your application a pleasure to work with in the long run. 235
  236. 236. Model-Level Validation • Model-level validations are the best way to ensure that only valid data is saved into your database. • They are database agnostic, cannot be bypassed by end users, and are convenient to test and maintain. Rails makes them easy to use, provides built-in helpers for common needs, and allows you to create your own validation methods as well. 236
  237. 237. Database Validations • Database constraints and/or stored procedures make the validation mechanisms database-dependent and can make testing and maintenance more difficult. • If your database is used by other applications, it may be a good idea to use some constraints at the database level. • Database-level validations can safely handle some things (such as uniqueness in heavily-used tables) that can be difficult to implement otherwise. 237
  238. 238. ActiveRecord Validation Triggers create create! save save! update update_attributes update_attributes! The bang versions (e.g. save!) raise an exception if the record is invalid. The non- bang versions don’t: save and update_attributes return false, create and update just return the objects. 238
  239. 239. ActiveRecord Validation Skipping save(:validate => false) decrement! decrement_counter increment! increment_counter toggle! touch update_all update_attribute update_column update_counters 239
  240. 240. ActiveRecord Valid? class Person < ActiveRecord validates :name, :presence => true end Person.create(:name => "John Doe").valid? # => true Person.create(:name => nil).valid? # => false Person.new(:name => nil).valid? # => false 240
  241. 241. ActiveRecord Errors • To verify whether or not a particular attribute of an object is valid, you can use errors[:attribute]. • It returns an array of all the errors for :attribute. • If there are no errors on the specified attribute, an empty array is returned. 241
  242. 242. Object-Level Errors • You can add error messages that are related to the object’s state as a whole, instead of being related to a specific attribute in errors[:base]. • You can use this method when you want to say that the object is invalid, no matter the values of its attributes. • class Person < ActiveRecord::Base • def a_method_used_for_validation_purposes • errors[:base] << "This person is invalid because ..." • end • end 242
  243. 243. Validation Helpers • Active Record offers many pre-defined validation helpers that provide common validation rules. • Every time a validation fails, an error message is added to the object’s errors collection. • Each helper accepts an arbitrary number of attribute names, so with a single line of code you can add the same kind of validation to several attributes. • All of them accept the :on and :message options, which define when the validation should be run and what message should be added to the errors collection if it fails, respectively. • The :on option takes one of the values :save (the default), :create or :update. • There is a default error message for each one of the validation helpers. These messages are used when the :message option isn’t specified. 243
  244. 244. validates_presence_of • Validates that the specified attributes are not empty. • class Person < ActiveRecord::Base • validates_presence_of :name, :login, :email • end 244
  245. 245. validates_uniqueness_of • Validates that the attribute’s value is unique right before the object gets saved. • Not guaranteed due to race conditions, so use together unique database index on the attribute. • class Holiday < ActiveRecord::Base • validates_uniqueness_of :name, :scope => :year, • :message => "should happen once per year" • end 245
  246. 246. validates_acceptance_of • Validates that a checkbox on the user interface was checked when a form was submitted. • This validation is very specific to web applications and this ‘acceptance’ does not need to be recorded anywhere in your database. • class Person < ActiveRecord::Base • validates_acceptance_of :terms_of_service • end 246
  247. 247. validates_confirmation_of • This validation creates a virtual attribute whose name is the name of the field that has to be confirmed with “_confirmation” appended. • This check is performed only if email_confirmation is not nil. To require confirmation, make sure to add a presence check for the confirmation attribute. • <%= text_field :person, :email %> • <%= text_field :person, :email_confirmation %> • : • class Person < ActiveRecord::Base • validates_confirmation_of :email • validates_presence_of :email_confirmation • end 247
  248. 248. validates_format_of • Validates the attributes’ values by testing whether they match a given regular expression, which is specified using the :with option. • class Product < ActiveRecord::Base • validates_format_of :legacy_code, :with => /A[a-zA-Z]+z/, • :message => "Only letters allowed" • end 248
  249. 249. validates_length_of • Validates the length of the attributes’ values • The possible length constraint options are: • :minimum – The attribute cannot have less than the specified length. • :maximum – The attribute cannot have more than the specified length. • :in (or :within) – The attribute length must be included in a given interval. The value for this option must be a range. • :is – The attribute length must be equal to the given value. • The default error messages depend on the type of length validation being performed. You can personalize these messages using the :wrong_length, :too_long, and :too_short options and %{count} as a placeholder for the number corresponding to the length constraint being used. • class Person < ActiveRecord::Base • validates_length_of :bio, :maximum => 1000, • :too_long => "%{count} characters is the maximum allowed" • end 249

×