48
SEG4110 – Advanced Software Design and Reengineering TOPIC N Ruby on Rails

Topic N: Ruby on Rails

Embed Size (px)

Citation preview

Page 1: Topic N: Ruby on Rails

SEG4110 – Advanced Software Design and Reengineering

TOPIC NRuby on Rails

Page 2: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 2

What is Ruby on Rails?

•Ruby is an object-oriented programming language-Incorporates ideas from Smalltalk, Java, etc.

-Very pure

•Rails is a Ruby framework for creating web applications quickly and easily

Page 3: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 3

The Ruby language - basicsAn open-source, dynamic language•Created by Yukihiro “matz” Matsumoto

—Originated in 2001, Public release in 2005

•Supported by a large community

Main website:•www.ruby-lang.org

Rapidly growing library and online resources•http://rubyforge.org/softwaremap/trove_list.php

•http://www.sapphiresteel.com/The-Little-Book-Of-Ruby

Store programs in files with extension .rb•Or run the irb interpreter

Page 4: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 4

Until last year, the fastest growing language http://www.tiobe.com/tpci.htm

Page 5: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 5

Ruby: Flexibility, simplicity and power• Everything is an object•Inherits from the Smalltalk legacy

• No variable declarations needed

• Dynamic typing

• ‘Blocks’ allow you to define new control structures

• ‘Mixins’ (modules) give power of multiple inheritance without the drawbacks.

• Code can be added to a class dynamically

• Easy to•Add C code (or code in another language)•Extend the language

Page 6: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 6

Defining functions

def helloworld puts "Hello World"end

def hello(arg) puts "hello "+argend

Example runshello "Tim”hello ("Tim") # preferred - usually helps prevent bugs

Page 7: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 7

A few more details 1

Placeholder substitution – works with "", but not with ' ' puts "hello #{arg} Your argument is #{arg.length} long!"

Default argument to a method – and concatenationdef hello(arg="World") puts "hello "+argend

Defining an arrayx=[1, 2 ,3]y= %w(an array of strings with the words of this phrase)

Sorting - the ! Means alter (side-effect) the objectx.sort!

Page 8: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 8

A few more details 2To access array elementsx [1..3] #elements 0 to 3x [1,3] # elements 0 for 3x [-3,1] # from 3rd-from-the-end for 1

Non numeric collections are called hashesz = Hash.newz [‘SEG4210’] = ‘Advanced Software Engineering’anotherhash = {‘test’ => 12334, ‘test2’ => 98776}

A Ruby method will always return the value of the last expression evaluated•Although using ‘return’ is good practice for clarity

