Slide 1

Slide 1 text

What is Ruby on Rails? Monday, October 15, 12

Slide 2

Slide 2 text

Scope One Hour Speedrun ie: Knowing something is possible vs. exactly how ie: Trying to avoid all syntax details but please ask ie: You are smart and can figure the details out Monday, October 15, 12

Slide 3

Slide 3 text

Quick History Created in 1995 Created by Yukihiro Matsumoto (Matz) "For me the purpose of life is partly to have joy. Programmers often feel joy when they can concentrate on the creative side of programming, So Ruby is designed to make programmers happy." Inspired by Perl and Smalltalk Many Implementations MRI, JRuby, MacRuby, Rubinius Version 1.8 vs 1.9 1.8.7 is Rails 2, 1.9.2+ is Rails 3 just use latest stable (Ruby 1.9.3 today, Rails 3.1.3) Monday, October 15, 12

Slide 4

Slide 4 text

What is Ruby $ which ruby gem irb /usr/bin/ruby /usr/bin/gem /usr/bin/irb $ ruby -v ruby 1.9.3p0 (2011-10-30 revision 33570) [x86_64-darwin11.2.0] $ ruby -e 'puts "Hello World"; print "Hello World\n"' Hello World Hello World $ gem install rails Monday, October 15, 12

Slide 5

Slide 5 text

IRB & Ruby Methods $ irb >> a = [1, 2, 3, 4, 5] => [1, 2, 3, 4, 5] >> a.class => Array >> a.max => 5 >> a.m [TAB][TAB] array.map array.map! array.max array.max_by ... >> a.reverse => [5, 4, 3, 2, 1] >> a => [1, 2, 3, 4, 5] >> a.reverse! => [5, 4, 3, 2, 1] >> a => [5, 4, 3, 2, 1] >> a.sort! => [1, 2, 3, 4, 5] >> a => [1, 2, 3, 4, 5] Monday, October 15, 12

Slide 6

Slide 6 text

Ruby Compared aka: inaccurate opinion mode aka: broad-brush language flame war mode Python Close Race I don’t grok Python Hard to find people that really grok Python and Ruby and both communities Ruby has blocks, Python has namespaces Ruby no significant whitespace, Pythonistas hate that comment (editor) Easier than Java Factory Factories and XML configuration files forevah Prettier than Perl It’s an easy switch and you’ll love Ruby classes / OO stuff More Portable than C# (discount Mono) Windows is actually a problem area for Ruby Not Tied to the Web like PHP (but many times used only for Rails) Monday, October 15, 12

Slide 7

Slide 7 text

Ruby “vs” Python & Perl Python ages = [18, 16, 17, 18, 16, 19, 14, 17, 19, 18] unique_ages = list(set(ages)) Ruby ages = [18, 16, 17, 18, 16, 19, 14, 17, 19, 18] unique_ages = ages.uniq Perl my @list = qw{ox cat deer whale}; my @lengths = map {length($_)} @list; print "@lengths\n"; 2 3 4 5 Ruby ["ox", "cat", "deer", "whale"].collect{|i| i.length} => [2, 3, 4, 5] Got lucky in this example Python programs are sometimes shorter than Ruby ones. It goes both ways. Argh, $_ the line noise special variable. It’s a block! .collect takes code as an argument. Weird huh? Monday, October 15, 12

Slide 8

Slide 8 text

Blocks Monday, October 15, 12

Slide 9

Slide 9 text

Blocks # multiply these numbers times 10 and print them array = [1, 2, 3, 4, 5] Monday, October 15, 12

Slide 10

Slide 10 text

Blocks # multiply these numbers times 10 and print them array = [1, 2, 3, 4, 5] # long, SO LONG, full of semi-colons not the ruby way math_results = Array.new(); # multi-line block array.each() do |element| math_results.push(element * 10); end puts(math_results.join(", ")); 10, 20, 30, 40, 50 Monday, October 15, 12

Slide 11

Slide 11 text

