Ruby + Ruby on Rails
Sam Ruby
In case you were curious…
No relation
Ruby
Elevator Pitch
- Ruby is Smalltalk with a better syntax
- Because—Dang it!—syntax matters!
Smalltalk 80
- Everything is an object
- type-checking is dynamic
- message-sending
- garbage collection
- everything is modifyable
- bytecodes
- MVC
- JITs (80’s)
Source: wikipedia
Preface
Myth: scripting languages can’t scale
- Counter-examples:
- BitTorrent (Python)
- Slashdot (Perl)
- Yahoo! (PHP)
- Reality:
- A script on modest modern hardware can saturate your bandwidth… with or without a database.
Basics
Ruby has all the basics that one would expect from a programming language…
assigments
name = "Sally";
tip = price * 0.15;
if statements
if answer != 'yes' then
STDOUT.puts('Why not?');
end
loop statements
while Time.now.hour < 17
Kernel.sleep(60);
end
total = 0;
for item in [ 6, 17, 3 ]
total += item;
end
functions
def circumference(radius)
return 2 * Math::PI * radius;
end
classes
class Dog
def bark()
STDOUT.puts("woof!");
end
end
fifi = Dog.new();
fifi.bark();
Note: new is a method on the Dog object.
Back to the 80's
“New” stuff
Everything is an object
Integer.parseInt(“5”)
“5”.to_i
Math.abs(-5)
-5.abs
type-checking is dynamic
def dump(results)
results << "abc"
results << "xyz"
end
Examples of classes that implement ”<<” :
everything is modifyable
Facilities enabled by being able to modify existing classes:
- Bug fixes
- Features
- Application Specific logic on generic (e.g. DOM) classes
message-sending
Facilities enabled by being able to capture messages:
- Remote Proxies
- Auto Loaders
- Decorators
- Mock Objects
- Builders
Block Example
sorted_employees =
employees.sort_by {|e| e.lastname}
Block Usages
- Iteration
- Sort and comparison
- Ensuring post process
- Conditionals
- Callbacks
- Enumerable
- Enumerator
Closure Example
def high_paid_employees(employees, limit)
employees.select{|e| e.salary > limit}
end
Part III
Incredible shrinking code
Hello World!
class Greeting {
public static void main(String args[]) {
System.out.println(“Hello World!”);
}
}
Kernel::puts(“Hello World!”);
Hello World!
class Greeting {
public static void main(String args[]) {
System.out.println(“Hello World!”);
}
}
puts(“Hello World!”);
Hello World!
class Greeting {
public static void main(String args[]) {
System.out.println(“Hello World!”);
}
}
puts(“Hello World!”)
Hello World!
class Greeting {
public static void main(String args[]) {
System.out.println(“Hello World!”);
}
}
puts “Hello World!”
Omittable
- self.
- ;
- then
- return
- () – method calls
- {} – hashes as parameters
Beans
class Point {
private int x;
public int getX() {
return this.x;
}
public void setX(int x) {
this.x = x;
}
}
Beans
class Point
def x()
return @x
end
def x=(x)
@x=x
end
end
Beans
class Point {
private float x;
public float getX() { return this.x; }
public void setX(float x) { this.x = x; }
}
class Point
def x() @x end
def x=(x) @x=x end
end
Beans
class Point {
private float x;
public float getX() { return this.x; }
public void setX(float x) { this.x = x; }
}
class Point
attr_accessor :x
end
Beans
class Point {
private float x,y;
public float getX() { return this.x; }
public void setX(float x) { this.x = x; }
public float getY() { return this.y; }
public void setY(float y) { this.y = y; }
}
class Point
attr_accessor :x, :y
end
Rails
class Entry < ActiveRecord::Base
acts_as_tree :order => “updated”
validates_presence_of :updated
end
Note:
- self.acts_as_tree( Hash[“order”.to_sym, “updated”] );
Expansion
Like a macro, that one line expands to define the following methods:
- self.root
- self.roots
- add_children
- build_parent
- build_to_children
- child_ids=
- children
- children=
- children_count
- create_in_children
- create_parent
- find_all_in_children
- find_in_children
- has_children?
- has_parent?
- parent
- parent=
- parent?
- remove_children
- set_parent_target
- siblings
Exceptions
Scanner sc = new Scanner(new File(args[0]));
while (sc.hasNextLine()) {
System.out.println(sc.nextLine());
}
sc.close();
Exceptions
Scanner sc = new Scanner(new File(args[0]));
try {
while (sc.hasNextLine()) {
System.out.println(sc.nextLine());
}
} finally {
sc.close();
}
Exceptions
f = open(ARGV[0])
begin
for line in f.readlines
puts line
end
ensure
f.close
end
Exceptions
open(ARGV[0]) do |f|
f.each {|line| puts line}
end
Ruby
Elevator Pitch
- Ruby is Smalltalk with a better syntax
- Because—Dang it—syntax matters!
Memes
Memes
- Agile
- Pragmatic
- Less Code
Summary
Dynamic languages
- Focus on the task at hand
- Executable pseudocode
- Enable domain specific grammars
- IDEs available, not required
- Eclipse plug-ins
- ActiveState
- FreeRide
- RadRails
- jEdit Ruby Editor Plugin
Begs the Question?
Does the world need another Smalltalk, even with a bettter syntax and all?
- Tool vendors would rather focus on customer requirements
- Customers would rather focus on business problems
- Developers would rather build upon the skills they have
Result
- Nine years of Ruby being a niche language, primarily in Japan
- 2005: enter Rails
Rails
Elevator Pitch
- Rails is the 80/20 rule applied twice
- 64% of the function for 4% of the complexity
- What about the “other” 36%?
- if anybody here doesn’t believe that J2EE has 36% fat, I’m talking to the wrong audience.
Memes
- Convention over Configuration
- D on’t R epeat Y ourself
- Opinionated Software
MVC(Model View Controller)
Ruby on Rails
Flow
Simple flow:
- requests are routed based on their URI to a controller,
- controllers contains business logic which accesses a model
- models contain verification logic and database access
- controllers ultimately render a view which may make use
of helpers and a layout.
JDBC
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/mysql";
Connection con = DriverManager.getConnection(url,"root", "");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(
"SELECT * FROM users where clue>0");
while ( rs.next() ) {
System.out.println(rs.getString("name"));
}
JNDI
Context env =
(Context) new InitialContext().lookup("java:comp/env");
DataSource pool = (DataSource) env.lookup("jdbc/test");
Connection conn = pool.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(
"SELECT * FROM users where clue>0");
while ( rs.next() ) {
System.out.println(rs.getString("name"));
}
PHP
mysql_connect("localhost", "root", "");
mysql_select_db("db");
$result = mysql_query("SELECT * FROM users where clue>0");
while ($row=mysql_fetch_array($result)) {
print $row["name"];
}
Rails
for user in User.find(:all, conditions=>'clue>0')
puts user.name
end
database
development:
adapter: mysql
database: customer_development
host: localhost
username: root
password:
test:
adapter: mysql
database: customer_test
host: localhost
username: root
password:
production:
adapter: mysql
database: customer_production
host: localhost
username: root
password:
Example of Conventions
Junit:
import junit.framework.TestCase;
public class ThingTester extends TestCase {
public void testSomeThing() throws Exception {
assertEquals(“non-zero”, 0, ””.length());
}
}
Extend a class, and follow a convention, and voila instant test cases
Example of Conventions
But what if you were in a language where introspection and reflection weren’t read-only?
User
class User < ActiveRecord::Base
end
user = User.find :first
puts user.name
- Looks for a table named users in the development/test/production database.
- Opinionated? Definately.
- Convention over Configuration
view (html)
<% for entry in @entries %>
<%= entry_header entry %>
<div class="blogbody">
<%= entry_title entry %>
<%= entry.summary or entry.content %>
<%= sigline entry %>
</div>
<% end %>
view (xml)
@entries.each do |entry|
xml.entry do
xml.title entry.title
xml.link :href=>url_for(entry.by_date)
xml.id entry.atomid
xml.updated entry.updated.iso8601
xml.author { xml.name entry.author.name } if entry.author
xml.content do
xml.div :xmlns=>'http://www.w3.org/1999/xhtml' do
xml << entry.content
end
end
end
end
Controller
class BlogController < ApplicationController
def posts
paginate :conditions => 'parent_id IS NULL',
:order_by => 'published DESC'
end
end
Function tests
def test_index
get :posts, :path=>['index.html']
assert_response :success
assert_template 'html'
# only two posts have titles
assert_tag :tag=>'div', :attributes => {:class => 'content'},
:children => {:count => 2, :only => {:child => {:tag => 'h3'} } }
end
“There exists a div tag in the response with class=’content’ which
has exactly two child ‘h3’ tags”
Summary
“Rails is a full-stack, open-source web framework in Ruby for writing real-world applications with joy and less code than most frameworks spend doing XML sit-ups” – http://www.rubyonrails.org/
Rails
Elevator Pitch
- Rails is the 80/20 rule applied twice
- 64% of the function for 4% of the complexity
Language?
Could Rails be done in other languages?
- Trails (Java)
- Cake (PHP)
- Subway, TurboGears (Python)
To date, it seems that Rails is often immitated, never duplicated
LINQ
Elevator Pitch
- C# and SQL, two great tastes that go together
- Unifying Objects, Tables, and Documents
Video
- Frankly, most of the credit for the impressive demo can be
attributed to the charisma of Anders
- Mitigated by Scoble’s annoying habit of whisking away from the
screen whenever I was trying to read it.
Arms Race
Java vs C#
- C# 1.0 was marginally better, attracted few converts
- C# 2.0 added generics, closures
- Java responded with generics
- C# 3.0 continues to raise the bar
- Building on closures
- Note: pretext of joint submission with HP, Intel seems to be gone
First example
Side-by-side
var q = c.customers
.where(c => c.City == “London”)
.select(c => c.CompanyName);
q = c.customers.
find_all {|c| c.city == “London”}.
map {|c| c.companyName}
Syntatic Sugar
var q = c.customers
.where(c => c.City == “London”)
.select(c => c.CompanyName);
var q =
from c in customers
where c.City == “London”
select c.CompanyName;
Syntatic Sugar
- This difference is not currently matched by Ruby.
- It is important because—Dang It!—syntax matters!
- Note a few obvious differences between “SQL#” and SQL:
- == vs =
- && vs and
- from/where/select vs select/from/where
LINQ
Elevator Pitch
- C# and SQL, two great tastes that go together
- Unifying Objects, Tables, and Documents
Ruby on Rails Adoption
- Existing, large customers won’t lead the way with Rails adoption
- Instead it will be their agile and more pragmatic competitors
- This will make it in the same way that Linux and Apache did: through
the back door and via acquisitions.
Outlook
- JRuby
- Excellent at consuming Java objects
- Java unliklely to ever fully comprehend the dynamic aspects of Ruby
- YARV
- new VM, better performance
- Rapid evolution