453
Rapid Application Development using Ruby on Rails Presented By: Hisham Malik http://www.arkhitech.com 1

Rapid Application Development using Ruby on Rails

Embed Size (px)

DESCRIPTION

Rapid Application Development using Ruby on Rails

Citation preview

Page 1: Rapid Application Development using Ruby on Rails

Rapid Application Development using Ruby on Rails

Presented By: Hisham Malik http://www.arkhitech.com

1

Page 2: Rapid Application Development using Ruby on Rails

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

Page 3: Rapid Application Development using Ruby on Rails

Day 2 - Details

• DB Migrations• MVC – ActiveRecord, ActionController, ActionView• Form Helpers• Routing

3

Page 4: Rapid Application Development using Ruby on Rails

Day 3 - Optimizations

• Testing • Ruby Language – More Details.• Active Records - More Details.• Live Demo Contd.• AJAX – Unobtrusive Javascripting (UJS)• Security

4

Page 5: Rapid Application Development using Ruby on Rails

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

Page 6: Rapid Application Development using Ruby on Rails

Day 1

6

Page 7: Rapid Application Development using Ruby on Rails

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

Page 8: Rapid Application Development using Ruby on Rails

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

Page 9: Rapid Application Development using Ruby on Rails

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

Page 10: Rapid Application Development using Ruby on Rails

Environment Check!

• Ruby - 2.0.0• ruby -v

• Rails - 4.0.0• rails -v

10

Page 11: Rapid Application Development using Ruby on Rails

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

Page 12: Rapid Application Development using Ruby on Rails

Getting Up and Running Quickly - Demo

12

Page 13: Rapid Application Development using Ruby on Rails

Creating Sample Application

• rails new sample• You can see all of the switches that the Rails application builder

accepts by running rails -h.

13

Page 14: Rapid Application Development using Ruby on Rails

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

Page 15: Rapid Application Development using Ruby on Rails

Generated User Pages/URL

15

Page 16: Rapid Application Development using Ruby on Rails

Ruby“I always thought Smalltalk would beat Java. I just didn’t know it would be called ‘Ruby’ when it did”- Kent Beck

16

Page 17: Rapid Application Development using Ruby on Rails

Ruby Principles

• Focusing on the human• Dynamic Programming• Principle of Least Surprise• Principle of Succinctness• Lightweight Language• Choosing good names• Embedding hidden messages

17

Page 18: Rapid Application Development using Ruby on Rails

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

Page 19: Rapid Application Development using Ruby on Rails

Principle of Least Surprise

• diff = ary1 – ary2• union = ary1 + ary2

19

Page 20: Rapid Application Development using Ruby on Rails

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

Page 21: Rapid Application Development using Ruby on Rails

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

Page 22: Rapid Application Development using Ruby on Rails

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

Page 23: Rapid Application Development using Ruby on Rails

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

Page 24: Rapid Application Development using Ruby on Rails

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

Page 25: Rapid Application Development using Ruby on Rails

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

Page 26: Rapid Application Development using Ruby on Rails

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

Page 27: Rapid Application Development using Ruby on Rails

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

Page 28: Rapid Application Development using Ruby on Rails

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

Page 29: Rapid Application Development using Ruby on Rails

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

Page 30: Rapid Application Development using Ruby on Rails

attr_reader

• class Person • attr_reader :name • def initialize(name) • @name = name • end • end • person = Person.new("Andreas") • puts person.name

30

Page 31: Rapid Application Development using Ruby on Rails

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

Page 32: Rapid Application Development using Ruby on Rails

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

Page 33: Rapid Application Development using Ruby on Rails

Class Instance Methods

• andreas = Person.new(“Andreas”)• def andreas.andreas_says_hi• “Andreas says hi”• end• andreas.andreas_says_hi

33

Page 34: Rapid Application Development using Ruby on Rails

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

Page 35: Rapid Application Development using Ruby on Rails

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

Page 36: Rapid Application Development using Ruby on Rails

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

Page 37: Rapid Application Development using Ruby on Rails

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

Page 38: Rapid Application Development using Ruby on Rails

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

Page 39: Rapid Application Development using Ruby on Rails

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

Page 40: Rapid Application Development using Ruby on Rails

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

Page 41: Rapid Application Development using Ruby on Rails

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

Page 42: Rapid Application Development using Ruby on Rails

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

Page 43: Rapid Application Development using Ruby on Rails

Easier Conditional Statements• puts “Good Morning” if Time.now.hour < 12• puts “Good Night” unless Time.now.hour < 20

43

Page 44: Rapid Application Development using Ruby on Rails

Iterators: while, until,and for. Keywords: breakand 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

Page 45: Rapid Application Development using Ruby on Rails

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

Page 46: Rapid Application Development using Ruby on Rails

Regular Expressions

• puts 'matches' if ‘Ruby’ =~ /^(ruby|python)$/i• “Go\nRuby” =~ /Go\s+(\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

Page 47: Rapid Application Development using Ruby on Rails

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

Page 48: Rapid Application Development using Ruby on Rails

Rails MVC

48

Page 49: Rapid Application Development using Ruby on Rails

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

Page 50: Rapid Application Development using Ruby on Rails

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

Page 51: Rapid Application Development using Ruby on Rails

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

Page 52: Rapid Application Development using Ruby on Rails

Rails - MVC

52

Page 53: Rapid Application Development using Ruby on Rails

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

Page 54: Rapid Application Development using Ruby on Rails

Creating Blog Project

54

Page 55: Rapid Application Development using Ruby on Rails

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

Page 56: Rapid Application Development using Ruby on Rails

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:create56

Page 57: Rapid Application Development using Ruby on Rails

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

Page 58: Rapid Application Development using Ruby on Rails

Hello Rails

• rails server

58

Page 59: Rapid Application Development using Ruby on Rails

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 RakefileYou can put your own tasks under lib/tasksbundle exec rake -Tbundle exec rake -T db #See a list of database tasks

59

Page 60: Rapid Application Development using Ruby on Rails

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

Page 61: Rapid Application Development using Ruby on Rails

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

Page 62: Rapid Application Development using Ruby on Rails

Live Demo

62

Page 63: Rapid Application Development using Ruby on Rails

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

Page 64: Rapid Application Development using Ruby on Rails

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

Page 65: Rapid Application Development using Ruby on Rails

Creating Post Resource

• rails generate scaffold Post name:string title:string content:text• bundle exec rake db:migrate

65

Page 66: Rapid Application Development using Ruby on Rails

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

Page 67: Rapid Application Development using Ruby on Rails

Under The Hood

67

Page 68: Rapid Application Development using Ruby on Rails

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

Page 69: Rapid Application Development using Ruby on Rails

Posts Model

• #app/models/post.rb• class Post < ActiveRecord::Base• end

69

Page 70: Rapid Application Development using Ruby on Rails

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

Page 71: Rapid Application Development using Ruby on Rails

Post Controllerapp/controller/posts_controller.rb

71

Page 72: Rapid Application Development using Ruby on Rails

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

Page 73: Rapid Application Development using Ruby on Rails

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

Page 74: Rapid Application Development using Ruby on Rails

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

Page 75: Rapid Application Development using Ruby on Rails

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

Page 76: Rapid Application Development using Ruby on Rails

Comments can be given for Post?

76

Page 77: Rapid Application Development using Ruby on Rails

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

Page 78: Rapid Application Development using Ruby on Rails

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

Page 79: Rapid Application Development using Ruby on Rails

Comments Controller?

79

Page 80: Rapid Application Development using Ruby on Rails

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

Page 81: Rapid Application Development using Ruby on Rails

Add Comments to Post

• Comments should be added where the post is shown.

81

Page 82: Rapid Application Development using Ruby on Rails

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

Page 83: Rapid Application Development using Ruby on Rails

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

Page 84: Rapid Application Development using Ruby on Rails

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

Page 85: Rapid Application Development using Ruby on Rails

Day 2

85

Page 86: Rapid Application Development using Ruby on Rails

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

Page 87: Rapid Application Development using Ruby on Rails

Migrations

A way to evolve your database schema over timeMigrations use a database independent Ruby APIMigration classes extend ActiveRecord::Migration and have an up and a down methodMigrations are stored in files in db/migrate, one for each migration class. Running migrations:bundle exec rake db:migrate

87

Page 88: Rapid Application Development using Ruby on Rails

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

Page 89: Rapid Application Development using Ruby on Rails

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

Page 90: Rapid Application Development using Ruby on Rails

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

Page 91: Rapid Application Development using Ruby on Rails

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

Page 92: Rapid Application Development using Ruby on Rails

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

Page 93: Rapid Application Development using Ruby on Rails

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

Page 94: Rapid Application Development using Ruby on Rails

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

Page 95: Rapid Application Development using Ruby on Rails

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

functionally equivalent

functionally equivalent

Page 96: Rapid Application Development using Ruby on Rails

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

Page 97: Rapid Application Development using Ruby on Rails

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

Page 98: Rapid Application Development using Ruby on Rails

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

Page 99: Rapid Application Development using Ruby on Rails

Boolean Attributes

Everything except nil and false is true in RubyHowever, 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

Page 100: Rapid Application Development using Ruby on Rails

Active Record

100

Page 101: Rapid Application Development using Ruby on Rails

Fundamentals

One database table maps to one Ruby classRuby classes live under app/models and extend ActiveRecord::BaseTable names are plural and class names are singularDatabase columns map to attributes, i.e. get and set methods, in the model classAll tables have an integer primary key called idDatabase tables are created with migrations

101

Page 102: Rapid Application Development using Ruby on Rails

Methods

whereselectgrouporderlimitoffsetjoinsincludeslockreadonlyfromhaving

102

Page 103: Rapid Application Development using Ruby on Rails

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

Page 104: Rapid Application Development using Ruby on Rails

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

Page 105: Rapid Application Development using Ruby on Rails

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

Page 106: Rapid Application Development using Ruby on Rails

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

Page 107: Rapid Application Development using Ruby on Rails

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

Page 108: Rapid Application Development using Ruby on Rails

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

Page 109: Rapid Application Development using Ruby on Rails

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

Page 110: Rapid Application Development using Ruby on Rails

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

Page 111: Rapid Application Development using Ruby on Rails

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

Page 112: Rapid Application Development using Ruby on Rails

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

Page 113: Rapid Application Development using Ruby on Rails

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

Page 114: Rapid Application Development using Ruby on Rails

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

Page 115: Rapid Application Development using Ruby on Rails

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

Page 116: Rapid Application Development using Ruby on Rails

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

Page 117: Rapid Application Development using Ruby on Rails

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

Page 118: Rapid Application Development using Ruby on Rails

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

Page 119: Rapid Application Development using Ruby on Rails

Passing Variables toPartials• 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

Page 120: Rapid Application Development using Ruby on Rails

Partials andCollections• <% 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

Page 121: Rapid Application Development using Ruby on Rails

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

Page 122: Rapid Application Development using Ruby on Rails

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

Page 123: Rapid Application Development using Ruby on Rails

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

Page 124: Rapid Application Development using Ruby on Rails

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

Page 125: Rapid Application Development using Ruby on Rails

Selecting Layout at Runtime

• class BlogController < ActionController::Base• layout :determine_layout• private• def determine_layout• user.admin? ? “admin” : “standard”• end• end

125

Page 126: Rapid Application Development using Ruby on Rails

Yield/Content_for

• Layout:• <html>• <head>• <%= yield :head %>• </head>• <body>• <%= yield %>• </body>• </html>

126

Page 127: Rapid Application Development using Ruby on Rails

Yield/Content_for/Provide

• <%= content_for :head do %>• <title>A simple page</title>• <% end %>• <%= provide :head %> • <p>Hello, Rails!</p>

127

Page 128: Rapid Application Development using Ruby on Rails

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

Page 129: Rapid Application Development using Ruby on Rails

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

Page 130: Rapid Application Development using Ruby on Rails

AssetTagHelper

• Provide methods for generating HTML that links views to feeds, JavaScript, stylesheets, images, videos and audios.

• http://api.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.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

Page 131: Rapid Application Development using Ruby on Rails

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

Page 132: Rapid Application Development using Ruby on Rails

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

Page 133: Rapid Application Development using Ruby on Rails

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

Page 134: Rapid Application Development using Ruby on Rails

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

Page 135: Rapid Application Development using Ruby on Rails

TextHelper• Provides common-use text manipulation methods• http://api.rubyonrails.org/classes/ActionView/Helpers/

TextHelper.html• 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

[email protected]")• <tr class="<%= cycle("even", "odd") -%>">

135

Page 136: Rapid Application Development using Ruby on Rails

UrlHelper• Provides an easy way to create dynamic urls based on • http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html

• 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

Page 137: Rapid Application Development using Ruby on Rails

UrlHelper

• button_to "New", :action => "new"• <form method="post" action="/controller/new"

class="button_to">• <div><input value="New" type="submit" /></div>• </form>"

137

Page 138: Rapid Application Development using Ruby on Rails

Protect Email Addresses

• Encoding emails makes it more difficult for web spiders to lift email addresses off of a site• mail_to "[email protected]", "My email", :encode =>

"javascript"• mail_to "[email protected]", "My email", :encode => "hex"

138

Page 139: Rapid Application Development using Ruby on Rails

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

Page 140: Rapid Application Development using Ruby on Rails

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

Page 141: Rapid Application Development using Ruby on Rails

*_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

Page 142: Rapid Application Development using Ruby on Rails

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

Page 143: Rapid Application Development using Ruby on Rails

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

Page 144: Rapid Application Development using Ruby on Rails

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

Page 145: Rapid Application Development using Ruby on Rails

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

Page 146: Rapid Application Development using Ruby on Rails

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

Page 147: Rapid Application Development using Ruby on Rails

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

Page 148: Rapid Application Development using Ruby on Rails

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

Page 149: Rapid Application Development using Ruby on Rails

Routing

149

Page 150: Rapid Application Development using Ruby on Rails

Routing Options

• Resource Routing• Non-Resourceful Routes

150

Page 151: Rapid Application Development using Ruby on Rails

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

Page 152: Rapid Application Development using Ruby on Rails

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

Page 153: Rapid Application Development using Ruby on Rails

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

Page 154: Rapid Application Development using Ruby on Rails

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

Page 155: Rapid Application Development using Ruby on Rails

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

Page 156: Rapid Application Development using Ruby on Rails

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

Page 157: Rapid Application Development using Ruby on Rails

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

Page 158: Rapid Application Development using Ruby on Rails

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

Page 159: Rapid Application Development using Ruby on Rails

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

Page 160: Rapid Application Development using Ruby on Rails

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

Page 161: Rapid Application Development using Ruby on Rails

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

Page 162: Rapid Application Development using Ruby on Rails

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://example.com/resources/

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

Delete the entire collection.

Element URI, such as http://example.com/resources/xyz

Retrieve a representation of the addressed member of the collection, expressed in an appropriate Internet media type.

Update the addressed member of the collection.

Treat the addressed member as a collection in its own right and create a new entry in it.

Delete the addressed member of the collection.

Page 163: Rapid Application Development using Ruby on Rails

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

Page 164: Rapid Application Development using Ruby on Rails

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

Page 165: Rapid Application Development using Ruby on Rails

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

Page 166: Rapid Application Development using Ruby on Rails

Empty Route

• The root of the web site is the empty route.• root :to => 'welcome#show'

166

Page 167: Rapid Application Development using Ruby on Rails

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

Page 168: Rapid Application Development using Ruby on Rails

Day 3

168

Page 169: Rapid Application Development using Ruby on Rails

Testing

169

Page 170: Rapid Application Development using Ruby on Rails

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

Page 171: Rapid Application Development using Ruby on Rails

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

Page 172: Rapid Application Development using Ruby on Rails

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

Page 173: Rapid Application Development using Ruby on Rails

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

Page 174: Rapid Application Development using Ruby on Rails

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

Page 175: Rapid Application Development using Ruby on Rails

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

Page 176: Rapid Application Development using Ruby on Rails

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

Page 177: Rapid Application Development using Ruby on Rails

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

Page 178: Rapid Application Development using Ruby on Rails

Do it yourself?

178

Page 179: Rapid Application Development using Ruby on Rails

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• get 'about'• response.should be_success• end

• it "should have the right title" do• get 'about'• response.should have_selector("title",• :content =>• "Ruby on Rails Workshop Sample App | About")• end• end• end• # run all the tests• bundle exec rspec spec/

179

Page 180: Rapid Application Development using Ruby on Rails

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

Page 181: Rapid Application Development using Ruby on Rails

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

Page 182: Rapid Application Development using Ruby on Rails

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

Page 183: Rapid Application Development using Ruby on Rails

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

Page 184: Rapid Application Development using Ruby on Rails

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

Page 185: Rapid Application Development using Ruby on Rails

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

Page 186: Rapid Application Development using Ruby on Rails

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

Page 187: Rapid Application Development using Ruby on Rails

User Model Testing

• User should be created successfully when passed valid attributes

187

Page 188: Rapid Application Development using Ruby on Rails

User Model Testing• #spec/models/user_spec.rb• require 'spec_helper'• describe User do• before(:each) do• @attr = { name: "Hisham Malik", email: “[email protected]”,

password: "arkhitech", password_confirmation: "confiz" }• end