Blocks # multiply these numbers times 10 and print them array = [1, 2, 3, 4, 5] # long, SO LONG, full of semi-colons not the ruby way math_results = Array.new(); # multi-line block array.each() do |element| math_results.push(element * 10); end puts(math_results.join(", ")); 10, 20, 30, 40, 50 # slightly shorter way # get rid of parens and .push (<< is push) math_results = Array.new # multi-line block array.each do |e| math_results << e * 10 end puts math_results.join ", " 10, 20, 30, 40, 50 Monday, October 15, 12

Slide 12

Slide 12 text

Blocks # multiply these numbers times 10 and print them array = [1, 2, 3, 4, 5] # long, SO LONG, full of semi-colons not the ruby way math_results = Array.new(); # multi-line block array.each() do |element| math_results.push(element * 10); end puts(math_results.join(", ")); 10, 20, 30, 40, 50 # slightly shorter way # get rid of parens and .push (<< is push) math_results = Array.new # multi-line block array.each do |e| math_results << e * 10 end puts math_results.join ", " 10, 20, 30, 40, 50 # pretty short way math_results = Array.new # single line block array.each {|e| math_results << e * 10 } puts math_results.join ", " 10, 20, 30, 40, 50 Monday, October 15, 12

Slide 13

Slide 13 text

Blocks # multiply these numbers times 10 and print them array = [1, 2, 3, 4, 5] # long, SO LONG, full of semi-colons not the ruby way math_results = Array.new(); # multi-line block array.each() do |element| math_results.push(element * 10); end puts(math_results.join(", ")); 10, 20, 30, 40, 50 # slightly shorter way # get rid of parens and .push (<< is push) math_results = Array.new # multi-line block array.each do |e| math_results << e * 10 end puts math_results.join ", " 10, 20, 30, 40, 50 # pretty short way math_results = Array.new # single line block array.each {|e| math_results << e * 10 } puts math_results.join ", " 10, 20, 30, 40, 50 # really short way # single line block, no second variable, still no parens, no semicolons puts array.collect {|e| e * 10 }.join ", " 10, 20, 30, 40, 50 Monday, October 15, 12

Slide 14

Slide 14 text

Java Bashing Time public class JavaIsSoAnnoying { public static void main(String[] args) { int[] array = { 1, 2, 3, 4, 5 }; int[] math_results = new int[5]; int i = 0; for (int e : array) { math_results[i] = e * 10; i++; } System.out.println(JavaIsSoAnnoying.join(math_results, ", ")); } private static String join(int[] input, String delimiter) { StringBuilder sb = new StringBuilder(); for(int value : input) { sb.append(value); sb.append(delimiter); } int length = sb.length(); if(length > 0) { sb.setLength(length - delimiter.length()); } return sb.toString(); } } Monday, October 15, 12

Slide 15

Slide 15 text

Ruby vs Everyone aka:blocks are amazing and encourages beautiful DSLs Python (namespaces, thee I covet) from xml.dom import minidom csv = """bread,3,2.50 milk,2,3.50""" doc = minidom.Document() shopping = doc.createElement("shopping") for line in csv.split("\n"): name, quantity, price = line.split(",") el = doc.createElement("item") el.setAttribute("name", name) el.setAttribute("quantity", quantity) el.setAttribute("price", price) shopping.appendChild(el) print shopping.toprettyxml() Ruby # gem install builder require 'builder' xml = Builder::XmlMarkup.new xml.shopping do xml.item(:name => "bread", :quantity => 3, :price => "2.50") xml.item(:name => "milk", :quantity => 2, :price => "3.50") end xml Monday, October 15, 12

Slide 16

Slide 16 text

The Good, The Bad & The Ugly Monday, October 15, 12

Slide 17

Slide 17 text

The Bad & Ugly langpop.com Monday, October 15, 12

Slide 18

Slide 18 text

Runtime Performance Myth canrailsscale.com twitter moved away language speed myth and HTTP (@igrigorik) Need Osmosis Time “Weird” gem names like: spork, cucumber, wirble Sites like: github and rubyforge Not Installed or Old RHEL 5.6 has 1.8.5 Mac (Lion) has 1.8.6 Ubuntu has 1.9.2p0 which is actually called 1.9.1 Windows is near impossible to get running The Bad & Ugly igvita.com Monday, October 15, 12

