71
Ruby for Java Developers Neal Ford Architect ThoughtWorks www.nealford.com www.thoughtworks.com [email protected] Blog: memeagora.blogspot.com

Ruby for Java Developers

  • Upload
    elijah

  • View
    63

  • Download
    0

Embed Size (px)

DESCRIPTION

Ruby for Java Developers. Neal Ford Architect Thought Works www.nealford.com www.thoughtworks.com [email protected] Blog: memeagora.blogspot.com. Questions, Slides, and Samples. Please feel free to ask questions anytime The slides and samples will be available at www.nealford.com - PowerPoint PPT Presentation

Citation preview

Page 1: Ruby for Java Developers

Ruby for Java Developers

Neal FordArchitect

ThoughtWorkswww.nealford.com

[email protected]

Blog: memeagora.blogspot.com

Page 2: Ruby for Java Developers

2

Questions, Slides, and Samples

• Please feel free to ask questions anytime• The slides and samples will be available at

www.nealford.com• I’ll show that address again at the end

Page 3: Ruby for Java Developers

3

What This Session Covers:

• The Ruby Philosophy• Ruby syntax• Classes and objects in Ruby• Closures• Mixins• Networking• Threading• Test-first coding in Ruby• Ruby as a scripting language• Ruby as a web language

Page 4: Ruby for Java Developers

4

What is Ruby?

• From www.ruby-lang.org:• Ruby is the interpreted scripting language for quick and easy

object-oriented programming. • It has many features to process text files and to do system

management tasks (as in Perl). • It is simple, straight-forward, extensible, and portable.

• Ruby has:• Simple syntax, partially inspired by Eiffel and Ada• Exception handling features, like Java or Python• operators that are syntax sugar for the methods. You can

redefine them easily

Page 5: Ruby for Java Developers

5

Features of Ruby

• Ruby is a complete, full, pure object oriented language. • All data in Ruby is an object, in the sense of Smalltalk: no

exceptions. • Example: In Ruby, the number 1 is an instance of class Fixnum.

• Ruby's OO is carefully designed to be both complete and open for improvements. • Example: Ruby has the ability to add methods to a class, or

even to an instance during runtime. A• An instance of one class *can* behave differently from other

instances of the same class.

Page 6: Ruby for Java Developers

6

Features of Ruby

• Ruby features single inheritance only, *on purpose*. • But Ruby knows the concept of modules (called Categories in

Objective-C). • Modules are collections of methods. • Every class can import a module and so gets all its methods for

free.

• Ruby features true closures. • Not just unnamed function, but with present variable bindings.

• Ruby features blocks in its syntax (code surrounded by '{' ... '}' or 'do' ... 'end'). • These blocks can be passed to methods, or converted into

closures.

Page 7: Ruby for Java Developers

7

Features of Ruby

• Ruby features a true mark-and-sweep garbage collector. • It works with all Ruby objects. • You don't have to care about maintaining reference counts in

extension libraries.

• Integers in Ruby can (and should) be used without counting their internal representation. • There *are* small integers (instances of class Fixnum) and

large integers (Bignum), but you need not worry over which one is used currently.

• If a value is small enough, an integer is a Fixnum, otherwise it is a Bignum. Conversion occurs automatically.

Page 8: Ruby for Java Developers

8

Features of Ruby

• Ruby needs no variable declarations. • It uses simple naming conventions to denote the scope of

variables.

• Ruby can load extension libraries dynamically if an OS allows.

• Ruby features OS independent threading• Ruby is highly portable: it is developed mostly on Linux,

but works on many types of UNIX, DOS, Windows 95/98/Me/NT/2000/XP, MacOS, BeOS, OS/2, etc.

var_name => local variable@var_name => instance variable@var_name => global variable

Page 9: Ruby for Java Developers

9

Obtaining Ruby

• The main Ruby site: www.ruby-lang.org• For Windows, the One-Click Ruby Installer project on

RubyForge: http://rubyinstaller.rubyforge.org/wiki/wiki.pl• If you have Cygwin, you can choose Ruby as one of the

language options• There is a specific cygwin-ruby version, optimized for Cygwin

• The English reference manual is very outdated• However, http://www.ruby-doc.org/ has up to date

RubyDocs and other resources• The Pick-Axe book (shown at the end)

Page 10: Ruby for Java Developers

10

Classes and Objects in Ruby

class Employee def initialize(name, salary, hire_year) @name = name @salary = salary @hire_year = hire_year end def to_s "Name is #{@name}, salary is #{@salary}, “

