CIT 383: Administrative Scripting Classes and Exceptions CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Topics Creating a Class Initialization Accessors and Attributes Redefining Methods Equality Class Variables Inheritance CIT 383: Administrative Scripting
CIT 383: Administrative Scripting What is a Class? A class is a blueprint to create objects Attributes: named values associated with object Methods: actions that can be performed on object. A Point object has Attributes: x and y coordinates Methods: x to read x-coordinate y to read y-coordinate to_s to return a string representation CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Why Create Classes? Ruby provides many classes Array DateTime File Regexp String But it doesn’t provide classes for your domain ApacheStatus AutomateIt PasswdCheck Uptime VMstats CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Creating a Class A class is defined with the class keyword class Hello end Class names must begin with a capital letter. Classes contain method definitions: def say() puts “Hello” CIT 383: Administrative Scripting
Creating an Instance of a Class Instances of the class are created with new myhello = Hello.new Methods can be called on objects of the class: irb(main):007:0> myhello = Hello.new => #<Hello:0xb7c92f04> irb(main):008:0> myhello.say Hello => nil CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Instance Variables An instance variable is a variable that belongs to an instance of a class. An instance variable is prefixed by a single at sign(@). You can define an instance variable inside a method. You can only access an instance variable from outside an object via a method. CIT 383: Administrative Scripting
Instance Variable Example The initialize method is run by new class Greeting def initialize(mygreeting) @what = mygreeting end def say() puts @what CIT 383: Administrative Scripting
Instance Variable Example irb(main):001:0> class Greeting irb(main):002:1> def initialize(mygreeting) irb(main):003:2> @what = mygreeting irb(main):004:2> end irb(main):005:1> def say() irb(main):006:2> puts @what irb(main):007:2> end irb(main):008:1> end => nil irb(main):009:0> mygreet = Greeting.new('Konichiwa') => #<Greeting:0xb7c4275c @what="Konichiwa"> irb(main):010:0> mygreet.say Konichiwa CIT 383: Administrative Scripting
CIT 383: Administrative Scripting The Point xample The Point class represents a point in 2D: class Point def initialize(x,y) @x, @y = x, y end def x @x CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Using the Point Class irb(main):009:0> origin = Point.new(0,0) => #<Point:0xb7c8b9d4 @x=0, @y=0> irb(main):010:0> point2 = Point.new(1,2) => #<Point:0xb7c81074 @x=1, @y=2> irb(main):011:0> xvalue = origin.x => 0 irb(main):012:0> xvalue2 = point2.x => 1 CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Assignment Methods Use the = operator by creating a method whose name ends in = class Point def x=(value) @x = value end Assignment methods let you change values: irb(main):018:0> p = Point.new(0,0) => #<Point:0xb7c66b34 @x=0, @y=0> irb(main):019:0> p.x = 10 => 10 irb(main):020:0> p => #<Point:0xb7c66b34 @x=10, @y=0> irb(main):021:0> p.x CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Accessor Shortcuts Let Ruby create accessor methods automatically. attr_reader: create read-only methods attr_writer: create assignment write methods attr_accessor: create read and write methods. Examples class Point attr_accessor :x, :y end p = Point.new(0,0) p.x p.y = 1 CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Operator Methods Can define operators like == for class too. class Point def ==(o) if o.is_a? Point @x==o.x && @y==o.y else false end Example p1 = Point.new(1,1) p2 = Point.new(1,2) p1 == p2 # false CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Redefining Methods Can redefine built-in methods like to_s class Point def to_s “(#@x,#@y)” end Example: irb(main):032:0> p = Point.new(1,2) => #<Point:0xb7c309a8 @x=1, @y=2> irb(main):033:0> puts p (1,2) => nil CIT 383: Administrative Scripting
Class Variables Class variables are shared by all objects of class. Use @@prefix; differ from instance vars which belong to one object. Example: Track number of objects of class created. class Point @@npoints = 0 def initialize(x,y) @x, @y = x, y @@npoints += 1 end def count @@npoints p1 = Point.new(0,0); p2 = Point.new(1,1) p2.count # returns 2
Loading Classes from Files To load a class from a file, use require require filename Must not use .rb file extension in filename. To load a class a second time, use load load ‘filename.rb’ Must have complete filename, including .rb. Primarily used in irb. CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Subclassing A subclass is a class that is based on but modified from an existing class. A subclass extends another class. Extends means the same as inherits from. The original class is known as the superclass. A subclass is defined using < superclass: class Point3D < Point end CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Inheritance Subclasses inherit methods of superclass. Superclass methods can be called on subclass. p3d = Point3D.new(0,0,0) p3d.x p3d.y Class variables shared with superclass. If subclass modifies value of a class variable, the superclass sees the modified value too. CIT 383: Administrative Scripting
Overiding and Adding Methods To make Point3D useful, redefine initialize: class Point3D < Point def initialize(x,y,z) @x,@y,@z = x,y,z end We also need to add accessors for the z coordinate: def z @z def z=(value) @z = value CIT 383: Administrative Scripting
Calling Superclass Methods How can you call an overidden method? Use super(args) in method of subclass. New definition of initialize: class Point3D < Point def initialize(x,y,z) # pass first two args to # superclass initialize method super(x,y) # initialize the third argument @z = z end CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Default Methods All classes inherit methods from Object class is_a?(class) kind_of?(class) methods object_id to_s These methods can be redefined if needed to_s prints class name and address of object CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Formatting Classes Indent body of a class statement by one tab. class Hello def hi … end Use descriptive nouns as class names. CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Exception Topics What is an Exception? Raising Exceptions Catching Exceptions Retry Exception Classes CIT 383: Administrative Scripting
CIT 383: Administrative Scripting What is an Exception? An exception is an object that represents an exceptional condition, such as an error of some type. An exception is raised when an error occurs. A ruby program terminates on an exception. An exception can be caught by an exception handler, preventing program termination. CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Exception Examples irb(main):001:0> 1/0 ZeroDivisionError: divided by 0 from (irb):1:in `/' from (irb):1 irb(main):002:0> 'a' + 1 TypeError: can't convert Fixnum into String from (irb):2:in `+' from (irb):2 CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Exception Classes Exceptions belong to Exception class or its subclasses. Exception NoMemoryError SystemExit StandardError ArgumentError IOError IndexError TypeError ZeroDivisionError CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Raising Exceptions The raise method raises an exception raise: creates a new RuntimeError and raises it. raise(string): creates new RuntimeError with the specified string as its message and raises it. raise(class): creates a new exception object of the specified class and raises it. raise(class, string): creates a new exception object of the specified class with the specified string as its message and raises it. CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Raise Example #1 def factorial(n) raise TypeError if n.class != Fixnum raise ArgumentError if n < 1 if n == 1 return 1 else return n * factorial(n-1) end CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Raise Example #2 Raise an exception on a bad username argument: class User def initialize(username) if username =~ /^[a-z][a-z0-9]+$/ @username = username else raise 'Invalid username' end CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Handling Exceptions Use a rescue clause with a begin statement. begin # Ruby code that may raise # an exception. rescue # Exception-handling code. end CIT 383: Administrative Scripting
Handling Exceptions by Type Specify the exception class after rescue By default, only StandardError is caught. To catch any exception rescue Exception To catch specific exception and assign to e. rescue ArgumentError => e Accessing the exception object string puts e.message CIT 383: Administrative Scripting
Catching Multiple Exception Types Handling multiple types identically: rescue ArgumentError, IOError => e Handling each type differently: begin #code that may raise exception rescue ArgumentError => e # code to handle ArgumentErrors rescue IOError => e # code to handle IOErrors end CIT 383: Administrative Scripting
Handling Raise Example #1 begin number = gets.chomp puts factorial(number) rescue ArgumentError puts "Please give a value >=1" rescue TypeError puts "Please give an integer value" end CIT 383: Administrative Scripting
Handling Raise Example #2 Retry restarts at top of begin block. # Create a User object, prompting the user for # a username repeated until we get a valid one. begin print “Enter a username: “ username = gets.chomp u = User.new(username) rescue Exception retry end CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Another Retry Example # Try to get URL up to 10 times in case svr slow tries = 0 begin tries += 1 geturl(‘http://example.com/’) rescue Exception => e puts e.message # print error if tries < 10 sleep(10) # wait 10 seconds retry # try get_url again end CIT 383: Administrative Scripting
Writing Exception Handlers Keep begin blocks short. begin # no more than 3 lines rescue end Move longer blocks into a new method. try_to_geturl() CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Defining Exceptions What is the purpose of exception classes? Each class can be handled differently by its own rescue block. Creating a new exception class class MyError < StandardError end Exception subclasses define no new methods Only characteristics that matter are their name and parent class. CIT 383: Administrative Scripting
CIT 383: Administrative Scripting Defining Exceptions Create a new exception class only when The caller has to do something different to recover from the error. Do not create a class for each error message Use the message string to contain the message instead of creating a new exception class. CIT 383: Administrative Scripting
Raise Example #2 Revisited class UsernameError < ArgumentError end class User def initialize(username) if username =~ /^[a-z][a-z0-9]+$/ @username = username else raise UsernameError CIT 383: Administrative Scripting
Handling Raise Example #2 # Create a User object, based on user-supplied # username. If username is invalid, retry. If # another exception is raised, print error but # do not retry. begin print “Enter a username: “ username = gets.chomp u = User.new(username) rescue UsernameError retry rescue Exception => e puts “Unexpected exception: #{e.message}” end CIT 383: Administrative Scripting
CIT 383: Administrative Scripting References Michael Fitzgerald, Learning Ruby, O’Reilly, 2008. David Flanagan and Yukihiro Matsumoto, The Ruby Programming Language, O’Reilly, 2008. Hal Fulton, The Ruby Way, 2nd edition, Addison- Wesley, 2007. Robert C. Martin, Clean Code, Prentice Hall, 2008. Dave Thomas with Chad Fowler and Andy Hunt, Programming Ruby, 2nd edition, Pragmatic Programmers, 2005. CIT 383: Administrative Scripting