Slide 19

Slide 19 text

The Bad & Ugly http://shootout.alioth.debian.org/ "After all, facts are facts, and although we may quote one to another with a chuckle the words of the Wise Statesman, 'Lies--damned lies-- and statistics,' still there are some easy figures the simplest must understand, and the astutest cannot wriggle out of." Leonard Henry Courtney, 1895 A comparison between programs written in such different languages is a comparison between apples and oranges - but sometimes the choice is between apples and oranges. Languages with the Fastest Benchmark Programs Quad Core x86 Ubuntu Intel Q6600 Monday, October 15, 12

Slide 20

Slide 20 text

The Bad & Ugly Languages with the Fastest Benchmark Programs Single Core x86 Ubuntu Intel Q6600 Monday, October 15, 12

Slide 21

Slide 21 text

The Good Community Testing and Craftsmanship culture Fun and sometimes weird _why’s poignant guide to ruby (Google for PDF) Amazing Projects and Libraries Rails (specifically ActiveRecord) Rspec & Cucumber (great example of DSL blocks) Heroku and EngineYard Amazing Powah in the End Game Blocks Monkey Patching Method Missing and Metaprogramming Sometimes, really short programs - langref.org Gems are Easy to Get rubygems.org 384,005,635 downloads of 31,312 gems since July 2009 Monday, October 15, 12

Slide 22

Slide 22 text

The Good Street Cred HBase, Chef, Redmine, Jetbrains IDE, Apple, Etsy Twitter, Hulu, Groupon, Github, LivingSocial (DC) RoAR, Variety of DC Contractors Extremely Rapid Development Core Tech is Flexible Mobile Development (Rhomobile, cross platform framework) Game Development (Gosu 2D) Native Desktop Apps (Macruby, Shoes) CLI (clamp) Monday, October 15, 12

Slide 23

Slide 23 text

What can Ruby not do? Low Level Stuff Embedded Systems (C) Drivers (C) Performance Computing Image Processing Simulation Compute PI C, C++, Java is faster, even when using JRuby Monday, October 15, 12

Slide 24

Slide 24 text

Rails Monday, October 15, 12

Slide 25

Slide 25 text

Rails’ Origin Ruby Runs Rails like Python runs Django Extracted from Basecamp by David Heinemeier Hansson @dhh Ruby’s Killer App Creating a blog in 15 minutes with Rails by DHH in 2005 Less total app code than just J2EE config lines Derivatives: Grails (Java) CakePHP (PHP) Not Django Monday, October 15, 12

Slide 26

Slide 26 text

But What Is It Web framework MVC Less brittle SQL, cleaner code Keeps SQL queries out of your views Multiple views and layouts ORM (ActiveRecord) Object impedance mismatch Explicit Dev / Test / Prod Environments Form Validation and JS helpers Mailers Password reset Caching hooks with memcached etc Background jobs User Authentication / Authorization OAuth and Facebook login Generates web service URLs /products/45 /products/45.json /products/rss.xml Monday, October 15, 12

Slide 27

Slide 27 text

Rails à la Carte I Don’t Need All of This! You might not but you can pick and choose bits of rails to use Script that hits a database Number formatting Dummy Data Generator Data Migrator I Don’t Need Any of This! You should try at least one web framework if you work on a webapp MVC is a great pattern to learn But you may be right :) Monday, October 15, 12

Slide 28

Slide 28 text

Starting out Generators & Scaffolding Convention over Configuration Defaults for development speed Opinionated Pluralization of names Mouse object lives in Mice table Department object lives in Departments table ID field as primary key RESTful URLs “Magic” can be overridden Primary Keys can be set Table names can be set Prius -> Prii AKA: Don’t worry Monday, October 15, 12

Slide 29

Slide 29 text

Build a Game Store The typical rails example is a blog. Brook is sick of that. I am too. So we’ll build an ecommerce site in 20 minutes but I’ll have to go fast. Not designed in 20 minutes! https://github.com/squarism/quickie_mart Monday, October 15, 12