• it "should create a new instance given valid attributes" do• User.create!(@attr)• end• end

188

Page 189: Rapid Application Development using Ruby on Rails

Have a test to write, but not ready yet(requirements missing)?

189

Page 190: Rapid Application Development using Ruby on Rails

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

Page 191: Rapid Application Development using Ruby on Rails

Ruby - Advance Topics

191

Page 192: Rapid Application Development using Ruby on Rails

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

Page 193: Rapid Application Development using Ruby on Rails

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

Page 194: Rapid Application Development using Ruby on Rails

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

Page 195: Rapid Application Development using Ruby on Rails

Reflection

• method_missing• const_missing• instance_variable_get• instance_variable_set

195

Page 196: Rapid Application Development using Ruby on Rails

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

Page 197: Rapid Application Development using Ruby on Rails

blocks, closures, andproc 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

Page 198: Rapid Application Development using Ruby on Rails

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

Page 199: Rapid Application Development using Ruby on Rails

Active Records Contd.

199

Page 200: Rapid Application Development using Ruby on Rails

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

Page 201: Rapid Application Development using Ruby on Rails

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

Page 202: Rapid Application Development using Ruby on Rails

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

Page 203: Rapid Application Development using Ruby on Rails

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

Page 204: Rapid Application Development using Ruby on Rails

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

Page 205: Rapid Application Development using Ruby on Rails

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

Page 206: Rapid Application Development using Ruby on Rails

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

Page 207: Rapid Application Development using Ruby on Rails

Update/Create

Member.create(:nick_name => 'Salaar', expiry_date => Time.now + 1.year) member.nick_name = 'Mikael'member.save!

207

Page 208: Rapid Application Development using Ruby on Rails

Destroy/Delete

Destroy instantiates object and calls all hooksDelete simply executes the delete query

208

Page 209: Rapid Application Development using Ruby on Rails

Available Callbacks

• Creating/Updating an Object• before_validation• after_validation• before_save• after_save

• Destroying an Object• before_destroy• after_destroy• around_destroy

209

Page 210: Rapid Application Development using Ruby on Rails

Transactions

•Account.transaction do account1.deposit(100) account2.withdraw(100)endAccount.transaction(account1, account2) do account1.deposit(100) account2.withdraw(100)end

210

Page 211: Rapid Application Development using Ruby on Rails

Calculations

Person.minimum(‘age’)Person.maximum(‘age’)Person.sum(‘age’)Person.where([“age > ?”, 25]).countPerson.average(‘age’)

211

Page 212: Rapid Application Development using Ruby on Rails

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

Page 213: Rapid Application Development using Ruby on Rails

Why Associations?

• Allows to define associations between different models. For example:

• Firm has many Customers • Customer has many Orders• Order belongs to Customer

213

Page 214: Rapid Application Development using Ruby on Rails

Types of Associations

• belongs_to• has_one• has_many• has_many :through• has_one :through• has_and_belongs_to_many

214

Page 215: Rapid Application Development using Ruby on Rails

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

Page 216: Rapid Application Development using Ruby on Rails

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

Page 217: Rapid Application Development using Ruby on Rails

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

Page 218: Rapid Application Development using Ruby on Rails

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

Page 219: Rapid Application Development using Ruby on Rails

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

Page 220: Rapid Application Development using Ruby on Rails

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

Page 221: Rapid Application Development using Ruby on Rails

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

Page 222: Rapid Application Development using Ruby on Rails

Has and Belongs to Many

• Creates a direct many-to-many connection with another model, with no intervening model.

222

Page 223: Rapid Application Development using Ruby on Rails

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.

Page 224: Rapid Application Development using Ruby on Rails

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

Page 225: Rapid Application Development using Ruby on Rails

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

functionally equivalent

functionally equivalent

Page 226: Rapid Application Development using Ruby on Rails

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

Page 227: Rapid Application Development using Ruby on Rails

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

Page 228: Rapid Application Development using Ruby on Rails

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

Page 229: Rapid Application Development using Ruby on Rails

Methods Added by Has Many/Has and Belongs to Manycollection.clear

Removes every object from the collectioncollection.empty?collection.sizecollection.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 type229

Page 230: Rapid Application Development using Ruby on Rails

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

Page 231: Rapid Application Development using Ruby on Rails

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

Page 232: Rapid Application Development using Ruby on Rails

Joining and Searching

• Group.joins(:group_members).where(:name=>'ror-training',:group_members=>{:is_muted => false})

232

Page 233: Rapid Application Development using Ruby on Rails

ValidationValidations are used to ensure that only valid data is saved into your database.

233

Page 234: Rapid Application Development using Ruby on Rails

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

Page 235: Rapid Application Development using Ruby on Rails

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

Page 236: Rapid Application Development using Ruby on Rails

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

Page 237: Rapid Application Development using Ruby on Rails

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

Page 238: Rapid Application Development using Ruby on Rails

ActiveRecord Validation Triggerscreatecreate!savesave!updateupdate_attributesupdate_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

Page 239: Rapid Application Development using Ruby on Rails

ActiveRecord Validation Skippingsave(:validate => false)decrement!decrement_counterincrement!increment_countertoggle!touchupdate_allupdate_attributeupdate_columnupdate_counters 239

Page 240: Rapid Application Development using Ruby on Rails

ActiveRecord Valid?

class Person < ActiveRecord validates :name, :presence => trueend

Person.create(:name => "John Doe").valid? # => truePerson.create(:name => nil).valid? # => falsePerson.new(:name => nil).valid? # => false

240

Page 241: Rapid Application Development using Ruby on Rails

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

Page 242: Rapid Application Development using Ruby on Rails

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

Page 243: Rapid Application Development using Ruby on Rails

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

Page 244: Rapid Application Development using Ruby on Rails

validates_presence_of

• Validates that the specified attributes are not empty.• class Person < ActiveRecord::Base• validates_presence_of :name, :login, :email• end

244

Page 245: Rapid Application Development using Ruby on Rails

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

Page 246: Rapid Application Development using Ruby on Rails

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

Page 247: Rapid Application Development using Ruby on Rails

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

Page 248: Rapid Application Development using Ruby on Rails

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

Page 249: Rapid Application Development using Ruby on Rails

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

Page 250: Rapid Application Development using Ruby on Rails

validates_numericality_of• Validates that your attributes have only numeric values.• Accepts the following options to add constraints to acceptable values:

• :only_integer - Specifies the value must be only integral values.• :greater_than – Specifies the value must be greater than the supplied value. • :greater_than_or_equal_to – Specifies the value must be greater than or equal

to the supplied value. • :equal_to – Specifies the value must be equal to the supplied value. • :less_than – Specifies the value must be less than the supplied value. • :less_than_or_equal_to – Specifies the value must be less than or equal the

supplied value. • :odd – Specifies the value must be an odd number if set to true. • :even – Specifies the value must be an even number if set to true.

• class Player < ActiveRecord::Base• validates_numericality_of :points• validates_numericality_of :games_played, :only_integer => true• end

250

Page 251: Rapid Application Development using Ruby on Rails

Conditional Validation

• Validate an object just when a given predicate is satisfied• class Order < ActiveRecord::Base• validates_presence_of :card_number, :if => :paid_with_card? • def paid_with_card?• payment_type == "card"• end• end

251

Page 252: Rapid Application Development using Ruby on Rails

Creating Custom Validation Methods• Simply create methods that verify the state of your models and add messages to the

errors collection when they are invalid. • You must then register these methods by using one or more of the validate,

validate_on_create or validate_on_update class methods, passing in the symbols for the validation methods’ names.

• class Invoice < ActiveRecord::Base• validate :expiration_date_cannot_be_in_the_past,• :discount_cannot_be_greater_than_total_value• def expiration_date_cannot_be_in_the_past• errors.add(:expiration_date, "can't be in the past") if• !expiration_date.blank? and expiration_date < Date.today• end• def discount_cannot_be_greater_than_total_value• errors.add(:discount, "can't be greater than total value") if• discount > total_value• end• end

252

Page 253: Rapid Application Development using Ruby on Rails

Displaying Validation Errors• error_messages - to render all failed validation messages for the current model instance.• <%= form_for(@product) do |f| %>• <%= f.error_messages %>• <p>• <%= f.label :description %><br />• <%= f.text_field :description %>• </p>• <p>• <%= f.label :value %><br />• <%= f.text_field :value %>• </p>• <p>• <%= f.submit "Create" %>• </p>• <% end %> 253

Scaffolding for generates public/stylesheets/scaffold.css, which defines the red-based style.

Page 254: Rapid Application Development using Ruby on Rails

Validation Errors CSS

• The selectors to customize the style of error messages are:• .field_with_errors – Style for the form fields and labels with

errors.• #errorExplanation – Style for the div element with the error

messages.• #errorExplanation h2 – Style for the header of the div

element.• #errorExplanation p – Style for the paragraph that holds the

message that appears right below the header of the div element.

• #errorExplanation ul li – Style for the list items with individual error messages. 254

Page 255: Rapid Application Development using Ruby on Rails

Live Demo - Contd

255

Page 256: Rapid Application Development using Ruby on Rails

Refactoring View

• app/views/posts/show.html.erb is getting long• Solution?

• Comments displayed should be separated out• Comments form should be separated out

256

Page 257: Rapid Application Development using Ruby on Rails

Refactoring View• app/views/posts/show.html.erb is getting long• Partials can be employed to refactor comments out

(app/views/comments/_comment.html.erb)• <p>• <b>Commenter:</b>• <%= comment.commenter %>• </p> • <p>• <b>Comment:</b>• <%= comment.body %>• </p>

• Update app/views/posts/show.html.erb• <h2>Comments</h2>• <%= render :partial => "comments/comment",• :collection => @post.comments %>

257

Page 258: Rapid Application Development using Ruby on Rails

Refactoring View• Comments form should also be moved to its own partial (app/views/comments/_form.html.erb)• <%= 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 %>• Update app/views/posts/show.html.erb• <h2>Add a comment:</h2>• <%= render "comments/form" %> 258

Page 259: Rapid Application Development using Ruby on Rails

Can view, and add comments. What about delete?

259

Page 260: Rapid Application Development using Ruby on Rails

Deleting Comments• Add link to delete comment in app/views/comments/_comment.html.erb• <p>• <%= link_to 'Destroy Comment', [comment.post, comment],• :confirm => 'Are you sure?',• :method => :delete %>• </p>• Add destroy method in CommentsController

(app/controller/comments_controller)• def destroy• @post = Post.find(params[:post_id])• @comment = @post.comments.find(params[:id])• @comment.destroy• redirect_to post_path(@post)• end 260

Page 261: Rapid Application Development using Ruby on Rails

Associated comments should get deleted when post is deleted?

261

Page 262: Rapid Application Development using Ruby on Rails

Dependent Destroy

• If you delete the post, then all associated comments should also get deleted.

• To achieve this, we can cascade the destruction of associated models.