+ "hire year is #{@hire_year}" end def raise_salary_by(perc) @salary += (@salary * 0.10) endend

Page 11: Ruby for Java Developers

11

Inheritance

class Manager < Employee def initialize(name, salary, hire_year, asst) super(name, salary, hire_year) @asst = asst end def to_s super + ",\tAssistant info: #{@asst}" end def raise_salary_by(perc) perc += 2005 - @hire_year super(perc) endend

Page 12: Ruby for Java Developers

12

Tying It Together

def show(emps) emps.each { |e| puts e }end

employees = Array.newemployees[0] = Employee.new("Homer", 200.0, 1995)employees[1] = Employee.new("Lenny", 150.0, 2000)employees[2] = Employee.new("Carl", 250.0, 1999)employees[3] = Manager.new("Monty", 3000.0, 1950,

employees[2])

show(employees)

employees.each { |e| e.raise_salary_by(10) }puts "\nGive everyone a raise\n\n"show employees

Page 13: Ruby for Java Developers

13

Class Members

class Employee @@num_employees = 0; def Employee.num_employees @@num_employees end def initialize(name, salary, hire_year) @name = name @salary = salary @hire_year = hire_year @@num_employees += 1 end # . . .

show employees

puts "Number of employees in the company: " + Employee.num_employees.to_s

Page 14: Ruby for Java Developers

14

Properties

class Employee def name @name end def salary @salary end def hire_year @hire_year end # . . . emp = Employee.new("Homer", 100.0, 2004)printf "Name: %s\n", emp.nameprintf "Salary: %d\n", emp.salaryprintf "Hire Year: %s\n", emp.hire_year

Page 15: Ruby for Java Developers

15

attr_readerclass Employee attr_reader :name, :salary, :hire_year def initialize(name, salary, hire_year) @name = name @salary = salary @hire_year = hire_year end

# . . .

emp = Employee.new("Homer", 100.0, 2004)printf "Name: %s\n", emp.nameprintf "Salary: %d\n", emp.salaryprintf "Hire Year: %s\n", emp.hire_year

Page 16: Ruby for Java Developers

16

Writable Properties

class Employee def name @name end def name=(new_name) @name = new_name end def salary @salary end def hire_year @hire_year end # . . . emp = Employee.new("Homer", 100.0, 2004)emp.name = "Monty"printf "Name: %s\n", emp.name

Page 17: Ruby for Java Developers

17

attr_writer

class Employee attr_reader :name, :salary, :hire_year attr_writer :name, :salary, :hire_year def initialize(name, salary, hire_year) @name = name @salary = salary @hire_year = hire_year end #. . .

emp = Employee.new("Homer", 100.0, 2004)emp.name = "Monty"printf "Name: %s\n", emp.name

Page 18: Ruby for Java Developers

18

Singletons

class Logger private_class_method :new @@logger = nil def Logger.create @@logger = new unless @@logger @@logger endend

puts Logger.create.object_idputs Logger.create.object_id

Page 19: Ruby for Java Developers

19

Access Control

• Ruby has 3 levels of access control