Slide 30

Slide 30 text

Rails console to see data Tests run immediately when you save spork + guard + inotify + rspec + growl Test Database with Factories and Fixtures End Game ie: Sell Me on This Monday, October 15, 12

Slide 31

Slide 31 text

Tools and Docs pragprog.com Great books with free e-updates and Dropbox support (written in rails) Not just Ruby and Rails ruby-toolbox.com popular gem list passenger Apache deployment aka: mod_rails rvm multiple ruby installs and project isolation (virtualenv etc in Python) Docs http://api.rubyonrails.org (not great) http://guides.rubyonrails.org/ (great) http://www.ruby-doc.org/core-1.9.3/ (good) http://apidock.com (meh) Monday, October 15, 12

Slide 32

Slide 32 text

Where to Start 15 minute intro tryruby.org, interactive ruby shell on the web Free interactive class codeschool.com - rails for zombies (2 hours), free Buy a book It takes me three Watch some screencasts railscasts.com (5 minute videos) Reinvent the wheel Martial Arts Shu Ha Ri: Imitate, Assimilate, Innovate Or: Write a blog, grok some libs, write a lib Try not to do everything in one project 80/20 rule? Monday, October 15, 12

Slide 33

Slide 33 text

Extra Credit If there’s time Monday, October 15, 12

Slide 34

Slide 34 text

Build a Blog Rails is not a blog platform Just the canonical hello world Posts and Comments SQL Database (sqlite3) Javascript helpers (jQuery) CSS stylin’ RESTful URLs Atom RSS feed No concept of users, authors or permissions (because of time) Monday, October 15, 12

Slide 35

Slide 35 text

Blog ~$ cd ~/src/rails ~/src/rails$ rails Usage: rails new APP_PATH [options] Options: -r, [--ruby=PATH] # Path to the Ruby binary of your choice # Default: /Users/chris/.rvm/rubies/ruby-1.9.3-p0/bin/ruby -b, [--builder=BUILDER] # Path to a application builder (can be a filesystem path or URL) -m, [--template=TEMPLATE] # Path to an application template (can be a filesystem path or URL) [--skip-gemfile] # Don't create a Gemfile [--skip-bundle] # Don't run bundle install -G, [--skip-git] # Skip Git ignores and keeps -O, [--skip-active-record] # Skip Active Record files -S, [--skip-sprockets] # Skip Sprockets files -d, [--database=DATABASE] # Preconfigure for selected database (options: mysql/oracle/postgresql/ ... ... Description: The 'rails new' command creates a new Rails application with a default directory structure and configuration at the path you specify. Example: rails new ~/Code/Ruby/weblog This generates a skeletal Rails installation in ~/Code/Ruby/weblog. See the README in the newly created application to get going. ~/src/rails$ rails new blog create create README create Rakefile create config.ru create .gitignore create Gemfile create app create app/assets/images/rails.png ... Complete Guide at http://guides.rubyonrails.org/getting_started.html Monday, October 15, 12

Slide 36

Slide 36 text

~/src/rails/blog$ ls -l total 56 -rw-r--r-- 1 user group 716 Nov 2 15:48 Gemfile -rw-r--r-- 1 user group 2597 Nov 2 15:49 Gemfile.lock -rw-r--r-- 1 user group 9208 Nov 2 15:48 README -rw-r--r-- 1 user group 269 Nov 2 15:48 Rakefile drwxr-xr-x 8 user group 272 Nov 2 15:48 app drwxr-xr-x 10 user group 340 Nov 2 15:48 config -rw-r--r-- 1 user group 154 Nov 2 15:48 config.ru drwxr-xr-x 3 user group 102 Nov 2 15:48 db drwxr-xr-x 3 user group 102 Nov 2 15:48 doc drwxr-xr-x 4 user group 136 Nov 2 15:48 lib drwxr-xr-x 3 user group 102 Nov 2 15:48 log drwxr-xr-x 8 user group 272 Nov 2 15:48 public drwxr-xr-x 3 user group 102 Nov 2 15:48 script drwxr-xr-x 8 user group 272 Nov 2 15:48 test drwxr-xr-x 3 user group 102 Nov 2 15:48 tmp drwxr-xr-x 4 user group 136 Nov 2 15:48 vendor $ rake db:create $ ls db development.sqlite3 seeds.rb test.sqlite3 # generator help $ rails g # model generator help, you can type g or generate $ rails g model # save time $ rails g scaffold post author:string title:string body:text $ rake db:migrate == CreatePosts: migrating ==================================================== -- create_table(:posts) -> 0.0017s == CreatePosts: migrated (0.0018s) =========================================== Layout and Generators Monday, October 15, 12