• Modify the Post model, app/models/post.rb, to have dependent destroy of comments.

• has_many :comments, :dependent => :destroy

262

Page 263: Rapid Application Development using Ruby on Rails

Multi-Model Form

• Rails supports interacting with more than one model from a single form.

• Supports nested attributes for a referencing model within the same form

• For example, we want to tag a post when creating or editing.

263

Page 264: Rapid Application Development using Ruby on Rails

Creating Tags

• Create a model to hold Tags• rails generate model tag name:string post:references• Define association in Post model and specify that we want to

accept nested attributes for Tag Model.• has_many :tags• accepts_nested_attributes_for :tags, :allow_destroy

=> :true, :reject_if => :all_blank?• Render a partial in app/views/posts/_form.html to make a tag• <% @post.tags.build %>• <%= render :partial => 'tags/form', :locals => {:form =>

post_form} %>264

Page 265: Rapid Application Development using Ruby on Rails

Creating Tags• Create partial app/views/tags/_form.html.erb containing the form

fields for tags• <%= form.fields_for :tags do |tag_form| %>• <div class="field">• <%= tag_form.label :name, 'Tag:' %>• <%= tag_form.text_field :name %>• </div>• <% unless tag_form.object.nil? || tag_form.object.new_record? %>• <div class="field">• <%= tag_form.label :_destroy, 'Remove:' %>• <%= tag_form.check_box :_destroy %>• </div>• <% end %>• <% end %>

265

Page 266: Rapid Application Development using Ruby on Rails

Displaying Tags• Show associated tags for the posts (app/views/posts/show.html)• <p>• <b>Tags:</b>• <%= @post.tags.map { |t| t.name }.join(", ") %>• </p>

• Use View Helpers Instead to make code more cleaner• # app/helpers/posts_helper.rb• module PostsHelper• def join_tags(post)• post.tags.map { |t| t.name }.join(", ")• end• end• #app/views/posts/show.html• <p>• <b>Tags:</b>• <%= join_tags(@post) %>• </p>

266

Page 267: Rapid Application Development using Ruby on Rails

AJAX

267

Page 268: Rapid Application Development using Ruby on Rails

Introduction

• AJAX stands for Asynchronous JavaScript and XML• AJAX uses an XMLHttpRequest object that does HTTP requests

in the background. This avoids full page reloads and instead update parts of the page.

• This makes the application more responsive and interactive.

268

Page 269: Rapid Application Development using Ruby on Rails

Suitable Applications of Ajax

• Post something in the background and add it to a list on the page

• In-Place form editing• Autocompletion of a text field• Drag-and-drop sortable lists• Live searching

269

Page 270: Rapid Application Development using Ruby on Rails

Environment

• Notice app/views/layouts/application.html.erb• <%= javascript_include_tag :defaults %>

• public/javascripts• application.js• controls.js• dragdrop.js• effects.js• prototype.js• rails.js

270

Page 271: Rapid Application Development using Ruby on Rails

Unobtrusive Javascript Background• Before CSS, the presentation of HTML elements was defined

inline.• Then CSS come, and the document presentation attributes

were slowly moved outside the main HTML document.• Something similar is happening now with HTML and JavaScript.

• the most common way to append a JavaScript function was to use the HTML-Javascript bridges such as the onevent attributes.

• <a href="javascript:void(0);" onclick="alert('Thanks for clicking me');">Click me</a>

271

Page 272: Rapid Application Development using Ruby on Rails

Unobtrusive Javascript

• With the huge adoption of JavaScript programming, HTML documents fall again into the trap of mixing presentation/interaction elements within the page content and structure.

• The essence of the Unobtrusive JavaScript technique is to define the JavaScript interaction in a separate behavior layer.

• <a href="/domains/1" data-confirm="Are you sure?" data-method="delete" rel="nofollow">delete</a>

272

Page 273: Rapid Application Development using Ruby on Rails

UJS Benefits

• Separation of the behavior from the markup (as happened for CSS with the separation of the presentation from the markup)

• Reusable code (both HTML and JavaScript code)• Better cache support• Supporting Progressive enhancement by using web

technologies in a layered fashion that allows everyone to access the basic content and functionality of a web page.

• Allows developers to write code much more easier to maintain and debug

273

Page 274: Rapid Application Development using Ruby on Rails

Prototype -> JQuery

• JQuery is a more popular AJAX library and become the default in edge Rails (> 3.1).

• Setup Rails to use JQuery• Add gem 'jquery-rails', '>= 1.0.12' to Gemfile and run bundle

install• rails generate jquery:install --ui

• copy http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css to public/stylesheets/

274

Page 275: Rapid Application Development using Ruby on Rails

Posting Comments Through AJAX• Update view to do AJAX form submit

(app/views/comments/_form.html.erb)• Update CommentsController to respond to javascript• Update show post template to

• encapsulate comments in a container (div) that javascript can reference.

• create a container where notice for comment creation can be displayed

• Create create comment javascript template that• appends the new comment to the comments container

defined• displays the flash notice in the container for notice• clears the comments form

275

Page 276: Rapid Application Development using Ruby on Rails

Posting Comments Through AJAX• Update view to do AJAX form submit

(app/views/comments/_form.html.erb)• <%= form_for([@post, @post.comments.build], :remote =>

true) do |f| %>• Update CommentsController to respond to javascript• flash[:notice] = "Thanks for commenting!"• respond_to do |format|• format.html {redirect_to post_path(@post)}• format.js• end

276

Page 277: Rapid Application Development using Ruby on Rails

Posting Comments Through AJAX• Encapsulate code app/views/comments/_comment.html.erb

within div that will be updated through AJAX (optional)• <div id=comment_<%=comment.id%>>• ...• </div>• Define divs in app/views/posts/show.html.erb that will be

updated through AJAX• <div id="comments">• <%= render :partial => "comments/comment", :collection =>

@post.comments %>• </div>• <div id="comment-notice"></div>

277

Page 278: Rapid Application Development using Ruby on Rails

Posting Comments Through AJAX• Create javascript view to do some rendering and apply effects