public • Called by anyone (i.e., no access control)• Default access (except for initialize, which is always private

protected • Only by objects of the defining class and its subclasses

private • Cannot be called without an explicit receiver• You cannot specify an object when using them• Can be called only in the defining class and by direct descendents within that same object

Page 20: Ruby for Java Developers

20

Private vs. Protected

• The difference is subtle• Protected

• Can be called by any instance of the defining class or its subclasses

• Private• Called only within the context of the calling object• Never possible to access another object’s private methods

directly, even if the object is the same class as the caller

Page 21: Ruby for Java Developers

21

Arrays - General Syntax

a = [ 3.14159, "pi", 99 ]a.type » Arraya.length » 3a[0] » 3.14159a[1] » "pi"a[2] » 99a[3] » nilb = Array.newb.type » Arrayb.length » 0b[0] = "second"b[1] = "array"b » ["second", "array"]

Page 22: Ruby for Java Developers

22

Arrays - Indexing

• Negative indicesa = [ 1, 3, 5, 7, 9 ]a[-1] » 9a[-2] » 7a[-99] » nil

• Pairsa = [ 1, 3, 5, 7, 9 ]a[1, 3] » [3, 5, 7]a[3, 1] » [7]a[-3, 2] » [5, 7]

Page 23: Ruby for Java Developers

23

Arrays - Ranges

a = [ 1, 3, 5, 7, 9 ]a[1..3] » [3, 5, 7]a[1...3] » [3, 5]a[3..3] » [7]a[-3..-1] » [5, 7, 9]

Page 24: Ruby for Java Developers

24

Array Assignment

a = [ 1, 3, 5, 7, 9 ] » [1, 3, 5, 7, 9]a[1] = 'foo' » [1, "foo", 5, 7, 9]a[-3] = 'bar' » [1, "foo", "bar", 7, 9]a[3] = [ 9, 8 ] » [1, "foo", "bar", [9, 8],

9]a[6] = 99 » [1, "foo", "bar", [9, 8], 9, nil,

99]

Page 25: Ruby for Java Developers

25

Array Assignment Ranges

a = [ 1, 3, 5, 7, 9 ] » [1, 3, 5, 7, 9]a[2, 2] = 'cat' » [1, 3, "cat", 9]a[2, 0] = 'dog' » [1, 3, "dog", "cat", 9]a[1, 1] = [ 9, 8, 7 ]

» [1, 9, 8, 7, "dog", "cat", 9]a[0..3] = [] » ["dog", "cat", 9]a[5] = 99 » ["dog", "cat", 9, nil, nil, 99]

Page 26: Ruby for Java Developers

26

Hashes

h = { 'dog' => 'canine', 'cat' => 'feline', 'donkey' => 'asinine' }

h.length » 3h['dog'] » "canine"h['cow'] = 'bovine'h[12] = 'dodecine'h['cat'] = 99h » {"cow"=>"bovine",

"cat"=>99, 12=>"dodecine", "donkey"=>"asinine", "dog"=>"canine"}

Page 27: Ruby for Java Developers

27

Blocks

class EmployeeList def initialize @employees = Array.new end def add(an_employee) @employees.push(an_employee) self end def delete_first @employees.shift end def delete_last @employees.pop end

Page 28: Ruby for Java Developers

28

Blocks def show @employees.each { |e| puts e } end def [](key) if key.kind_of?(Integer) @employees[key] end endend

list = EmployeeList.newlist.add(Employee.new("Homer", 200.0, 1995)). add(Employee.new("Lenny", 150.0, 2000)). add(Employee.new("Carl", 250.0, 1999))

list.showputs "Employee #1 is " + list[0].to_s

Page 29: Ruby for Java Developers

29

Iterating with for def [](key) if key.kind_of?(Integer) @employees[key] else for i in [email protected] return @employees[i] if key == @employees[i].name end end return nil endend

list = EmployeeList.newlist.add(Employee.new("Homer", 200.0, 1995)). add(Employee.new("Lenny", 150.0, 2000)). add(Employee.new("Carl", 250.0, 1999))

list.showputs "Employee #1 is " + list[0].to_sputs "Employee named 'Homer' is " + list["Homer"].to_s

Page 30: Ruby for Java Developers

30

Using Blocks

def [](key) if key.kind_of?(Integer) result = @employees[key] else result = @employees.find{ |anEmp| key == anEmp.name } end return result endend

puts "Employee #1 is " + list[0].to_sputs "Employee named 'Homer' is " +

list["Homer"].to_s

Page 31: Ruby for Java Developers

31

The Ruby Way

def [](key) return @employees[key] if key.kind_of?(Integer) return @employees.find { |anEmp| key == anEmp.name } endend

list = EmployeeList.newlist.add(Employee.new("Homer", 200.0, 1995)). add(Employee.new("Lenny", 150.0, 2000)). add(Employee.new("Carl", 250.0, 1999))

list.showputs "Employee #1 is " + list[0].to_sputs "Employee named 'Homer' is " +

list["Homer"].to_s

Page 32: Ruby for Java Developers

32

Building Iterators

def fib_up_to(max) i1, i2 = 1, 1 # parallel assignment while i1 <= max yield i1 i1, i2 = i2, i1+i2 endend

fib_up_to(1000) { |f| print f, " " }

Page 33: Ruby for Java Developers

33

Blocks for Transactions

class File def File.open_and_process(*args) f = File.open(*args) yield f f.close() endend

File.open_and_process("testfile", "r") do |aFile| print while aFile.getsend

Page 34: Ruby for Java Developers

34

Blocks for Transactions, Take 2

class File def File.myOpen(*args) aFile = File.new(*args) # If there's a block, pass in the file and close # the file when it returns if block_given? yield aFile aFile.close aFile = nil end return aFile endend

Page 35: Ruby for Java Developers

35

Closures

• Closures represent a (nameless) block of code that retains its context, even if the context is out of scope

• A closure object has code to run, the executable, and state around the code, the scope.

• You can refer to the local variables inside a closure.• Even after the function has returned, and its local scope

has been destroyed, the local variables remain in existence as part of the closure object.

• When no one refers to the closure anymore, it's garbage collected, and the local variables go away.

Page 36: Ruby for Java Developers

36

Closure Exampledef make_counter var = 0 proc do # coverts a block to a proc object

var +=1 endend

c1 = make_counterc1.callc1.callc1.call

c2 = make_counter

puts "c1 = #{c1.call}, c2 = #{c2.call}"# output: c1 = 4, c2 = 1

Page 37: Ruby for Java Developers

37

Regular Expressions

• Regular expressions are a built-in data type in Ruby

a = Regexp.new('^\s*[a-z]') » /^\s*[a-z]/b = /^\s*[a-z]/ » /^\s*[a-z]/c = %r{^\s*[a-z]} » /^\s*[a-z]/

a = "Fats Waller"a =~ /a/ » 1a =~ /z/ » nila =~ "ll" » 7

Page 38: Ruby for Java Developers

38

Extending Classes

• Classes are never closed in Ruby• You can add new methods to user or system classes• Just open the definition and start typing!class String def rot(num = 13) return self.split("").collect { |ch| if /^[a-z]$/ === ch ((ch[0] + num - 'a'[0]) % 26 + 'a'[0]).chr elsif /^[A-Z]$/ === ch ((ch[0] + num - 'A'[0]) % 26 + 'A'[0]).chr else ch end }.join("") end

alias rot13 rotend

Page 39: Ruby for Java Developers

39

Conditionals

• True in Ruby implies• Any value that is not nil or the constant false• Zero is not false• A zero-length string is not false

• Ruby includes an operator defined?• Returns nil if its argument (which can be an arbitrary

expression) is not defined• Otherwise returns a description of the argument

defined? 1 » "expression"defined? dummy » nildefined? printf » "method"defined? String » "constant"

Page 40: Ruby for Java Developers

40

Comparison Operators

Operator Meaning

== Equality

=== Equality in a case statement when clause

<=> General comparison operator, returns -1, 0, +1

<, <=, >=, >

Standard comparisons

=~ Regular expression pattern match

eql? True if receiver and argument are both the same type and have equal values1 == 1.0 >> true 1.eql?(1.0) >> false

equal? True if the receiver and argument have the same object id

Page 41: Ruby for Java Developers

41

if Expression

• General form:

if emp.name == "Homer" then handle = "slacker"elsif emp.name == "Lenny" then handle = "go-getter"else handle = "unknown"end

Page 42: Ruby for Java Developers

42

if Expression

• If you lay them out on separate lines, you can skip the then keyword

if emp.name == "Homer" handle = "slacker"elsif emp.name == "Lenny" handle = "go-getter"else handle = "unknown"end

Page 43: Ruby for Java Developers

43

if Expression

• You can use if as an expression if you want

handle = if emp.name == "Homer" then "slacker" elsif emp.name == "Lenny" then "go-getter" else "unknown" end

Page 44: Ruby for Java Developers

44

unless, Conditional Expressions

• The unless expression is a negated if statement

unless emp.salary > 2000 then raise_percentage = 0.10else raise_percentage = 0.11end

• There is also the ternary operator

raise_percentage = empl.salary > 2000 ? 0.10 : 0.11

Page 45: Ruby for Java Developers

45

If and Unless Modifiers

• Like Perl, statement modifiers allow you to attach conditional statements onto the end of a normal statement

mon,day,year = $1,$2,$3 if /(\d\d)-(\d\d)-(\d\d)/puts "a = #{a}" if debugprint total unless total == 0

while gets next if /^#/ # Skip comments parseLine unless /^$/ # Don't parse empty lines

end

Page 46: Ruby for Java Developers

46

Case Expressions

case inputLine when "debug" dumpDebugInfo dumpSymbols when /p\s+(\w+)/ dumpVariable($1) when "quit", "exit" exit else print "Illegal command: #{inputLine}"end

Page 47: Ruby for Java Developers

47

Case + Regular Expressions

case line when /name=(.*)/ puts "Name is #$1" when /salary=(.*)/ puts "Salary is #$1" when /Hire\sYear=(.*)/ puts "Hire Year is #$1"end

Page 48: Ruby for Java Developers

48

Loops

• While loop

while gets # . . .end

• Until loop

until emp.salary > 2500 employee_list.add(emp.pop)end

Page 49: Ruby for Java Developers

49

Loops as Modifiers

a *= 2 while a < 100

a -= 10 until a < 100

Page 50: Ruby for Java Developers

50

Iterators

3.times do print "knock "end# knock knock knock

0.upto(9) do |x| print x, " "end#0 1 2 3 4 5 6 7 8 9

0.step(12, 3) {|x| print x, " " } # 0, 3, 6, 9, 12

Page 51: Ruby for Java Developers

51

Exceptions

the_file = File.open(name, "w")begin # Exceptions raised by this code will # be caught by the following rescue clause while data = socket.read(512) the_file.write(data) end

rescue SystemCallError $stderr.print "IO failed: " + $! the_file.close File.delete(name) raiseend

Page 52: Ruby for Java Developers

52

ensure and else

f = File.open("testfile")begin # .. processrescue # .. handle errorelse puts "Congratulations-- no errors!"ensure f.close unless f.nil?end

Page 53: Ruby for Java Developers

53

retrybegin if @esmtp then @command.ehlo(helodom) else @command.helo(helodom) end

rescue ProtocolError if @esmtp then @esmtp = false retry else raise endend

Page 54: Ruby for Java Developers

54

Modules

• Modules allow you to group methods, classes, and constants• Provide unique namespaces• Implement mixins

• Namespaces allow you to partition your application into separate files

• Use the require keyword to reference other modules

Page 55: Ruby for Java Developers

55

Mixins

• Ruby does not support multiple inheritance• Instead, it supports mixins• You define methods within a module• You include that module into a class

Page 56: Ruby for Java Developers

56

Mixins

module Debug def who_am_i? "#{self.class.name} " + "(\##{self.object_id}): #{self.to_s}" endend

class Employee include Debug . . .endclass Manager < Employee include Debug . . .end

puts "\n\nWho are they?"puts employees[0].who_am_i?puts employees[3].who_am_i?

Page 57: Ruby for Java Developers

57

Threadsrequire 'net/http'

pages = %w( www.rubycentral.com www.nealford.com www.pragmaticprogrammer.com )threads = []for page in pages threads << Thread.new(page) { |myPage| h = Net::HTTP.new(myPage, 80) puts "Fetching: #{myPage}" resp, data = h.get('/', nil ) puts "Got #{myPage}: #{resp.message}" }end

threads.each { |aThread| aThread.join }

Page 58: Ruby for Java Developers

58

Thread Variables

• Thread has a special facility that allows thread-local variables to be created and accessed by name

• Treat the thread object as if it were a Hashcount = 0arr = []10.times do |i| arr[i] = Thread.new { sleep(rand(0)/10.0) Thread.current["mycount"] = count count += 1 }endarr.each {|t| t.join; print t["mycount"], ", " }puts "count = #{count}"

Page 59: Ruby for Java Developers

59

Testing and Rubyrequire 'test/unit/testcase'require 'Employee'

class TestEmployee < Test::Unit::TestCase def setup @emp = Employee.new("Homer", 3000, 2004) end def teardown @emp = nil end

def test_raise_salary_by @emp.salary = 2000 perc = 10; expected_salary = @emp.salary +

(@emp.salary * 0.1 * perc) @emp.raise_salary_by(10) assert(@emp.salary = expected_salary) endend

Page 60: Ruby for Java Developers

60

Testing and Ruby

class TestEmployeeList < Test::Unit::TestCase

def setup @list = EmployeeList.new @test_subject =

Employee.new("Homer", 3000, 2004) end def teardown @list = nil @test_subject = nil end def test_add @list.add(@test_subject) assert(@list.delete_last.eql?(@test_subject)) assert(@list.empty?) end

Page 61: Ruby for Java Developers

61

Testing and Rubyrequire 'test/unit/testsuite'require 'test/unit/ui/tk/testrunner'require 'test/unit/ui/console/testrunner'require 'TestEmployee'require 'TestEmployeeList'

class TestSuite_AllTests def self.suite suite = Test::Unit::TestSuite.new("HR Tests") suite << TestEmployee.suite suite << TestEmployeeList.suite return suite endend

#Test::Unit::UI::Tk::TestRunner.run(TestSuite_AllTests)Test::Unit::UI::Console::TestRunner.run(

TestSuite_AllTests)