Slide 37

Slide 37 text

Optional nice-to-have bit $ vi Gemfile gem ‘wirble’ gem ‘hirb’ $ bundle # Google Hirb and Wirble .irbrc setup to automatically flip on colors and table output $ rails console Loading development environment (Rails 3.1.2) >> Post.all Post Load (0.1ms) SELECT "posts".* FROM "posts" => [] >> Post.first Post Load (0.1ms) SELECT "posts".* FROM "posts" LIMIT 1 => nil # now we can play a little bit >> Post.new # one way to create a row, another is p = Post.new, set attributes, then p.save >> Post.create(:author => "Charlie", :title => "Tigerblood!", :body => "Something something winning something") # now we can play a little bit >> Post.first >> Post.count >> post = Post.first >> post.author => "Charlie" First Model Monday, October 15, 12

Slide 38

Slide 38 text

Date math on date columns >> Date.yesterday >> Date.today.end_of_year >> (Date.today - 20.years - 1.week).end_of_month >> Date.today >> Post.first.updated_at + 9.months Scopes and Validations class Post < ActiveRecord::Base has_many :comments scope :crazies, where(:author => "Charlie") validates_presence_of :author, :body, :title def self.is_charlie_ranting? where(:author => "Charlie").count > 0 end end >> Post.create(:author => "Gary Busey", :title => "Oh my.", :body => "...") >> Post.crazies.count >> Post.is_charlie_ranting? ActiveRecord Powah Monday, October 15, 12

Slide 39

Slide 39 text

$ rails g scaffold comment author:string body:text post_id:integer $ rake db:migrate # Add relationships # app/models/post.rb class Post < ActiveRecord::Base has_many :comments end # app/models/comment.rb class Comment < ActiveRecord::Base belongs_to :post end # fire up rails console again, or rails c or type reload! in rails console $ rails console # we could do this ... >> Comment.create(:author => "Shrink", :post_id => 1 ... etc # but comment = Comment.new comment.author = "Shrink" comment.body = "You're crazy." # << is push, there's also .push if you want p = Post.first p.comments << comment Post.first.comments.count => 1 Post.where(:author => "Charlie").first.comments.where(:author => "Shrink").first.body => "You're crazy." ActiveRecord Powah Monday, October 15, 12

Slide 40

Slide 40 text

Start rails server $ rails s Replace Scaffolding

My Great Blog

<% @posts.each do |post| %>
<%= link_to post.title, post %> by <%= post.author %>
on: <%= post.updated_at %>

<% end %>

<%= link_to 'New Post', new_post_path %> It’s not just HTML http://localhost:3000/posts.json # edit this first: app/controllers/posts_controller.rb format.xml { render xml: @posts } http://localhost:3000/posts.xml MVC: How much and which code did I touch to change formats? Views Monday, October 15, 12

Slide 41

Slide 41 text

Ruby vs Python Python ‘{:,}’.format(1234567890) '1,234,567,890' Ruby require ‘action_controller’ ActionController::Base.helpers.number_with_delimiter(1234567890) => "1,234,567,890" class Fixnum def commify self.to_s.reverse.gsub( %r{([[:digit:]]{3})(?=[[:digit:]])(?![[:digit:]]*\.)}, "\\1#{','}" ).reverse end end 1234567890.commify => "1,234,567,890" 1_234_567_890.commify => "1,234,567,890" http:/ /postyourcode.heroku.com/ Monday, October 15, 12

