Upgrade to Pro — share decks privately, control downloads, hide ads and more …

What is Ruby on Rails?

What is Ruby on Rails?

An intro to Ruby and then Rails with some comparisons to Python and Java.

Chris Dillon

October 15, 2012
Tweet

More Decks by Chris Dillon

Other Decks in Programming

Transcript

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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
  7. Blocks # multiply these numbers times 10 and print them

    array = [1, 2, 3, 4, 5] Monday, October 15, 12
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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 <shopping> <item name=”bread” quantity=”3” price=”2.50” /> <item name=”milk” quantity=”2” price=”3.50” /> </shopping> Monday, October 15, 12
  14. 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
  15. 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
  16. The Bad & Ugly Languages with the Fastest Benchmark Programs

    Single Core x86 Ubuntu Intel Q6600 Monday, October 15, 12
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  27. 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
  28. 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
  29. 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
  30. ~/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
  31. 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
  32. 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
  33. $ 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
  34. Start rails server $ rails s Replace Scaffolding <h1>My Great

    Blog</h1> <div id="posts"> <% @posts.each do |post| %> <div id="post"> <%= link_to post.title, post %> by <%= post.author %><br> <em>on: <%= post.updated_at %></em><br><br> <% end %> </div> <br /> <%= 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
  35. 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
  36. 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
  37. 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 # => #<Person:0x007f9e2a136878 @name="James", @age=99> Monday, October 15, 12
  38. 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 # => #<Person:0x007f9e2a136878 @name="James", @age=99> Monday, October 15, 12
  39. 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 # => #<Person:0x007f9e2a136878 @name="James", @age=99> 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
  40. Monkey Patching & Method Missing puts Person.create_with_name_and_problems("James",99).inspect # <Person:0x007ffc8a835250 @name="James",

    @problems=99> puts Person.create_with_age_and_problems_and_name(55, 99, "Jay- Z").inspect # <Person:0x007ffc8b0ae990 @name="Jay-Z", @age=55, @problems=99> 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 # => #<Person:0x007f9e2a136878 @name="James", @age=99> 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
  41. 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) # <Person:0x007fda421044f0 @name="James", @age=99, @problems=99> puts p.name # James puts p.age # 99 puts p.problems # 99 Monday, October 15, 12