Page 62: Ruby for Java Developers

62

Controlling Windows

require 'Win32API'require 'win32ole'

class DailyLogs private @@Home_Dir = "c:\\MyDocuments\\" def get_doc_list docs = Array.new docs << "im\\2005 Weekly Workout Planner.xls" docs << "im\\2005 Workout Log.xls" docs << "trackers\\Master Task List.xls" end

Page 63: Ruby for Java Developers

63

Controlling Windows

public def open_daily_logs excel = WIN32OLE.new("excel.application") workbooks = excel.WorkBooks excel.Visible = true get_doc_list.each { |f| workbooks.Open(@@Home_Dir + f, true) } excel.Windows.Arrange(7) end end

DailyLogs.new.open_daily_logs

Page 64: Ruby for Java Developers

64

Java vs. Ruby: Java, City.javapublic class City { private String name; private String symposiumName; private int showId;

public City(final String name, final String symposiumName, final int showId) { this.name = name; this.symposiumName = symposiumName; this.showId = showId; }

public String getName() { return name; }

public String getSymposiumName() { return symposiumName; }

public int getShowId() { return showId; }}

Page 65: Ruby for Java Developers

65

Java vs. Ruby, Harvester.javapublic class Harvester { public static final String WEB_SITE = "http://www.nofluffjuststuff.com"; public static final String PAGE_NAME = "/show_topics.jsp?showId="; private static final String NAME_REGEX = "Neal\\sFord";

public Harvester() { int count; System.out.println("No Fluff, Just Stuff sessions:"); System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); try { for (Iterator<City> cityItr = createCityList().iterator(); cityItr.hasNext();) { count = 0; City city = cityItr.next(); StringBuffer content = getWebContent(city); Matcher matcher = generateRegexForMatch(content); while (matcher.find()) { count++; } System.out.println(city.getName() + " = " + count); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }

Page 66: Ruby for Java Developers

66

Java vs. Ruby, Harvester.java private Matcher generateRegexForMatch(final StringBuffer content) { Pattern pattern = Pattern.compile(NAME_REGEX); return pattern.matcher(content); }

private StringBuffer getWebContent(final City city) throws IOException {

URL url = new URL(WEB_SITE + PAGE_NAME + city.getShowId()); HttpURLConnection http = (HttpURLConnection) url.openConnection(); InputStream is = http.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); String line; StringBuffer content = new StringBuffer(1000); while ((line = reader.readLine()) != null) { content.append(line); } return content; }

private List<City> createCityList() { List<City> cityList = new ArrayList<City>(10); cityList.add(new City("Salt Lake", "Salt Lake", 16)); cityList.add(new City("Pacific Northwest", "Seattle", 32)); return cityList; }}