Use ` to execute a program in the operating system`ls`

Page 9: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 9

A few more details 3

Symbols•E.g. :all•Represent specific language keywords used in various contexts

Page 10: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 10

Iteration

@names.each do |name| print nameend

For number in [1,2,3] do print numberend

while tired sleepend

Ruby has various other allowed syntaxes, including allowing { }

Page 11: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 11

“Duck typing”

In Ruby you don't declare the type of a variable•You can put any object in a variable.•You can determine the type in order to make a decision

if @names.respond_to?("join")

“if it talks like a duck, it is a duck”

Page 12: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 12

Defining a class The initialize method is used to construct the object•@name is an instance variable•all instance variables are private by default

class Greeter def initialize(name = "World") @name = name end

def say_hi puts "Hi #{@name}!" endend

To create an objectg = Greeter.new("Pat")

Page 13: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 13

Attributes: To make instance variables public

attr_accessor :name, :studentNumber

Now I can say in another classg.name="Tim"

andg.name

I can also separately sayattr_reader :studentNumberattr_writer :age

Page 14: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 14

Creating a subclass

def specialGreeter < greeter

To call the same method with same arguments in a superclasssuper

Page 15: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 15

Class variables (static variables)

@@defaultSize = 5

Page 16: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 16

Introspection

Greeter.instance_methods

["method", "send", "object_id", "singleton_methods", "say_hi", "__send__", "equal?", "taint", "frozen?", "instance_variable_get", "kind_of?", "to_a", "hello", "instance_eval", "type", "protected_methods", "extend", "eql?", "display", "instance_variable_set", "hash", "is_a?", "to_s", "class", "tainted?", "private_methods", "untaint", "id", "inspect", "dello", "==", "===", "clone", "public_methods", "respond_to?", "freeze", "__id__", "=~", "methods", "nil?", "dup", "instance_variables", "instance_of?"]

Note that the "hello" method we defined earlier is here•It is inherited from class Object

Inspecting an objectg.inspect or p(g)

Page 17: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 17

Dynamic classes

Ruby is a very dynamic language•You can modify a class at any time!•Add new methods, variables, etc.

Page 18: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 18

Modules - have some similarities to interfaces

You can achieve multiple inheritance by simply adding “mixin” modules to several classes

module MyModule def func(arg) doSomethingWith arg endend

def myclass include MyModule …end

Modules also provide namespaces

Page 19: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 19

Loading modules from files

require (MyModule.rb)

Standard modules already loaded in every Ruby class

Comparable, EnumerableFileTestGC, KernelMathObjectSpace, Precision, Process, Signal

Use notation: X::Y to refer to class X in module Y

Page 20: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 20

The Ruby on Rails frameworkMakes it easy to develop web applications•Most manipulate data in a database such as MySQL or Postgres

•Can also manipulate flat files

Integrates with AJAX

Suggested book: Agile Web Development with Rails•By

—Dave Thomas—and David Heinemeier Hansson (creater of Rails)

•Second edition Jan 2007, www.pragprog.com

Page 21: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 21

Rails philosophies

Model-View-Controller (MVC)•Separation of the data from the presentation from the control logic

Don’t Repeat Yourself•E.g. define your database structure in just one place

Convention over Configuration•Sensible defaults

Agility•The application can be quickly changed

Page 22: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 22

Models MVC

Models•Whatever is shown in the class diagram plus business rules

•The Ruby model and the Database are separated using ORM (Object Relational Mapping)

•Avoids any need to embed SQL code in an application

Page 23: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 23

Active Record as the basis for the Model

Database tables map to Rails classes•Rows map to objects

require 'active_record’

# The following is generated from the database and attributes are filled # in from database columnsclass Order < ActiveRecord::Base end

order = Order.find(1)order.discount = 0.5order.save

Page 24: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 24

Querying the model in Ruby code

Order.find(:all, :conditions => "name='dave'" ).each do |order| puts order.amountend

Page 25: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 25

The View - Three mechanisms

rhtml•Embedded Ruby code in html, using Erb (embedded Ruby)

rxml•Construct XML using Ruby code

rjs•Create javascript in Ruby code to create AJAX applications

•The JavaScript runs in the client, of course

Page 26: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 26

The Controller

Routes http requests to Ruby methods•Allows you to create people-friendly URLs

Manages Caching•Database data stored in memory for a speed boost

Manages Sessions•E.g. a shopping session

Page 27: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 27

Getting started

You need to install•Ruby•Rails•A database•I leave it to the TA and/or reference manuals to do that

The hard part of application development is now done!!

Page 28: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 28

Really getting startedCreate a new empty default application with the commandrails myapplication

This will install directories neededREADME components/ doc/ public/ tmp/Rakefile config/ lib/ script/ vendor/app/ db/ log/ test/

app contains controllers/, models/ and views/

Start the built-in serverruby script/server

Page 29: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 29

Here’s what the default application looks like

Page 30: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 30

Next step: Add a controller

ruby script/generate controller Airline

This will generate an empty (hook, or stub) class •airline_controller.rb

class AirlineController < ApplicationControllerend

Now let us add a method addflight to the Airline class def addflight end

We would call this from a URL as followshttp://www.mysite.ca/airline/addflight

Page 31: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 31

Next step: Add a view using the Erb/rhtml approach

We need a ‘template’ file called addflight.rhtml

<html> <head> <title>Add a flight/title> </head> <body> <h1>Add a flight</h1> <%= AirlineView.addFlightForm %> </body></html>

Page 32: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 32

Some things you can embed in rhtml code

Create an href to another ruby action <%= link_to "Airline help", :action => "help" %>

Page 33: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 33

We need to actually build a database to go any further

It is possible that the database is already there•But Ruby provides nice facilities for setting up a database incrementally in Ruby code

First create an empty database using your database package•MySQL is the easiest

Next test that Ruby can connectrake db:migrate

Page 34: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 34

Creating a simple database

The rule is one table per classruby script/generate model regularflight

You will then find a filedb/migrate/file 001_create_regularflights.rb

class CreateRegularflights < ActiveRecord::Migration def self.up create_table :regularFlights do |t| t.column :number, :integer t.column :departuretime, :string t.column :origin, :string t.column :destination, :string endend

Page 35: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 35

Next steps

Make Ruby upgrade the databaserake db:migrate

Now create a controller adminruby script/generate controller admin

And edit app/controllers/admin_controller.rbclass AdminController < ApplicationControllerscaffold :regularflightend

Go to http://www.mysite.ca/airline/adminAnd you will see a page where you can add flights

Page 36: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 36

Making Ruby upgrade the database:Adding a new column

class AddPrice < ActiveRecord::Migration def self.up add_column :products, :price, :decimal, :precision => 8, :scale => 2, :default => 0 end

def self.down remove_column :products, :price end

end

Page 37: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 37

Ruby on Rails’ naming

If you have a table called People, RoR will generate a class called Person and vice-versa

Other transformations:•Specific_flights <-> SpecificFlight•Person_roles <-> PersonRole

Page 38: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 38

Enhancing model classes

When we create a new class/table the model code for the class is empty:

class Product < ActiveRecord::Baseend

Page 39: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 39

Validation

If we want to force certain data to be present

class Product < ActiveRecord::Base

validates_presence_of :title, :description, :image_url

end

The generated web page for adding products will automatically ensure this data is filled in•Fields with errors are highlighted•Errors are summarized at the top of the form

Page 40: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 40

Example generated web page

Page 41: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 41

Other validationsvalidates_numericality_of :price

validates_uniqueness_of :title

def validate errors.add(:price, "should be at least 0.01" ) if price.nil? || price < 0.01end

validates_format_of :image_url, :with => %r{\.(gif|jpg|png)$}i, :message => "must be a URL for a GIF, JPG, or PNG image"

Page 42: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 42

Class methods in the model to return a set of instances

class Product < ActiveRecord::Base # The self. Means it is a class (static) method def self.find_products_for_sale find(:all, :order => "title" ) end# validation stuff...end

Page 43: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 43

Specifying associations in model classesclass Order < ActiveRecord::Base has_many :line_items …end

class Product < ActiveRecord::Base has_many :line_items has_many :orders, :through => :line_items …end

class LineItem < ActiveRecord::Base belongs_to :order belongs_to :product …end

Page 44: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 44

Generating automatic documentationrake doc:app

rake stats(in /Users/dave/Work/depot)+----------------------+-------+-------+---------+---------+-----+-------+| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |+----------------------+-------+-------+---------+---------+-----+-------+| Helpers | 17 | 15 | 0 | 1 | 0 | 13 || Controllers | 229 | 154 | 5 | 23 | 4 | 4 || Components | 0 | 0 | 0 | 0 | 0 | 0 || Functional tests | 206 | 141 | 8 | 25 | 3 | 3 || Models | 261 | 130 | 6 | 18 | 3 | 5 || Unit tests | 178 | 120 | 5 | 13 | 2 | 7 || Libraries | 0 | 0 | 0 | 0 | 0 | 0 || Integration tests | 192 | 130 | 2 | 10 | 5 | 11 |+----------------------+-------+-------+---------+---------+-----+-------+| Total | 1083 | 690 | 26 | 90 | 3 | 5 |+----------------------+-------+-------+---------+---------+-----+-------+Code LOC: 299 Test LOC: 391 Code to Test Ratio: 1:1.3

Page 45: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 45

Automatic testing in RoR

Unit tests - test the model•In test/unit

Functional tests - test a controller action•In test/functional

Integration tests - test flow through the system•In test/integration

Page 46: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 46

Sample initially generated test for Product

require File.dirname(__FILE__) + '/../test_helper'class ProductTest < Test::Unit::TestCase #load test data fixtures :products def test_truth assert true endend

Page 47: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 47

A test to verify that an empty product is not valid and that empty variables are invalid

def test_invalid_with_empty_attributes product = Product.new assert !product.valid? assert product.errors.invalid?(:title) assert product.errors.invalid?(:description) assert product.errors.invalid?(:price) assert product.errors.invalid?(:image_url)end

Page 48: Topic N: Ruby on Rails

SEG4110 - Topic N - Ruby on Rails 48

Checking some other propertiesdef test_positive_price product = Product.new(:title => "My Book Title" , :description => "yyy" , :image_url => "zzz.jpg" ) product.price = -1 assert !product.valid? assert_equal "should be at least 0.01" , product.errors.on(:price)

product.price = 0 assert !product.valid? assert_equal "should be at least 0.01" , product.errors.on(:price)

product.price = 1 assert product.valid?end