(app/views/comments/create.js.erb)• /* Insert a notice between the last comment and the comment form */• $("#comment-notice").html('<div class="flash notice"><%=

escape_javascript(flash.delete(:notice)) %></div>');

• /* Add the new comment to the bottom of the comments list */• $("#comments").append("<%= escape_javascript(render(@comment))

%>");

• /* Highlight the new comment */• $("#comment_<%= @comment.id %>").effect("highlight", {}, 3000);

• /* Reset the comment form */• $("#new_comment")[0].reset();

278

Page 279: Rapid Application Development using Ruby on Rails

Deleting Comments Through AJAX• Update view to make an AJAX call for delete

(app/views/comments/_comment.html.erb)• Update CommentsController to respond to javascript• Create destroy javascript template for comments to apply some

effects

279

Page 280: Rapid Application Development using Ruby on Rails

Deleting Comments Through AJAX• Update view to make an AJAX call for delete

(app/views/comments/_comment.html.erb)• <%= link_to 'Remove Comment', [comment.post, comment],• :confirm => 'Are you sure?', :method => :delete, :remote => true %>• Update CommentsController to respond to javascript• respond_to do |format|• format.html {redirect_to post_path(@post)}• format.js• end• Create destroy javascript template for comments to apply some effects• /* Eliminate the comment by fading it out */• $('#comment_<%= @comment.id %>').fadeOut();

280

Page 281: Rapid Application Development using Ruby on Rails

Deleting Post Through Ajax (Optional)• Create Javascript handler public/javascripts/posts.js• $(function() {• $('.delete_post').bind('ajax:success', function() { • $(this).closest('tr').fadeOut(); • });• });• Update view to include the javascript file• <% content_for :head do %>• <title>Posts</title>• <%= javascript_include_tag 'posts'%>• <% end %>• Update view to make an AJAX call for delete (app/views/posts/index.html.erb)• <%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete, :remote => true, :class =

‘delete_post’ %>• Update PostsController’s destroy method to respond to javascript with no body. • respond_to do |format|• format.html { redirect_to(posts_url) } • format.js { render :nothing => true } • end

281

Page 282: Rapid Application Development using Ruby on Rails

Security

282

Page 283: Rapid Application Development using Ruby on Rails

Password Encryption• Rather than storing a raw password in the database (known as

“cleartext”), we store a string generated using a cryptographic hash function, which is essentially irreversible,

• Even an attacker in possession of the hashed password will be unable to infer the original!

• #try in rails console• require 'digest'• password = 'secret'• encrypted_password = Digest::SHA2.hexdigest(secret)• If an attacker ever got hold of the encrypted password, they would still

have a chance at discovering the originals (known as rainbow attack).• For example, attacker guesses that we used SHA2, and writes a

function to compare a given hash to the hashed values of potential passwords using perhaps english dictionary in combination with other brute force techniques which itself becomes a library of potential passwords!

283

Page 284: Rapid Application Development using Ruby on Rails

Password Encryption with Salt• Create on the fly value (using Time perhaps), join it with the

original password and encrypt it to create ‘salt’.• Add the ‘salt’ to the original password and encrypt it again.• Result is a unique encrypted password which is unique to your

deployment only.• salt = Digest::SHA2.hexdigest("#{Time.now.utc}--#{password}")• encrypted_password = Digest::SHA2.hexdigest(("#{salt}--

#{password}")• Both salt and encrypted_password are stored in db so that

provided password can be checked.

284

Page 285: Rapid Application Development using Ruby on Rails

Model Testing - Password Encryption• #spec/models/user_spec.rb• require 'spec_helper'• describe User do• ...• describe "password encryption" do

• before(:each) do• @user = User.create!(@attr)• end

• it "should have an encrypted password attribute" do• @user.should respond_to(:encrypted_password)• end

• it "should set the encrypted password" do• @user.encrypted_password.should_not be_blank• end• end• end

285

Page 286: Rapid Application Development using Ruby on Rails

Model Testing - Has Password• #spec/models/user_spec.rb• require 'spec_helper'• describe User do• ...• describe "password encryption" do• before(:each) do• @user = User.create!(@attr)• end• ...• describe "has_password? method" do• ...• it "should be true if the passwords match" do• @user.has_password?(@attr[:password]).should be_true• end

• it "should be false if the passwords don't match" do• @user.has_password?("#{@attr[:password]}-invalid").should be_false• end • end• end• end

286

Page 287: Rapid Application Development using Ruby on Rails

Secure Password Implementation• $ rails generate migration add_password_and_salt_to_users salt:string encrypted_password:string

• #app/models/user.rb• require 'digest'• class User < ActiveRecord::Base• ...• before_save :encrypt_password• ...• # Return true if the user's password matches the submitted password.• # Compares encrypted_password with the encrypted version of submitted_password. • def has_password?(submitted_password)• encrypted_password == encrypt(submitted_password)• end

• private• def encrypt_password• self.salt = make_salt if new_record?• self.encrypted_password = encrypt(password)• end

• def encrypt(string)• Digest::SHA2.hexdigest("#{salt}--#{string}")• end

• def make_salt• Digest::SHA2.hexdigest("#{Time.now.utc}--#{password}")• end• end

287

Page 288: Rapid Application Development using Ruby on Rails

Autheticate Method

• Should take in email and password.• Returns the user if password matched, nil otherwise

288

Page 289: Rapid Application Development using Ruby on Rails

Model Testing - Authenticate• describe User do• ...• describe "password encryption" do• ...• describe "authenticate method" do• it "should return nil on email/password mismatch" do• wrong_password_user = User.authenticate(@attr[:email], "wrongpass")• wrong_password_user.should be_nil• end

• it "should return nil for an email address with no user" do• nonexistent_user = User.authenticate("[email protected]", @attr[:password])• nonexistent_user.should be_nil• end

• it "should return the user on email/password match" do• matching_user = User.authenticate(@attr[:email], @attr[:password])• matching_user.should == @user• end• end• end• end 289

Page 290: Rapid Application Development using Ruby on Rails

Autheticate Method Implementation• class User < ActiveRecord::Base• ...• def self.authenticate(email, submitted_password)• user = find_by_email(email)• return nil if user.nil?• return user if user.has_password?(submitted_password)• end• private• ...• end

290

Page 291: Rapid Application Development using Ruby on Rails

Log Files Security Breach

• Even though we went to great pains to encrypt the password, both the password and its confirmation *may* have appeared as clear in the debug information

• If anyone ever got a hold of the file, they would potentially obtain the passwords for every user on the system!

• #log/development.log• Parameters: {"commit"=>"Sign up", "action"=>"create",• "authenticity_token"=>"K1HchFF8uYE8ZaQKz5DVG9vF2KGoXJu4J

Gp/VE3NMjA=",• "controller"=>"users",• "user"=>{"name"=>"Foo Bar",

"password_confirmation"=>"[FILTERED]",• "password"=>"[FILTERED]", "email"=>"foo@invalid"}}

291

Page 292: Rapid Application Development using Ruby on Rails

Filtering Parameter Logging

• Rails provides mechanism to filter defined parameters from the log file.

• By default, all password attributes are filtered automatically in Rails 3.• notice setting in config/application.rb• config.filter_parameters += [:password]

• Any other parameter that need filtering (example, credit card information), should be added into the array

292

Page 293: Rapid Application Development using Ruby on Rails

Day 4

293

Page 294: Rapid Application Development using Ruby on Rails

Cookies

• A hash stored by the browser• cookies[:preference] = { :value => ‘icecream’, :expires =>

10.days.from_now, :path => ‘/store’ }• Valid options:

• :domain, :expires, :path, :secure, :value• Secure the cookie so that value in it is not exposed:

• cookies.signed[:remember_token] = user.id

294

Page 295: Rapid Application Development using Ruby on Rails

Sessions

• A hash stored on the server, typically in a database table or in the file system.

• Keyed by the cookie _session_id that expires upon browser close.

• Avoid storing complex Ruby objects, instead put ids in the session and keep data in the database, i.e. use session[:user_id] rather than session[:user]

295

Page 296: Rapid Application Development using Ruby on Rails

Implementing Sign-in/Sign-out w/ Remember• Allow user to sign-in/sign-out without using session• Demonstrate use of secure cookies to implement sign-in that

lasts even after browser close.

296

Page 297: Rapid Application Development using Ruby on Rails

Sessions Helper• #app/helpers/sessions_helper.rb• module SessionsHelper• def signed_in?• !current_user.nil?• end• def sign_in(user)• cookies.permanent.signed[:remember_token] = [user.id, user.salt]• self.current_user = user• end• def sign_out• cookies.delete(:remember_token)• self.current_user = nil• end• def current_user=(user)• @current_user = user• end• def current_user• @current_user ||= user_from_remember_token• end• private

• def user_from_remember_token• User.authenticate_with_salt(*remember_token)• end

• def remember_token• cookies.signed[:remember_token] || [nil, nil]• end

• end

297

Page 298: Rapid Application Development using Ruby on Rails

Sessions Controller• $rails generate controller Sessions new• #config/routes.rb• SampleApp::Application.routes.draw do• resources :users• resources :sessions, :only => [:new, :create, :destroy]

• match '/signup', :to => 'users#new'• match '/signin', :to => 'sessions#new'• match '/signout', :to => 'sessions#destroy'• ...• end

• #app/views/sessions/new.html.erb• <h1>Sign in</h1>

• <%= form_for(:session, :url => sessions_path) do |f| %>• <div class="field">• <%= f.label :email %><br />• <%= f.text_field :email %>• </div>• <div class="field">• <%= f.label :password %><br />• <%= f.password_field :password %>• </div>• <div class="actions">• <%= f.submit "Sign in" %>• </div>• <% end %>• <p>New user? <%= link_to "Sign up now!", signup_path %></p>

298

Page 299: Rapid Application Development using Ruby on Rails

Sessions Controller Example• #app/controllers/sessions_controller• class SessionsController < ApplicationController• include SessionsHelper• def new• render 'new'• end

• def create• user = User.authenticate(

• params[:session][:email],• params[:session][:password])• if user.nil?• flash.now[:error] = "Invalid email/password combination."• @title = "Sign in"• render 'new'• else• sign_in user• redirect_to user• end• end

• def destroy• sign_out• redirect_to root_path• end• end

299

Page 300: Rapid Application Development using Ruby on Rails

Including SessionsHelper

• By default, all the helpers are available in the views but not in the controllers.

• We need the methods from the Sessions helper in both places, so we have to include it explicitly

300

Page 301: Rapid Application Development using Ruby on Rails

Authenticate Method

• #app/model/user.rb• class User < ActiveRecord• def self.authenticate_with_salt(id, cookie_salt)• user = find_by_id(id)• (user && user.salt == cookie_salt) ? user : nil• end• end

301

Page 302: Rapid Application Development using Ruby on Rails

Capistrano

302

Page 303: Rapid Application Development using Ruby on Rails

Capistrano

• Capistrano is a wonderful program for deploying Rails 3 apps.• Allows for great customization and is very simple to use. • It is very flexible and will work with almost any version control

system out there.

303

Page 304: Rapid Application Development using Ruby on Rails

Setup Capistrano

• Install Capistrano with the following command:• gem install capistrano

• Open up your Gemfile and add:• gem 'capistrano'

• Initialize Capistrano for project by running:• capify .

• This creates two files Capfile and config/deploy.rb

304

Page 305: Rapid Application Development using Ruby on Rails

Noteworthy Settings

• default_run_options[:pty] = true # Must be set for the password prompt from git to work

• set :repository, "ssh://[user@]host/~/projectdir.git" # Your clone URL

• set :scm, "git"• set :user, "deployer" # The server's user for deploys• set :scm_passphrase, "p@ssw0rd" # The deploy user's password• Remote Cache/Export

• set :deploy_via, :remote_cache• set :deploy_via, :checkout

• Roles allow you to write capistrano tasks that only apply to certain servers. This really only applies to multi-server deployments. 305

Page 306: Rapid Application Development using Ruby on Rails

Running Capistrano w/ Bundler• Add the line to config/deploy.rb

• require "bundler/capistrano"• Running cap deploy will now automatically run bundle install

on the remote server with deployment-friendly options. As list of options that can be changed is available in the help for the cap task. To see it, run cap -e bundle:install.

306

Page 307: Rapid Application Development using Ruby on Rails

Deploy.rb• require 'bundler/capistrano'• set :user, 'username'• set :domain, 'web.host.com'• set :applicationdir, "appdir"• • set :scm, 'git'• set :repository, "ssh://[user@]host/~/projectdir.git"• set :git_enable_submodules, 1 # if you have vendored rails• set :branch, 'master'• set :git_shallow_clone, 1• set :scm_verbose, true • # roles (servers)• role :web, domain• role :app, domain• role :db, domain, :primary => true•

• # deploy config• set :deploy_to, applicationdir• set :deploy_via, :export• • # additional settings• default_run_options[:pty] = true # Forgo errors when deploying from windows• #ssh_options[:keys] = %w(/home/user/.ssh/id_rsa) # If you are using ssh_keysset :chmod755, "app config db lib public vendor script script/* public/disp*"set :use_sudo,

false• • # Passenger• namespace :deploy do• task :start do ; end• task :stop do ; end• task :restart, :roles => :app, :except => { :no_release => true } do• run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}"• end• end

307

Page 308: Rapid Application Development using Ruby on Rails

Example: Local Deploy• require 'bundler/capistrano'• set :application, "blog"• set :repository, "/Users/hisham/projects/test/#{application}"• set :scm, :none• set :deploy_to, "/Users/hisham/projects/#{application}"• role :web, "localhost" # Your HTTP server, Apache/etc• role :app, "localhost" • role :db, "localhost", :primary => true # This is where Rails migrations will

run• set :use_sudo, false• set :default_environment, {• 'PATH' => "/usr/local/Cellar/ruby/1.9.2-p180/bin:$PATH"• }• namespace :deploy do• task :restart, :roles => :app, :except => { :no_release => true } do

308

Page 309: Rapid Application Development using Ruby on Rails

Caching

309

Page 310: Rapid Application Development using Ruby on Rails

RoR Caching

• Page Caching• Action Caching• Fragment Caching• set config.action_controller.perform_caching = true in

config/environments/*

310

Page 311: Rapid Application Development using Ruby on Rails

Page Caching

• Allows the request for a generated page to be fulfilled by the webserver.

• Automatically add a .html extension to requests for pages that do not have an extension to make it easy for the webserver to find those pages• Configurable through

config.action_controller.page_cache_extension• By default, the page cache directory is set to Rails.public_path

• Configurable through config.action_controller.page_cache_directory

• Page caches are always stored on disk.311

Page 312: Rapid Application Development using Ruby on Rails

Products Page Caching

• class ProductsController < ActionController• caches_page :index• def index• @products = Products.all• end• def create• expire_page :action => :index• end• end

312

Page 313: Rapid Application Development using Ruby on Rails

Page Caching Pros/Cons

• + Almost as fast as static page serving as Rails stack is completely by-passed.

• - Cache expiration is an issue• - Ignores all parameters passed

• /products?page=1 and /products?page=2 will direct to same products.html

• - Cannot be used for private/restricted pages. For example, user custom home page.

• + Cache sweepers can be used to expire cached objects to support a more sophisticated expiration scheme.

313

Page 314: Rapid Application Development using Ruby on Rails

Action Caching

• Action Caching works like Page Caching except for the fact that the incoming web request does go from the webserver to the Rails stack and Action Pack so that before filters can be run on it before the cache is served.

• This allows authentication and other restriction to be run while still serving the result of the output from a cached copy.

• Action caching uses fragment caching internally• A page that is accessed at hisham.confiz.com/lists/show/1

will result in a fragment named hisham.confiz.com/lists/show/1

• Different representations of the same resource based on format. For example hisham.confiz.com/lists/show/1.xml 314

Page 315: Rapid Application Development using Ruby on Rails

Products Action Caching

• class ProductsController < ActionController • before_filter :authenticate• caches_action :index• def index• @products = Product.all• end• def create• expire_action :action => :index• end • end

315

Page 316: Rapid Application Development using Ruby on Rails

Conditional Action Caching

• Use :if (or :unless) to pass a Proc that specifies when the action should be cached

• Use :layout => false to cache without layout so that dynamic information in the layout such as logged in user info or the number of items in the cart can be left uncached

• You can modify the default action cache path by passing a :cache_path option

• If you are using memcached, you can also pass :expires_in• If you pass :layout => false, it will only cache your action

content.

316

Page 317: Rapid Application Development using Ruby on Rails

Examples

• class ListsController < ApplicationController• before_filter :authenticate, :except => :public• caches_page :public• caches_action :index, :if => proc do |c|• !c.request.format.json? # cache if is not a JSON request• end• caches_action :show, :cache_path => { :project => 1 },

• :expires_in => 1.hour• caches_action :feeds, :cache_path => Proc.new { |c|

c.params }• end 317

Page 318: Rapid Application Development using Ruby on Rails

Action Caching Pros/Cons

• - Slower than Page Caching• + Allows you to restrict access to cached pages • + Allows you to cache differently based on parameters and

other conditions• + Allows conditional caching• - Allows control over main layout getting cached or not, but not

selective caching of components with the page

318

Page 319: Rapid Application Development using Ruby on Rails

Fragment Caching

• Fragment Caching allows a fragment of view logic to be wrapped in a cache block and served out of the cache store when the next request comes in.

• #View• <% Order.find_recent.each do |o| %>• <%= o.buyer.name %> bought <% o.product.name %>• <% end %> • <% cache do %>• All available products:• <% Product.all.each do |p| %>• <%= link_to p.name, product_url(p) %>• <% end %>• <% end %>

319

Page 320: Rapid Application Development using Ruby on Rails

Multiple Fragment Caching

• The cache block in previous example will bind to the action that called it and is written out to the same place as the Action Cache.

• Multiple fragments per action can be supported by • providing action_suffix to the cache call• <% cache(:action => 'recent', :action_suffix => 'all_products')

do %>• All available products:• <% end %>• using globally keyed fragments• <% cache('all_available_products') do %>• All available products:• <% end %>

320

Page 321: Rapid Application Development using Ruby on Rails

Expiring Fragment Cache

• #controller• expire_fragment(:controller => 'products', :action =>

'recent', :action_suffix => 'all_products')• expire_fragment('all_available_products')• #view• cache({:action => 'recent', :action_suffix =>

'all_products'}, :expires => 5.minutes)• cache('all_available_products', :expires => 5.minutes)

321

Page 322: Rapid Application Development using Ruby on Rails

SQL Caching

• If Rails encounters the same query again for that request, it will use the cached result set as opposed to running the query against the database again.

• Query caches are created at the start of an action and destroyed at the end of that action and thus persist only for the duration of the action.

322

Page 323: Rapid Application Development using Ruby on Rails

Cache Stores• Different storing options available for the cached data created by action and fragment

caches.• ActiveSupport::Cache::MemoryStore

• ActionController::Base.cache_store = :memory_store• ActiveSupport::Cache::SynchronizedMemoryStore

• ActionController::Base.cache_store = :synchronized_memory_store• ActiveSupport::Cache::FileStore

• ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"• ActiveSupport::Cache::DRbStore

• ActionController::Base.cache_store = :drb_store, "druby://localhost:9192"• ActiveSupport::Cache::MemCacheStore

• ActionController::Base.cache_store = :mem_cache_store, "localhost"• ActiveSupport::Cache::CompressedMemCacheStore

• ActionController::Base.cache_store = :compressed_mem_cache_store, "localhost"

• config.cache_store can be used in place of ActionController::Base.cache_store in Rails::Initializer.run block ienvironment.rb

323

Page 324: Rapid Application Development using Ruby on Rails

Low-level Caching

• You can access these cache stores at a low level for storing queries and other objects.

• Rails.cache.read(‘city’) # => nil• Rails.cache.write(‘city’, ‘Lahore’)• Rails.cache.read(‘city’) # => "Lahore"

324

Page 325: Rapid Application Development using Ruby on Rails

Database Optimizations

325

Page 326: Rapid Application Development using Ruby on Rails

N+1 Query Problem

• The code below executes 1 ( to find 10 clients ) + 10 ( one per each client to load the address ) = 11 queries in total

• clients = Client.all(:limit => 10) • clients.each do |client|• puts client.address.postcode• end

326

Page 327: Rapid Application Development using Ruby on Rails

Solution: Eager Loading

• With includes, Active Record ensures that all of the specified associations are loaded using the minimum possible number of queries.

• clients = Client.includes(:address).limit(10) • clients.each do |client|• puts client.address.postcode• end• #SELECT * FROM clients LIMIT 10• #SELECT addresses.* FROM addresses WHERE

(addresses.client_id IN (1,2,3,4,5,6,7,8,9,10))

327

Page 328: Rapid Application Development using Ruby on Rails

Eager Loading - Multiple Associations• Array of Multiple Associations

• Post.includes(:category, :comments)• Nested Associations

• Category.includes(:posts => [{:comments => :guest}, :tags]).limit(1)• Posts has many comments, comments belong to guests,

posts has many tags

328

Page 329: Rapid Application Development using Ruby on Rails

Eager Loading - Searching

• ActiveRecords allows you to provide nested conditions that match

• Group.includes(:group_members).where(:name=>'punjabians',:group_members=>{:is_approved => false})

329

Page 330: Rapid Application Development using Ruby on Rails

Database Indexing

330

Page 331: Rapid Application Development using Ruby on Rails

Overview

• A database index is a data structure that improves the speed of operations on a database table - Wikipedia

• For every index on a table, there is a penalty both when inserting and updating rows. Indexes also take up space on disk and in memory, which can affect the efficiency of queries.

• Having too many indexes can cause databases to choose between them poorly, actually harming performance rather than improving it.

331

Page 332: Rapid Application Development using Ruby on Rails

Indexing Simple Associations• Not indexing foreign keys can cripple your app• class User < ActiveRecord::Base• has_many :conversations• end• class Conversation < ActiveRecord::Base• belongs_to :user• end• #add index• add_index :conversations, :user_id

332

Page 333: Rapid Application Development using Ruby on Rails

Indexing Polymorphic Associations• For polymorphic associations the foreign key is made up of two

columns, one for the id and one for the type• class Artwork < ActiveRecord::Base• has_one :conversation, :as => :subject• end• class Conversation < ActiveRecord::Base• belongs_to :subject, :polymorphic => true• end• #add composite index• add_index :conversations, [:subject_id, :subject_type]

333

Page 334: Rapid Application Development using Ruby on Rails

Summary• If you have a belongs_to, has_many, has_one, or has_and_belongs_to_many association on a

model), then you almost certainly need to add an index for it.• If you find yourself frequently doing queries on a non-foreign-key column (like user_name or

permalink), you’ll definitely want an index on that column.• If you frequently sort on a column or combination of columns, make sure the index that is

being used for the query includes those sort columns, too (if at all possible). • Many databases (like MySQL, or Postgres prior to 8.1) will only use a single index per table,

per query, so make sure you have indexes defined for the column combinations that you will query on frequently. • A common mistake is to define an index on “user_name” and an index on “account_id”,

and then expect the database to use both indexes to satisfy a query that references both columns. (Some databases will use both indexes, though; be sure and understand how your DBMS uses indexes.)

• Too many indexes can be just as bad as too few, since the DB has to try and determine which of the myriad indexes to use to satisfy a particular query. • Also, indexes consume disk space, and they have to be kept in sync every time an insert,

delete, or update statement is executed. Lots of indexes means lots of overhead, so try to strike a good balance.

• Start with only the indexes you absolutely need, and try to use only those. As problem queries surface, see if they can be rewritten to use existing indexes, and only if they can’t should you go ahead and add indexes to fix them.

334

Page 335: Rapid Application Development using Ruby on Rails

Amazon S3

335

Page 336: Rapid Application Development using Ruby on Rails

Amazon S3

• #copied from http://aws.amazon.com/s3/• Amazon S3 is storage for the Internet. • It is designed to make web-scale computing easier for

developers.• Amazon S3 provides a simple web services interface that can

be used to store and retrieve any amount of data, at any time, from anywhere on the web.

• It gives any developer access to the same highly scalable, reliable, secure, fast, inexpensive infrastructure that Amazon uses to run its own global network of web sites.

• The service aims to maximize benefits of scale and to pass those benefits on to developers. 336

Page 337: Rapid Application Development using Ruby on Rails

AWS Gem

• gem install aws-s3• Allows you to CRUD operations on buckets, and S3Objects.• S3Objects represent the data you store on S3

• S3Object.store('me.jpg', open('headshot.jpg'), 'photos')• S3Object.url_for('beluga_baby.jpg', 'marcel_molina')

• By default authenticated urls expire 5 minutes after they were generated.

337

Page 338: Rapid Application Development using Ruby on Rails

Paperclip

338

Page 339: Rapid Application Development using Ruby on Rails

Paperclip

• Paperclip is intended as an easy file attachment library for ActiveRecord.

• The intent behind it was to keep setup as easy as possible and to treat files as much like other attributes as possible.

• This means they aren't saved to their final locations on disk, nor are they deleted if set to nil, until ActiveRecord::Base#save is called.

• It manages validations based on size and presence, if required. • It can transform its assigned image into thumbnails if needed. • Attached files are saved to the filesystem and referenced in the

browser by an easily understandable specification, which has sensible and useful defaults. 339

Page 340: Rapid Application Development using Ruby on Rails

Usage• Declare that your model has an attachment with the has_attached_file

method, and give it a name. • Paperclip will wrap up up to four attributes (all prefixed with that

attachment's name, so you can have multiple attachments per model if you wish) and give them a friendly front end.

• The attributes are • <attachment>_file_name, • <attachment>_file_size, • <attachment>_content_type, and • <attachment>_updated_at. • Only <attachment>_file_name is required for paperclip to operate.

• Attachments can be validated with Paperclip's validation methods, • validates_attachment_presence, • validates_attachment_content_type, and • validates_attachment_size.

340

Page 341: Rapid Application Development using Ruby on Rails

Storage

• The files that are assigned as attachments are, by default, placed in the directory specified by the :path option to has_attached_file.

• By default, this location is ":rails_root/public/system/:attachment/:id/:style/:filename"• On standard Capistrano deployments, the public/system

directory is symlinked to the app's shared directory• Files may be stored using Amazon’s S3 Service

341

Page 342: Rapid Application Development using Ruby on Rails

Setting up Paperclip

• ImageMagick must be installed• Include the gem in your Gemfile:

• gem "paperclip", "~> 2.3"

342

Page 343: Rapid Application Development using Ruby on Rails

User Avatar Example

• Create file association in model• class User < ActiveRecord::Base• has_attached_file :avatar, :styles => { :medium => "300x300>",

:thumb => "100x100>" }• validates_attachment_presence :avatar• ...• end• Create migration to add field columns

• rails generate migration add_avatar_columns_to_users avatar_file_name:string avatar_content_type:string avatar_file_size:integer avatar_updated_at:datetime

343

Page 344: Rapid Application Development using Ruby on Rails

User Avatar Example

• Update user form partial:• <% form_for :user, @user, :url => user_path, :html =>

{ :multipart => true } do |form| %>• ....• <%= form.file_field :avatar %>• ....• <% end %>• Update user show view to display avatar• <%= image_tag @user.avatar.url %>• <%= image_tag @user.avatar.url(:medium) %>• <%= image_tag @user.avatar.url(:thumb) %> 344

Page 345: Rapid Application Development using Ruby on Rails

Using Amazon S3 for Storage• Add to Gemfile• gem ‘aws-s3’• Create config/s3.yml• development:• bucket: bucket-dev• access_key_id: xxx• secret_access_key: xxx• test:• bucket: bucket-test• access_key_id: xxx• secret_access_key: xxx• production:• bucket: bucket-pro• access_key_id: xxx• secret_access_key: xxx• Specify s3 storage and s3 credentials for avatar in User Model• #app/models/user.rb• class User < ActiveRecord::Base• has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" },• :storage => :s3,• :s3_credentials => "#{RAILS_ROOT}/config/s3.yml",• :path => "/:style/:id/:filename"• ...• end

345

Page 346: Rapid Application Development using Ruby on Rails

Paperclip and S3

• The default permission used by Paperclip is :public_read• You can set permission on a per style bases by doing the following:• :s3_permissions => {• :original => :private• }• Or globaly:• :s3_permissions => :private• Can use url that expires after sometime to keep privacy

• <%= image_tag @user.avatar.expiring_url %>• <%= image_tag @user.avatar.url(3600, :medium) %>• <%= image_tag @user.avatar.url(3600, :thumb) %>

346

Page 347: Rapid Application Development using Ruby on Rails

Attaching Videos• class User < ActiveRecord::Base• has_attached_file :video, • :storage => :s3,• :s3_credentials => "#{RAILS_ROOT}/config/s3.yml",• :path => ":attachment/:id/:style/:basename.:extension",• :bucket => 'yourbucketname'

• # Paperclip Validations• validates_attachment_presence :video• validates_attachment_content_type :video, :content_type =>

['application/x-shockwave-flash', 'application/x-shockwave-flash', 'application/flv', 'video/x-flv']

• end• #For detailed example: http://scottmotte.com/archives/181.html

347

Page 348: Rapid Application Development using Ruby on Rails

Master/Slave Replication

348

Page 349: Rapid Application Development using Ruby on Rails

Octopus

• Ruby gem that allows an ActiveRecord application (including ActiveRecord 3/Rails 3 applications) to connect to one master and multiple slave databases.

• Most of the documentation centers on its sharding features, but is also very useful for sending writes to the master and reads to the slaves.

349

Page 350: Rapid Application Development using Ruby on Rails

Octopus Setup

• Add line in Gemfile and run bundle install• gem ‘ar-octopus’, :require => “octopus”

• Create config/shards.yml• If Octopus finds the directive replicated: true, it will assume

all shards as slaves, and the database specified in database.yml as master database.

• In a normal setup, octopus will just use replication for models that are marked as replicated_model. So, if you want to send all writes queries will be sent to master, and all reads queries to slaves, you need to add fully_replicated: true

350

Page 351: Rapid Application Development using Ruby on Rails

Sample shards.yml• #config/shards.yml• octopus:• replicated: true• fully_replicated: true• environments:• - development• - production• development:• slave1:• adapter: mysql2• encoding: utf8• host: localhost• database: confiz_slave• username: hisham• password: confiz• production:• slave1:• adapter: mysql2• encoding: utf8• host: 123.456.789.10• database: ufone_production• username: hisham• password: confiz

351

Page 352: Rapid Application Development using Ruby on Rails

Grids

352

Page 353: Rapid Application Development using Ruby on Rails

WiceGrid=========• Allows the programmer to define the contents of the cell by himself, just like one does

when rendering a collection via a simple table (and this is what differentiates WiceGrid from various scaffolding solutions), but automate implementation of filters, ordering, paginations, CSV export, and so on. Ruby blocks provide an elegant means for this.

• WiceGrid builds the call to the ActiveRecord layer for you and creates a table view with the results of the call including:• paging• sortable columns• filtering by multiple columns• CSV export• saving queries

• Filters are added automatically according to the type of the underlying DB column. • Filtering by more than one column at the same time is possible. • More than one such grid can appear on a page, and manipulations with one grid do not

have any impact on the other.• WiceGrid does not take a collection as an input, it works directly with ActiveRecord

(with the help of will_paginate).353

Page 354: Rapid Application Development using Ruby on Rails

WiceGrid Setup

• Add to Gemfile and run bundle install• gem 'wice_grid', '3.0.0.pre1'

• Run the generator tasks to copy assets• rails g wice_grid_assets_jquery

354

Page 355: Rapid Application Development using Ruby on Rails

initialize_grid• Creates a grid object to be used in the view. This is the main model, and the underlying table is the default table - if in other parameters a column name is mentioned without the

name of the table, this table is implied. Just like in an ordinary ActiveRecord find you can use :joins, :include, and :conditions.• The first parameter is an ActiveRecord class name. The generated ActiveRecord call will use it as the receiver of the paginate method: klass.paginate(...)

• The second parameters is a hash of parameters:• :joins - ActiveRecord :joins option.• :include - ActiveRecord :include option.• :conditions - ActiveRecord :conditions option.• :per_page - Number of rows per one page. The default is 10.• :page - The page to show when rendering the grid for the first time. The default is one, naturally.• :order - Name of the column to sort by. Can be of a short form (just the name of the column) if this is a column of the main table (the table of the main ActiveRecord model,

the first parameter of initialize_grid), or a fully qualified name with the name of the table.• :order_direction - :asc for ascending or :desc for descending. The default is :asc.• :name - name of the grid. Only needed if there is a second grid on a page. The name serves as the base name for HTTP parametes, DOM IDs, etc. The shorter the name, the

shorter the GET request is. The name can only contain alphanumeruc characters.• :enable_export_to_csv - <Enable export of the table to CSV. Read the How-To to learn what else is needed to enable CSV export.• :csv_file_name - Name of the exported CSV file. If the parameter is missing, the name of the grid will be used instead.• :custom_order - used for overriding the ORDER BY clause with custom sql code (for example, including a function). The value of the parameter is a hash where keys are fully

qualified names of database columns, and values the required chunks of SQL to use in the ORDER BY clause, either as strings or Proc object evaluating to string. See section ‘Custom Ordering’ in the README.

• :saved_query - id of the saved query or the query object itself to load initially. Read section "Saving Queries How-To" in README for more details.• :after - defined a name of a controller method which would be called by the grid after all user input has been processed, with a single parameter which is a Proc object. Once

called, the object returns a list of all records of the current selection throughout all pages. See section "Integration With The Application" in the README.• :total_entries - If not specified, will_paginate will run a select count• :select - ActiveRecord :select option. Please do not forget that :select is ignored when :include is present. It is unlikely you would need :select with WiceGrid, but if you do, use it

with care :)• :group - ActiveRecord :group option. Use it if you are sure you know what you are doing :)• :with_paginated_resultset - a callback executed from within the plugin to process records of the current page. Can be a lambda object or a controller method name (symbol). The

argument to the callback is the array of the records.• :with_resultset - a callback executed from within the plugin to process all records browsable through all pages with the current filters. Can be a lambda object or a controller

method name (symbol). The argument to the callback is a lambda object which returns the list of records when called. See the README for the explanation.• Defaults for parameters :per_page, :order_direction, :name, and :erb_mode can be changed in lib/wice_grid_config.rb, this is convenient if you want to set a project wide setting

without having to repeat it for every grid instance.355

Page 356: Rapid Application Development using Ruby on Rails

initialize_grid options contd.

• :enable_export_to_csv - Enable export of the table to CSV. • :csv_file_name - Name of the exported CSV file. If the

parameter is missing, the name of the grid will be used instead.• :total_entries - If not specified, will_paginate will run a select

count• Defaults for parameters :per_page, :order_direction, :name,

and :erb_mode can be changed in lib/wice_grid_config.rb, this is convenient if you want to set a project wide setting without having to repeat it for every grid instance.

356

Page 357: Rapid Application Development using Ruby on Rails

Controller Method

• Controller• def list• @posts = initialize_grid(Post,• :name => "post",• :page => params[:page],• :include => :tags,• :allow_showing_all_records => false,• :conditions => ["created_at >= ?", Time.now - 10.minutes],• :enable_export_to_csv => true,• :csv_file_name => 'members',• :per_page => 100)• export_grid_if_requested• end

357

Page 358: Rapid Application Development using Ruby on Rails

View• <%= grid(@tasks_grid) do |g|• g.column :column_name => 'ID', :attribute_name => 'id' do |task|• task.id• end• g.column :column_name => 'Title', :attribute_name => 'title' do |task|• task.title• end• g.column :column_name => 'Description', :attribute_name => 'description' do |task|• task.description• end• g.column :column_name => 'Archived', :attribute_name => 'archived' do |task|• task.archived? ? 'Yes' : 'No'• end• g.column :column_name => 'Added', :attribute_name => 'created_at' do |task|• task.created_at.to_s(:short)• end• g.column do |task|• link_to('Edit', edit_task_path(task))• end• end -%> 358

Page 359: Rapid Application Development using Ruby on Rails

JQGrid

• Ajax-enabled JavaScript control that provides solutions for representing and manipulating tabular data on the web.

• Can be integrated with any server-side technology, including PHP, ASP, Java Servlets, JSP, ColdFusion, and Perl.

• Download: http://www.trirand.com/blog/?page_id=6

359

Page 360: Rapid Application Development using Ruby on Rails

Thinking Sphinx

360

Page 361: Rapid Application Development using Ruby on Rails

Sphinx

• A search engine that indexes data, and then you can query it with search terms to find out which documents are relevant.

• Sphinx is implemented by more than 100 web sites and services, including craigslist.org, scribd.org, mozilla, metacafe, wordpress.org

• Also used in PakWheels.com and Naitazi.com for searching

361

Page 362: Rapid Application Development using Ruby on Rails

Advantages of Sphinx

• Full Text Searching with performance, relevance (search quality), and integration simplicity in mind!

• Sphinx lets you either batch index and search data stored in an SQL database, or index and search data on the fly.

• A variety of text processing features enable fine-tuning Sphinx for your particular application requirements, and a number of relevance functions ensures you can tweak search quality as well.

• Sphinx clusters scale up to billions of documents and tens of millions search queries per day.

362

Page 363: Rapid Application Development using Ruby on Rails

Installing Sphinx

• http://sphinxsearch.com/downloads/• ./configure• make• sudo make install

363

Page 364: Rapid Application Development using Ruby on Rails

Thinking Sphinx

• A Ruby connector between Sphinx and ActiveRecord.• gem 'thinking-sphinx', '2.0.3'

364

Page 365: Rapid Application Development using Ruby on Rails

Sphinx Components• Sphinx Structure

• A Sphinx daemon (the process known as searchd) can talk to a collection of indexes, and each index can have a collection of sources. Sphinx can be directed to search a specific index, or all of them, but you can’t limit the search to a specific source explicitly.

• Each source tracks a set of documents, and each document is made up of fields and attributes. While in other areas of software you could use those two terms interchangeably, they have distinct meanings in Sphinx (and thus require their own sections in this post).

• Fields• Fields are the content for your search queries – so if you want words tied to a specific

document, you better make sure they’re in a field in your source. They are only string data – you could have numbers and dates and such in your fields, but Sphinx will only treat them as strings, nothing else.

• Attributes• Attributes are used for sorting, filtering and grouping your search results. Their values do

not get paid any attention by Sphinx for search terms, though, and they’re limited to the following data types: integers, floats, datetimes (as Unix timestamps – and thus integers anyway), booleans, and strings. Take note that string attributes are converted to ordinal integers, which is especially useful for sorting, but not much else.

365

Page 366: Rapid Application Development using Ruby on Rails

Sphinx Components

• Filters• Useful with attributes to limit your searches to certain sets

of results. • for example, limiting a forum post search to entries by a

specific user id. • Sphinx’s filters accept arrays or ranges• The range filters are particularly useful for getting results

from a certain time span.• Relevance

• Relevancy is the default sorting order for Sphinx. • There are a couple of things you can do in your queries to

influence it. 366

Page 367: Rapid Application Development using Ruby on Rails

Sphinx Indexing

• Everything to set up the indexes for your models goes in the define_index method, within your model.

• class Article < ActiveRecord::Base• # ...• define_index do• indexes subject, :sortable => true• indexes content• indexes author(:name), :as => :author, :sortable => true• has author_id, created_at, updated_at• end• # ...• end

367

Page 368: Rapid Application Development using Ruby on Rails

Sphinx Fields

• The indexes method adds one (or many) fields, by referencing the model’s column names.

• Use the :as option to signify an alias.• indexes content, :as => :post

• Flag fields as being sortable.• indexes subject, :sortable => true

• Search associated model’s column• indexes author.location, :as => :author_location

• You can also define your indexes as raw SQL• indexes "LOWER(first_name)", :as => :first_name, :sortable

=> true368

Page 369: Rapid Application Development using Ruby on Rails

Sphinx Attributes

• The has method adds one (or many) attributes, and just like the indexes method, it requires references to the model’s column names.• has author_id• has tags(:id), :as => :tag_ids

369

Page 370: Rapid Application Development using Ruby on Rails

Conditions and Grouping

• Possible add custom conditions or groupings• define_index do• # ... • where "status = 'active'"• group_by "user_id"• end

370

Page 371: Rapid Application Development using Ruby on Rails

Running Sphinx

• rake ts:conf • Configures rake using config/sphinx.yml• Creates config/development.sphinx.conf

• rake ts:index• Builds search indexes in db/sphinx/

• rake ts:start• Starts the searchd process that uses

config/development.sphinx.conf and responds to search queries

• rake ts:stop• Stop the searchd process

• rake ts:rebuild• creates stops sphinx, creates configuration, builds indexes

and starts sphinx

371

Page 372: Rapid Application Development using Ruby on Rails

Basic and Conditional Searching• Basic Searching

• Article.search 'pancakes'• Field Conditions

• Article.search :conditions => {:subject => 'pancakes'}• Article.search 'pancakes', :conditions => {:subject => 'tasty'}

372

Page 373: Rapid Application Development using Ruby on Rails

Attributes Filters• Filters on attributes can be defined using a similar syntax, but using

the :with option.• Article.search 'pancakes', :with => {:author_id => @pat.id}• #multiple attributes• Article.search 'pancakes', :with => {• :created_at => 1.week.ago..Time.now,• :author_id => @fab_four.collect { |author| author.id }• }• #without• Article.search 'pancakes',• :without => {:user_id => current_user.id}• #conditions and with• Article.search 'pancakes',• :conditions => {:subject => 'tasty'},• :with => {:created_at => 1.week.ago..Time.now}

373

Page 374: Rapid Application Development using Ruby on Rails

Application Wide Searching/Pagination• Thinking Sphinx allows you to search across all indexed models

• ThinkingSphinx.search 'pancakes'• Can also selectively search across selected models

• ThinkingSphinx.search 'pancakes', :classes => [Article, Comment]

• Supports pagination• Article.search 'pancakes', :page => params[:page], :per_page

=> 20

374

Page 375: Rapid Application Development using Ruby on Rails

Match Mode• Supports several different ways of matching the given search

keywords• Article.search 'pancakes waffles', :match_mode => :any

• :all• Default, requires a document to have every given word

somewhere in its fields.• :any

• Include at least one of the keywords in their fields.• :phrase

• Matches all given words together in one place, in the same order. Same as wrapping a Google search in quotes.

• :boolean• Allows you to use boolean logic with your keywords. & is AND, | is

OR, and both – and ! function as NOTs. You can group logic within parentheses.

375

Page 376: Rapid Application Development using Ruby on Rails

Boolean Match Mode

• #ANDs are used implicitly if no logic is given• #search for ‘pancakes & waffles’• Article.search 'pancakes &amp; waffles', :match_mode

=> :boolean• #search for pancakes or waffles• Article.search 'pancakes | waffles', :match_mode => :boolean• #search for pancakes without waffles• Article.search 'pancakes !waffles', :match_mode => :boolean• #search for ‘pancakes topping’ or waffles• Article.search '( pancakes topping ) | waffles',• :match_mode => :boolean 376

Page 377: Rapid Application Development using Ruby on Rails

Ranking Modes• :proximity_bm25

• The default ranking mode, which combines both phrase proximity and BM25 ranking (see below).

• :bm25• A statistical ranking mode, similar to most other full-text search engines.

• :none• No ranking – every result has a weight of 1.

• :wordcount• Ranks results purely on the number of times the keywords are found in a document.

Field weights are taken into factor.• :proximity

• Ranks documents by raw proximity value.• :match_any

• Returns rankings calculated in the same way as a match mode of :any.• :fieldmask

• Returns rankings as a 32-bit mask with the N-th bit corresponding to the N-th field, numbering from 0. The bit will only be set when any of the keywords match the respective field.

377

Page 378: Rapid Application Development using Ruby on Rails

Sorting

• By default, Sphinx sorts by how relevant it believes the documents to be to the given search keywords.

• You can also sort by attributes (and fields flagged as sortable), as well as time segments or custom mathematical expressions.

• Direction of sorting is ascending by default• Direction of sorting can be changed using :sort_mode option:• Article.search "pancakes", :order => :created_at,• :sort_mode => :desc

378

Page 379: Rapid Application Development using Ruby on Rails

Extended/Expression Sorting

• :extended sort_mode allows you to use multiple attributes, or Sphinx’s ranking scores.• Article.search "pancakes", :sort_mode => :extended,• :order => "created_at DESC, @relevance DESC"

• Internal sphinx attributes available for sorting:• @id (The match’s document id)• @weight, @rank or @relevance (The match’s ranking

weight)• @random (Returns results in random order)

• Customize ranking algorithm using :expr sort_mode• Article.search "pancakes", :sort_mode => :expr,• :order => "@weight * views * karma"

379

Page 380: Rapid Application Development using Ruby on Rails

Field Weights• Sphinx has the ability to weight fields with differing levels of

importance.• Article.search "pancakes", :field_weights => {• :subject => 10,• :tags => 6,• :content => 3• }• Set the weight values in define_index block to apply same custom

weightings to all searches.• set_property :field_weights => {• :subject => 10,• :tags => 6,• :content => 3• }

380

Page 381: Rapid Application Development using Ruby on Rails

Wildcard Searching

• By default, Sphinx does not pay any attention to wildcard searching using an asterisk character.

• You can turn it on, using enable_star: true in sphinx.yml• Article.search ‘Sana*’• For partial word matching, sphinx supports either index

prefixes (the beginnings of words) or infixes (substrings of words). • You cannot enable both at once, though.

• development:• min_infix_len: 3• # OR• min_prefix_len: 3

381

Page 382: Rapid Application Development using Ruby on Rails

Word Stemming/Morphology

• Sometimes you may want it to recognise that certain words share pretty much the same meaning.• To enable this kind of behaviour, you need to specify a

morphology (or stemming library) to Sphinx. • development:• morphology: stem_en• #OR• morphology: metaphone• #OR• morphology: soundex• #OR• morphology: stem_en, metaphone

382

Page 383: Rapid Application Development using Ruby on Rails

Delta Indexing

• In sphinx, you cannot update the fields of a single document in an index.

• Instead, you have to re-process all the data for that index.• Common approach around this issue used by Thinking Sphinx is

to use Delta Indexing

383

Page 384: Rapid Application Development using Ruby on Rails

Delta Indexing Setup

• Add a delta column to your model• def self.up• add_column :articles, :delta, :boolean, :default => true, :null

=> false• end• Turn on delta indexing for the model• define_index do• # ... • set_property :delta => true• end• Stop, re-index and restart Sphinx.• rake thinking_sphinx:rebuild

384

Page 385: Rapid Application Development using Ruby on Rails

Delta Indexing/Full Indexing

• The delta index itself will grow to become just as large as the core indexes, and this removes the advantage of keeping it separate.

• It also slows down your requests to your server that make changes to the model records.

• Run full indexing regularly to avoid this.

385

Page 386: Rapid Application Development using Ruby on Rails

Timestamp/Datetime Deltas

• In Gemfile: • gem 'ts-datetime-delta', '1.0.2':require =>

'thinking_sphinx/deltas/datetime_delta'• In Rakefile:• require 'thinking_sphinx/deltas/datetime_delta/tasks'• In Model:• define_index do• # ...• #uses updated_at by default, change using :delta_column• set_property :delta => :datetime, :threshold => 1.hour• end• Run delta indexing within set threshold:• rake ts:in:delta

386

Page 387: Rapid Application Development using Ruby on Rails

Delayed Deltas• Queues up the index requests in a separate process (invoked by a constantly

running rake task), instead of dealing with them during each web request.• In Gemfile:• gem 'ts-delayed-delta', '1.1.2',• :require => 'thinking_sphinx/deltas/delayed_delta'• In Rakefile:• require 'thinking_sphinx/deltas/delayed_delta/tasks'• Generate Jobs table used by delayed_job• rails generate delayed_job• In Model:• define_index do• # ... • set_property :delta => :delayed• end 387

Page 388: Rapid Application Development using Ruby on Rails

Delayed Deltas

• Add boolean column to the model• def self.up• add_column :articles, :delta, :boolean, :default => true,• :null => false• end• Run background task (runs forever):• rake ts:dd

388

Page 389: Rapid Application Development using Ruby on Rails

Facets

• Facet Searches are search summaries – they provide a breakdown of result counts for each of the defined categories/facets.

• define_index do• # ...• indexes author.name, :as => :author, :facet => true • # ...• has category_id, :facet => true• end• Even if you define your facet as a field, Thinking Sphinx

duplicates it into an attribute, because facets are essentially grouped searches, and grouping can only be done with attributes.

389

Page 390: Rapid Application Development using Ruby on Rails

Querying Facets• Facets are available through the facets class method on all ActiveRecord

models that have Sphinx indexes, and are returned as a subclass of Hash.• Article.facets # =>• {• :author => {• "Sherlock Holmes" => 3,• "John Watson" => 10• },• :category_id => {• 12 => 4,• 42 => 7,• 47 => 2• }• }

390

Page 391: Rapid Application Development using Ruby on Rails

Querying Facets

• The facets method accepts the same options as the search method.

• Article.facets 'pancakes'• Article.facets :conditions => {:author => 'John Watson'}• Article.facets :with => {:category_id => 12}• You can also explicitly request just certain facets:• Article.facets :facets => [:author]• You can query fields with facets, as an attribute• Article.search :with => {:author_facet => 'John

Watson'.to_crc32}

391

Page 392: Rapid Application Development using Ruby on Rails

Global Facets• Faceted searches can be made across all indexed models• ThinkingSphinx.facets 'pancakes'• By default, Thinking Sphinx does not request all possible facets, only those

common to all models and class facet.• ThinkingSphinx.facets 'pancakes' # =>• {• :class => {• 'Article' => 13,• 'User' => 3,• 'Recipe' => 23• }• }• Class facets can be disabled• ThinkingSphinx.facets 'pancakes', :class_facet => false• All facets can be requested• ThinkingSphinx.facets 'pancakes', :all_facets => true

392

Page 393: Rapid Application Development using Ruby on Rails

Displaying Facets

• <% @facets.each do |facet, facet_options| %>• <h5><%= facet %></h5>• <ul>• <% facet_options.each do |option, count| %>• <li><%= link_to "#{option} (#{count})",• :params => {facet => option, :page => 1} %></li>• <% end %>• </ul>• <% end %>

393

Page 394: Rapid Application Development using Ruby on Rails

Thinking Sphinx Deployment

• Include the recipe in order to define the necessary tasks for us:• # If you're using Thinking Sphinx as a gem:• require 'thinking_sphinx/deploy/capistrano'• Define callbacks in order to make sure that Sphinx is properly

configured, indexed, and started on each deploy• task :before_update_code, :roles => [:app] do• thinking_sphinx.stop• end• task :after_update_code, :roles => [:app] do• symlink_sphinx_indexes• thinking_sphinx.configure• thinking_sphinx.start• end

394

Page 395: Rapid Application Development using Ruby on Rails

Messaging QueuesReference: http://www.slideshare.net/skyfallsin/message-queues-in-ruby-an-overview

395

Page 396: Rapid Application Development using Ruby on Rails

Background

• A request is supposed to return a response really fast.• However, there are certain actions that may take longer than a

few milliseconds• Examples:

• Delivering e-mail• Image processing (resizing, cropping etc.)• Sending out notifications• Talking to other web services

• These actions, if not extracted out to run asynchronously, will slow down the application

• Result in bad user experience.396

Page 397: Rapid Application Development using Ruby on Rails

Messaging Queues?

• A server that you submit asynchronous ‘jobs’ to.• Interfaces between your Rails app and the ‘workers’ that

perform each ‘job’.• ‘Workers’ pull jobs from each queue and process them ASAP.• Queues need to be FAST and RELIABLE.

397

Page 398: Rapid Application Development using Ruby on Rails

Delayed Job

• Backed by database• Serializes Ruby Job classes (YAML) into a ‘jobs’ table• Easy to use syntax• # without delayed_job• @user.activate!(@device)• # with delayed_job• @user.delay.activate!(@device)

398

Page 399: Rapid Application Development using Ruby on Rails

Handle Asynchronously

• If a method should always be run in the background, you can call handle_asynchronously after the method declaration

• Can also set priority for the asynchronous method• handle_asynchronously :send_mailer, :priority => 20

• Set time for running (delaying)• handle_asynchronously :in_the_future, :run_at => Proc.new

{ 5.minutes.from_now }

399

Page 400: Rapid Application Development using Ruby on Rails

Setup Delayed Job

• Add line to Gemfile,• gem ‘delayed_job’

• Run ‘bundle install’• Generate migration and delayed_job script

• rails generate delayed_job• Run db migration to create the jobs table

• rake db:migrate

400

Page 401: Rapid Application Development using Ruby on Rails

Running Background Process• Workers can run on any computer as long as they have access

to database! (and clock is in sync)• Start two workers in the background

• RAILS_ENV=production script/delayed_job -n 2 start• Run the job in the foreground

• rake jobs:work

401

Page 402: Rapid Application Development using Ruby on Rails

Delayed Job Summary

• Will get you 80% of the way there, has some nice advanced features (priority, retries etc.)

• Should be careful not to overload your jobs table• Backed up jobs can lead to query slowdown on your site• Monitor and profile workers before you launch on production

(and monitor their running through automated tools such as monit)

• Delayed Job also supports other backends for storing jobs besides Active Record.

402

Page 403: Rapid Application Development using Ruby on Rails

Starling

• Extracted out from early version of Twitter.• Speaks memcached protocol• Easy to use syntax• QUEUE = Starling.new(‘127.0.0.1:22122’)• QUEUE.set(‘my_queue’, queue_object)• QUEUE.get(‘my_queue’)

403

Page 404: Rapid Application Development using Ruby on Rails

Setup Starling

• Add line to Gemfile• gem ‘starling’

• Create user to run starling server• sudo /usr/sbin/adduser -s /sbin/nologin starling

• Create directory to store starling queues and set ownership• sudo mkdir -p /var/spool/starling• sudo chown starling:starling /var/spool/starling

• Create directory to store starling process id and set ownership• sudo mkdir -p /var/run/starling• sudo chown starling:starling /var/run/starling

• Start starling server as starling user• sudo -u starling starling -P /var/run/starling/starling.pid -d

404

Page 405: Rapid Application Development using Ruby on Rails

Running background Proces• Have to create your own background process• class MessageDaemon• def initialize(starling_queue, handler)• @starling_queue, @handler = starling_queue, handler• end• def start_listening• begin• starling = Starling.new(‘127.0.0.1:22122’)• while object = starling.get(@starling_queue)• @handler.handle(object)• end• rescue MemCache::MemCacheError, IndexError => e• ...• rescue StandardError => e• ...• end• end

405

Page 406: Rapid Application Development using Ruby on Rails

Starling Summary• Will have to poll the queue to get new jobs.

• By default, keeps on checking the server every 0.1 ms to check if something is available for the queue.

• Use ‘fetch’ instead of ‘get’ method which if you want to implement your own polling mechanism

• At least 10x faster than a queue based over database.• Very light-weight and adaptable to different needs.• Monitor and profile workers before you launch on production (and

monitor their running through automated tools such as monit)• Does not support interoperability.

• Both publisher and subscriber are expected to be written in Ruby.• Does have performance benefits as a result though by saving cost

on marshalling and unmarshalling to and from a language-neutral format (such as json or xml).

406

Page 407: Rapid Application Development using Ruby on Rails

Resque

• Sits on top of Redis• Redis?

• superfast data structures store like memcached, but smarter in-memory for the most part persisted to disk asynchronously

• sets, lists & corresponding operations• Persists jobs to Redis as JSON objects• Background jobs can be any Ruby class or module that responds

to perform. • Resque is heavily inspired by DelayedJob (which rocks) and comes

with:• A Ruby library for creating, querying, and processing jobs• A Rake task for starting a worker which processes jobs• A Sinatra app for monitoring queues, jobs, and workers.

407

Page 408: Rapid Application Development using Ruby on Rails

Setting up Resque• Install redis

• wget http://redis.googlecode.com/files/redis-2.2.12.tar.gz• tar xzf redis-2.2.12.tar.gz• cd redis-2.2.12• make• sudo make install• cp redis.conf /etc/

• redis-server /etc/redis.conf• Add line to Gemfile

• gem 'resque', :require => "resque/server"• Create lib/tasks/resque.rake and add line

• require 'resque/tasks'• Listen to all queues

• QUEUE='*' rake resque:work • COUNT=5 QUEUE=* rake resque:workers #running multiple workers

408

Page 409: Rapid Application Development using Ruby on Rails

Resque Jobs• Resque jobs are Ruby classes (or modules) which respond to the perform method.• The @queue class instance variable determines which queue Archive jobs will be placed

in.• class Archive• @queue = :file_serve• def self.perform(repo_id, branch = 'master')• repo = Repository.find(repo_id)• repo.create_archive(branch)• end• end• To place an Archive job on the file_serve queue, we might add this to our application's

pre-existing Repository class:• class Repository• def async_create_archive(branch)• Resque.enqueue(Archive, self.id, branch)• end• end

409

Page 410: Rapid Application Development using Ruby on Rails

Resque Persistence

• Jobs are persisted to queues as JSON objects. • your jobs must only accept arguments that can be JSON

encoded.• {• 'class': 'Archive',• 'args': [ 44, 'masterbrew' ]• }

410

Page 411: Rapid Application Development using Ruby on Rails

Resque Frontend

• Provides statistics to monitor resque

411

Page 412: Rapid Application Development using Ruby on Rails

Resque Summary

• Does not support Ruby objects that cannot be JSON encoded.• Supports potentially longer queues.• Easy to define new jobs than starling• Monitor and profile workers before you launch on production

(and monitor their running through automated tools such as monit)

412

Page 413: Rapid Application Development using Ruby on Rails

AJAX Contd.

413

Page 414: Rapid Application Development using Ruby on Rails

Autocomplete Tags

• Create TagsController query function that accepts params[:q] and return each result with new line

• Set :id => “tag_name” for name field in app/views/tags/_form.html.erb

• Set tags.js to include autocomplete binding• $(document).ready(function(){• $("#tag_name").autocomplete({• source: '/tags',• minLength: 2• });• });

414

Page 415: Rapid Application Development using Ruby on Rails

JQuery UI DatePicker

• $('#date').datepicker();• Download jquery-ui css in public/stylesheets/ for a cleaner

look!• wget

http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/themes/base/jquery-ui.css

415

Page 416: Rapid Application Development using Ruby on Rails

Periodically Call Remote Function• Periodically call remote function• $(document).ready(• function(){• setInterval(function(){• $('#mydiv').load('/controller/action');• }, 3000);• });

416

Page 417: Rapid Application Development using Ruby on Rails

Sortable List=============• Unobstrusively change the click's behavior via javascript.• <!-- in your view -->• <a id="foo" href="http://foo.info">This is foo!</a>

• //in application.js (possibly)• $(document).ready(function(){• $('#foo').click(function(){ • //handle the click• return false; //cancel the browser's traditional event.• });• });• Adjust layout to take in javascript:• <html>• <head><title>My boring blog</title>• <%= javascript_tag "var AUTH_TOKEN = #{form_authenticity_token.inspect};" if protect_against_forgery? %>• </head>• <body>• ...• yield :javascript• </body>• </html>

417

Page 418: Rapid Application Development using Ruby on Rails

Sortable List• #public/javascripts/application.js• // This sets up the proper header for rails to understand the request type,• // and therefore properly respond to js requests (via respond_to block, for example)• $.ajaxSetup({ • 'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")}• })

• $(document).ready(function() {• // UJS authenticity token fix: add the authenticy_token parameter• // expected by any Rails POST request.• $(document).ajaxSend(function(event, request, settings) {• // do nothing if this is a GET request. Rails doesn't need• // the authenticity token, and IE converts the request method• // to POST, just because - with love from redmond.• if (settings.type == 'GET') return;• if (typeof(AUTH_TOKEN) == "undefined") return;• settings.data = settings.data || "";• settings.data += (settings.data ? "&" : "") + "authenticity_token=" +

encodeURIComponent(AUTH_TOKEN);• });

418

Page 419: Rapid Application Development using Ruby on Rails

Model Setup

• Install acts_as_list• Add line to Gemfile and run bundle install

• gem ‘acts_as_list’• rails generate migration add_position_to_tasks • bundle install rake db:migrate• Add acts_as_list :scope => :user_story to the Task model.• Optionally, add default_scope => 'position' to the Task model.

419

Page 420: Rapid Application Development using Ruby on Rails

View Setup

• <ul id="tasks-list">• <% @user_story.tasks.each do |t| %>• <li id="task_<%= t.id -%>">• <span class="name">• <%= t.name -%>• </span>• <span class="handle">[handle]<span>• </li>• <% end %>• <ul>

420

Page 421: Rapid Application Development using Ruby on Rails

Unobtrusive Javascript Setup• <% content_for :javascript do %>

• <% javascript_tag do %>• $('#tasks-list').sortable(• {• axis: 'y', • dropOnEmpty:false, • handle: '.handle', • cursor: 'crosshair',• items: 'li',• opacity: 0.4,• scroll: true,• update: function(){• $.ajax({• type: 'post', • data: $('#tasks-list').sortable('serialize') + '&id=<%=@user_story.id-%>', • dataType: 'script', • complete: function(request){• $('#tasks-list').effect('highlight');• },• url: '/user_stories/prioritize_tasks'})• }• })• <% end %>• • <% end %> 421

Page 422: Rapid Application Development using Ruby on Rails

Controller/Route Setup• def prioritize_tasks• user_story = UserStory.find(params[:id])• tasks = user_story.tasks• tasks.each do |task|• task.position = params['task'].index(task.id.to_s) + 1• task.save• end• render :nothing => true• end• #Routes Setup• # add the :collection option• map.resources :user_story, :collection => { :prioritize_tasks => :post } 422

Page 423: Rapid Application Development using Ruby on Rails

Security Contd.

423

Page 424: Rapid Application Development using Ruby on Rails

reCAPTCHA

424

Page 425: Rapid Application Development using Ruby on Rails

reCAPTCHA

• #http://www.google.com/recaptcha/whyrecaptcha• Helps prevent automated abuse of your site (such as comment

spam or bogus registrations) by using a CAPTCHA to ensure that only humans perform certain actions.

• Has an audio test that allows blind people to freely navigate your site.

• Over 100,000 sites use reCAPTCHA, including household names like Facebook, Ticketmaster, and Craigslist.

425

Page 426: Rapid Application Development using Ruby on Rails

reCAPTCHA Setup

• Add line to Gemfile• gem "ambethia-recaptcha", :lib => "recaptcha/rails", :source => "

http://gems.github.com"• Get a reCAPTCHA account at http://www.google.com/recaptcha.• Setup your API Keys• Recaptcha.configure do |config|• config.public_key =

'6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'• config.private_key =

'6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'• end• Use recaptcha_tags to output the necessary HTML code and then

verify the input with verify_recaptcha.426

Page 427: Rapid Application Development using Ruby on Rails

Using reCAPTCHA• In View:• <%= recaptcha_tags %>• #If you are using SSL, use this instead:• #<%= recaptcha_tags :ssl => true %>• In Controller:• if verify_recaptcha• @thing.save!• redirect_to success_path• else• flash[:error] = "There was an error with the recaptcha code below. Please re-enter the code and click submit." • render :action => 'new'• end• #OR• if verify_recaptcha(:model => @thing, :message => 'There was an error with the recaptcha code below. Please re-

enter the code and click submit.') && @thing.save• redirect_to success_path• else• render :action => 'new'• end

427

Page 428: Rapid Application Development using Ruby on Rails

Sessions Hijacking• Sniff the cookie in an insecure network.

• A wireless LAN can be an example of such a network. • In an unencrypted wireless LAN it is especially easy to listen to the traffic of

all connected clients. • This is one more reason not to work from a coffee shop. • For the web application builder this means to provide a secure connection

over SSL.• Most people don’t clear out the cookies after working at a public terminal.

• So if the last user didn’t log out of a web application, you would be able to use it as this user.

• Provide the user with a log-out button in the web application, and make it prominent.

• Many cross-site scripting (XSS) exploits aim at obtaining the user’s cookie. • Instead of stealing a cookie unknown to the attacker, they fix a user’s session

identifier (in the cookie) known to them. 428

Page 429: Rapid Application Development using Ruby on Rails

Session Guidelines

• Critical data should not be stored in session. If the user clears his cookies or closes the browser, they will be lost. • And with a client-side session storage, the user can read the

data.• If using CookieStore for session storage, then the security of

this storage depends on the secret set (and on the digest algorithm, which defaults to SHA512, which has not been compromised, yet). • So don’t use a trivial secret, i.e. a word from a dictionary, or

one which is shorter than 30 characters. Put the secret in your environment.rb

• Already in-place for Rails 3 in: config/initializers/secret_token.rb

429

Page 430: Rapid Application Development using Ruby on Rails

Mass Assignment

• Without any precautions Model.new(params[:model]) allows attackers to set any database column’s value.

• def signup• params[:user] # => {:name => “ow3ned”, :admin => true}• @user = User.new(params[:user])• end• Use attr_protected to define attributes that will not be

accessible for mass-assignment• Even better is to use attr_accessible to define attributes that

will be accessible for mass-assignment

430

Page 431: Rapid Application Development using Ruby on Rails

SQL Injection• SQL injection attacks aim at influencing database queries by manipulating

web application parameters. • A popular goal of SQL injection attacks is to bypass authorization.

• User.find(:first, "login = '#{params[:name]}' AND password = '#{params[:password]}'")

• params[:name] => ’ OR ‘1’=‘1• params[:password] => ’ OR ’2’>’1• This will simply find the first record in the database, and grants access

to this user.• Another goal is to carry out data manipulation or reading arbitrary data.

• Project.find(:all, :conditions => "name = '#{params[:name]}'")• params[:name] => ’ OR 1 --’• The two dashes start a comment ignoring everything after it. So the

query returns all records from the projects table including those blind to the user.

431

Page 432: Rapid Application Development using Ruby on Rails

SQL Injection Solution?

• Instead of passing a string to the conditions option, you can pass an array to sanitize tainted strings• Model.find(:first, :conditions => ["login = ? AND password

= ?", entered_user_name, entered_password])• Model.find(:first, :conditions => {login: entered_user_name,

password: entered_password})

432

Page 433: Rapid Application Development using Ruby on Rails

Cross-site Request Forgery (CSRF)• Add this to the header of site-wide layout to prevent cross-site

request forgery• <%= csrf_meta_tag %>

• Required for UJS helpers to work.• No need to get into the details, but just know that Rails is

working hard to make your application secure.

433

Page 434: Rapid Application Development using Ruby on Rails

Testing Contd.

434

Page 435: Rapid Application Development using Ruby on Rails

Model Testing - Name• #spec/models/user_spec.rb• require 'spec_helper'• describe User do• ...• it "should require a name" do• no_name_user = User.new(@attr.merge(:name => ""))• #RSpec adopts the useful convention of allowing us to test any boolean method

by dropping the question mark and prepending be_• no_name_user.should_not be_valid• end• it "should reject names that are too long" do• long_name = "a" * 51• long_name_user = User.new(@attr.merge(:name => long_name))• long_name_user.should_not be_valid• end• end

435

Page 436: Rapid Application Development using Ruby on Rails

Model Testing - Email Address• #spec/models/user_spec.rb• require 'spec_helper'• describe User do• ...• it "should require an email address" do• no_email_user = User.new(@attr.merge(email: ""))• no_email_user.should_not be_valid• end

• it "should accept valid email addresses" do• addresses = %w[[email protected] [email protected] [email protected]]• addresses.each do |address|• valid_email_user = User.new(@attr.merge(email: address))• valid_email_user.should be_valid• end• end

• it "should reject invalid email addresses" do• addresses = %w[user@foo,com user_at_foo.org example.user@foo.]• addresses.each do |address|• invalid_email_user = User.new(@attr.merge(email: address))• invalid_email_user.should_not be_valid• end• end

• it "should reject duplicate email addresses" do• # Put a user with given email address into the database.• User.create!(@attr)• user_with_duplicate_email = User.new(@attr)• user_with_duplicate_email.should_not be_valid• end• end

436

Page 437: Rapid Application Development using Ruby on Rails

Model Testing - Password Validations• #spec/models/user_spec.rb• require 'spec_helper'• describe User do• ...• describe "password validations" do

• it "should require a password" do• User.new(@attr.merge(password: "", password_confirmation: "")).• should_not be_valid• end

• it "should require a matching password confirmation" do• User.new(@attr.merge(:password_confirmation => "invalid")).• should_not be_valid• end

• it "should reject short passwords" do• short = "a" * 5• hash = @attr.merge(password: short, :password_confirmation => short)• User.new(hash).should_not be_valid• end

• it "should reject long passwords" do• long = "a" * 41• hash = @attr.merge(password: long, :password_confirmation => long)• User.new(hash).should_not be_valid• end• end• end

437

Page 438: Rapid Application Development using Ruby on Rails

Code for Model Testing• $ rails generate model User name:string email:string• #app/models/user.rb• class User < ActiveRecord::Base• attr_accessor :password• attr_accessible :name, :email, :password, :password_confirmation• email_regex = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i

• validates :name, presence: true,• length: { maximum: 50 }• validates :email, presence: true,• format: { with: email_regex },• uniqueness: true• validates :password, presence: true,• confirmation: true,• length: { within: 6..40 }• end

438

Page 439: Rapid Application Development using Ruby on Rails

Factory Girl

• A fixtures replacement with a straightforward definition syntax, • support for multiple build strategies (saved instances,

unsaved instances, attribute hashes, and stubbed objects), and

• support for multiple factories for the same class (user, admin_user, and so on), including factory inheritance.

• Creates entries into the database as required.• Add to Gemfile and run ‘bundle install’• group :test do• ...• gem 'factory_girl_rails'• end

439

Page 440: Rapid Application Development using Ruby on Rails

Factory Girl Usage• #spec/factories.rb• Factory.define :user do |f|• f.sequence(:username) { |n| "foo#{n}" }• f.password "foobar"• f.password_confirmation { |u| u.password }• f.sequence(:email) { |n| "foo#{n}@confiz.com" }• end

• Factory.define :article do |f|• f.name "Foo"• f.association :user• end

• #spec/models/user_spec.rb• describe User do• it "should authenticate with matching username and password" do• user = Factory(:user, username: 'frank', password: 'secret')• User.authenticate('frank', 'secret').should == user• end• • it "should not authenticate with incorrect password" do• user = Factory(:user, username: 'frank', password: 'secret')• User.authenticate('frank', 'incorrect').should be_nil• end• end

440

Page 441: Rapid Application Development using Ruby on Rails

User Show Action Testing• #spec/controllers/users_controller_spec.rb• require 'spec_helper'

• describe UsersController do• render_views

• describe "GET 'show'" do• before(:each) do• @user = Factory(:user)• end• • it "should be successful" do• get :show, :id => @user• response.should be_success• end

• it "should find the right user" do• get :show, :id => @user• #assigns method takes in a symbol argument and returns the value of the corresponding instance variable in the controller action• assigns(:user).should == @user• end• end• ...• end 441

Page 442: Rapid Application Development using Ruby on Rails

Controller Testing for Failed User Sign-up• Should not create a user

• count of users should not increase• Should keep the user on the sign-up page

• Title should be ‘Sign Up’• Should re-render the new user page

• new user template should be rendered

442

Page 443: Rapid Application Development using Ruby on Rails

Controller Testing for Failed User Signup• #spec/controllers/users_controller_spec.rb• require 'spec_helper'• describe UsersController do• render_views• ...• describe "POST 'create'" do• describe "failure" do• before(:each) do• @attr = { :name => "", :email => "", password: "",• :password_confirmation => "" }• end

• it "should not create a user" do• lambda do• post :create, :user => @attr• end.should_not change(User, :count)• end

• it "should have the right title" do• post :create, :user => @attr• response.should have_selector("title", :content => "Sign up")• end

• it "should render the 'new' page" do• post :create, :user => @attr• response.should render_template('new')• end• end• end• end

443

Page 444: Rapid Application Development using Ruby on Rails

Controller Testing for Successful User Sign-up• Should create a user

• Count of users should increase by 1• Should be take to user’s show page

• redirect to user show page for that user

444

Page 445: Rapid Application Development using Ruby on Rails

Controller Testing for Successful User Sign-up• require 'spec_helper'

• describe UsersController do• render_views• ...• describe "POST 'create'" do• ...• describe "success" do• before(:each) do• @attr = { :name => "New User", :email => "[email protected]",• password: "foobar", :password_confirmation => "foobar" }• end

• it "should create a user" do• lambda do• post :create, :user => @attr• end.should change(User, :count).by(1)• end

• it "should redirect to the user show page" do• post :create, :user => @attr• response.should redirect_to(user_path(assigns(:user)))• end • end• end• end 445

Page 446: Rapid Application Development using Ruby on Rails

Integration Tests w/ Navigation!• RSpec integration tests also support a highly expressive web-

navigation syntax.• Especially useful for simulating filling out forms

• visit signin_path• fill_in "Name", :with => "Hisham Malik"

• The first arguments to fill_in are the label values, i.e., exactly the text the user sees in the browser

• click_button

446

Page 447: Rapid Application Development using Ruby on Rails

Integration Testing for Failed User Sign-up• When you leave the name, email, password, and confirmation

fields blank• Click Submit button• User should be taken back to the new user page.• The response should have error_explanation div

• <div id="error_explanation">...</div>• User count is also not changed

447

Page 448: Rapid Application Development using Ruby on Rails

Integration Testing for Failed User Sign-up• $ rails generate integration_test users• require 'spec_helper'

• describe "Users" do• describe "signup" do• describe "failure" do• it "should not make a new user" do• lambda do• visit signup_path• fill_in "Name", :with => ""• fill_in "Email", :with => ""• fill_in "Password", :with => ""• fill_in "Confirmation", :with => ""• click_button• response.should render_template('users/new')• response.should have_selector("div#error_explanation")• end.should_not change(User, :count)• end• end• end• end

448

Page 449: Rapid Application Development using Ruby on Rails

Active Resource

449

Page 450: Rapid Application Development using Ruby on Rails

Active Resource

• Represents your RESTful resources as manipulatable Ruby objects.

• class Post < ActiveResource::Base• self.site = “http://localhost:3000”• self.format = :json #by default format is xml• end• post = Post.find(1)• post.inspect• post.body = “new body”• post.save

450

Page 451: Rapid Application Development using Ruby on Rails

Custom REST Methods• Active Resource also supports defining your own custom REST methods• # POST to the custom 'register' REST method, i.e. POST

/people/new/register.xml.• Person.new(:name => 'Ryan').post(:register)

• # PUT an update by invoking the 'promote' REST method, i.e. PUT /people/1/promote.xml?position=Manager.

• Person.find(1).put(:promote, :position => 'Manager')

• # GET all the positions available, i.e. GET /people/positions.xml.• Person.get(:positions)

• # DELETE to 'fire' a person, i.e. DELETE /people/1/fire.xml.• Person.find(1).delete(:fire)

451

Page 452: Rapid Application Development using Ruby on Rails

Authentication• Supports HTTP Basic Authentication• class Person < ActiveResource::Base• self.site = "http://localhost:3000/”• self.user = "hisham"• self.password = "confiz"• end• Supports Certificate Authentication• class Person < ActiveResource::Base• self.site = "https://secure.api.people.com/"• self.ssl_options = {:cert =>

OpenSSL::X509::Certificate.new(File.open(pem_file))• :key => OpenSSL::PKey::RSA.new(File.open(pem_file)),• :ca_path => "/path/to/OpenSSL/formatted/CA_Certs",• :verify_mode => OpenSSL::SSL::VERIFY_PEER}• end

452

Page 453: Rapid Application Development using Ruby on Rails

Referenceshttp://marklunds.com/s5/rails101/html/rails_introduction.htmlhttp://www.troubleshooters.com/codecorn/ruby/symbols.htmhttp://www.techotopia.com/index.php/Ruby_Essentialshttp://www.slideshare.net/vishnu/the-ruby-programming-language-or-why-are-you-wasting-brain-powerhttp://guides.rubyonrails.org/association_basics.htmlhttp://guides.rubyonrails.org/layouts_and_rendering.htmlhttp://guides.rubyonrails.org/routing.htmlhttp://guides.rubyonrails.org/active_record_validations_callbacks.htmlhttp://guides.rubyonrails.org/getting_started.htmlhttp://blog.bernatfarrero.com/jquery-and-rails-3-mini-tutorial/http://net.tutsplus.com/tutorials/javascript-ajax/using-unobtrusive-javascript-and-ajax-with-rails-3/http://www.simonecarletti.com/blog/2010/06/unobtrusive-javascript-in-rails-3/http://www.aaginskiy.com/technology/2011/02/deploying-rails-3-apps-with-capistrano/http://freelancing-god.github.com/ts/enhttp://www.slideshare.net/skyfallsin/message-queues-in-ruby-an-overviewhttps://github.com/defunkt/resqueRails 3 in a Nutshell: http://ofps.oreilly.com/titles/9780596521424/index.htmlhttp://railscasts.com/episodes/158-factories-not-fixtureshttp://leikind.org/pages/wicegridhttp://awesomeful.net/posts/47-sortable-lists-with-jquery-in-railshttp://tomafro.net/2009/08/using-indexes-in-rails-index-your-associationshttp://weblog.jamisbuck.org/2006/10/23/indexing-for-db-performancehttp://www.simonecarletti.com/blog/2010/06/unobtrusive-javascript-technique/

453