Page 67: Ruby for Java Developers

67

Java vs. Ruby, harvester.rbrequire 'net/http'

class City def initialize(symposium_name, city_name, show_id) @city_name = city_name @symposium_name = symposium_name @show_id = show_id end attr_reader :city_name, :symposium_name, :show_idend

def check_city(http, url, city) count = 0 http.request_get(url) { |response| response.read_body { |body| body.scan(/Neal\sFord/) { count += 1 } } } printf("%-20s = %d\n", city, count)end

Page 68: Ruby for Java Developers

68

Java vs. Ruby, harvester.rb

Page_Name = '/show_topics.jsp?showId='cities = Array.newcities << City.new('Salt Lake', 'Salt Lake', 16)cities << City.new('Pacific', 'Seatle', 32)

http = Net::HTTP.new('www.nofluffjuststuff.com', 80)

puts "No Fluff, Just Stuff sessions:"puts "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"cities.each { |city| check_city(http, Page_Name + city.show_id.to_s, city.city_name)}

Page 69: Ruby for Java Developers

69

Resources:

The “Pick-Axe” Book

Programming Ruby: The Pragmatic Programmer’s Guide

Dave Thomas, Andy Hunt

Ruby in a Nutshell

Yukihiro Matsumoto, David L. Reynolds (Translator)

Page 70: Ruby for Java Developers

70

Resources:

Ruby Developer's Guide

Michael Neumann, Jonothon Ortiz, Robert Feldt, Lyle Johnson

The Ruby Way

Hal Fulton, Guy Hurst

Page 71: Ruby for Java Developers

Neal Fordwww.nealford.com

[email protected]

Questions?

Samples & slides at www.nealford.com