Slide 42

Slide 42 text

Monkey Patching & Method Missing Monday, October 15, 12

Slide 43

Slide 43 text

Monkey Patching & Method Missing class Person attr_accessor :name, :age, :problems def initialize(name, age) @name = name @age = age end end Monday, October 15, 12

Slide 44

Slide 44 text

Monkey Patching & Method Missing class Person attr_accessor :name, :age, :problems def initialize(name, age) @name = name @age = age end end puts Person.new("James", 99).inspect # => # Monday, October 15, 12

Slide 45

Slide 45 text

Monkey Patching & Method Missing class Person attr_accessor :name, :age, :problems def initialize(name, age) @name = name @age = age end end begin Person.create_with_name_and_problems("James", 99) # => undefined method `create_with_name_and_problems' # for Person:Class (NoMethodError) rescue NoMethodError # just continue end puts Person.new("James", 99).inspect # => # Monday, October 15, 12

Slide 46

Slide 46 text

Monkey Patching & Method Missing class Person attr_accessor :name, :age, :problems def initialize(name, age) @name = name @age = age end end begin Person.create_with_name_and_problems("James", 99) # => undefined method `create_with_name_and_problems' # for Person:Class (NoMethodError) rescue NoMethodError # just continue end puts Person.new("James", 99).inspect # => # class Person def initialize end def self.method_missing(meth, *args, &block) puts "OH NO! No method!" if meth.to_s =~ /^create_with_(.+)$/ self.run_create_with_method($1, *args, &block) else super end end def respond_to?(meth, *args, &block) if self.meth.to_s =~ /^create_with_.*$/ true else super end end def self.run_create_with_method(attrs, *args, &block) attrs = attrs.split('_and_') # #transpose will zip the two arrays together like so: # [[:a, :b, :c], [1, 2, 3]].transpose # # => [[:a, 1], [:b, 2], [:c, 3]] attrs_with_args = [attrs, args].transpose attributes = Hash[attrs_with_args] p = Person.new attributes.keys.each do |a| p.instance_variable_set "@#{a}", attributes[a] end return p end end Monday, October 15, 12

Slide 47

Slide 47 text

Monkey Patching & Method Missing puts Person.create_with_name_and_problems("James",99).inspect # puts Person.create_with_age_and_problems_and_name(55, 99, "Jay- Z").inspect # class Person attr_accessor :name, :age, :problems def initialize(name, age) @name = name @age = age end end begin Person.create_with_name_and_problems("James", 99) # => undefined method `create_with_name_and_problems' # for Person:Class (NoMethodError) rescue NoMethodError # just continue end puts Person.new("James", 99).inspect # => # class Person def initialize end def self.method_missing(meth, *args, &block) puts "OH NO! No method!" if meth.to_s =~ /^create_with_(.+)$/ self.run_create_with_method($1, *args, &block) else super end end def respond_to?(meth, *args, &block) if self.meth.to_s =~ /^create_with_.*$/ true else super end end def self.run_create_with_method(attrs, *args, &block) attrs = attrs.split('_and_') # #transpose will zip the two arrays together like so: # [[:a, :b, :c], [1, 2, 3]].transpose # # => [[:a, 1], [:b, 2], [:c, 3]] attrs_with_args = [attrs, args].transpose attributes = Hash[attrs_with_args] p = Person.new attributes.keys.each do |a| p.instance_variable_set "@#{a}", attributes[a] end return p end end Monday, October 15, 12

Slide 48

Slide 48 text

Actually # The better way in this very contrived case would be to make a # parameterized initialize method. class Person attr_reader :name, :age, :problems def initialize args args.each do |k,v| instance_variable_set("@#{k}", v) unless v.nil? end end end p = Person.new(:name => "James", :age => 99, :problems => 99) # puts p.name # James puts p.age # 99 puts p.problems # 99 Monday, October 15, 12

Slide 49

Slide 49 text

Unfair Question Would rapid development help this problem? Monday, October 15, 12