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

Ruby + Rails = LOVE (or, what makes the kool-ai...

Ruby + Rails = LOVE (or, what makes the kool-aid so tasty)

These slides are from some on-site training I did in early 2011, introducing a room full of .NET developers to the joys of Ruby.

Avatar for Ernie Miller

Ernie Miller

April 20, 2011
Tweet

More Decks by Ernie Miller

Other Decks in Programming

Transcript

  1. Ruby + Rails = LOVE (or, what makes the kool-aid

    so tasty) Friday, April 15, 2011
  2. Hi. • Ernie Miller • Rails Core Contributor • Ruby

    fanboi • Language Tourist Friday, April 15, 2011
  3. Hi. • Ernie Miller • Rails Core Contributor • Ruby

    fanboi • Language Tourist Ada / BASIC / C (++, #) / Java / Lua Obj-C / Pascal / Perl / PHP / More... Friday, April 15, 2011
  4. Hi. • Ernie Miller • Rails Core Contributor • Ruby

    fanboi • Language Tourist Ada / BASIC / C (++, #) / Java / Lua Obj-C / Pascal / Perl / PHP / More... Friday, April 15, 2011
  5. Ruby • Designed by Yukihiro “matz” Matsumoto • Originated in

    Japan in 1995 • Gained popularity in the US after 2001, with the release of Programming Ruby (“the pickaxe”) Friday, April 15, 2011
  6. Why learn Ruby? • NOT (necessarily) to write Ruby code

    • To gain new perspective Friday, April 15, 2011
  7. Why learn Ruby? • NOT (necessarily) to write Ruby code

    • To gain new perspective • Matz focused on making ruby “natural, not simple.” * http://www.ruby-lang.org/en/about/) Friday, April 15, 2011
  8. Consistency Everything’s an object. 5.class # => Fixnum 5.+(5) #

    => 10 Fixnum.ancestors # => [Fixnum, Integer, Numeric, Comparable, Object, Kernel, BasicObject] class MyClass end MyClass.ancestors # => [MyClass, Object, Kernel, BasicObject] MyClass2 = Class.new MyClass2.ancestors # => [MyClass2, Object, Kernel, BasicObject] Friday, April 15, 2011
  9. Consistency • Everything has a value 'well, duh.' if 1

    == 1 # => "well, duh." 'wha-wha-wha-what???' if 1 == 2 # => nil Friday, April 15, 2011
  10. Consistency • Everything has a value 'well, duh.' if 1

    == 1 # => "well, duh." 'wha-wha-wha-what???' if 1 == 2 # => nil • Every method returns something Friday, April 15, 2011
  11. Consistency • Everything has a value 'well, duh.' if 1

    == 1 # => "well, duh." 'wha-wha-wha-what???' if 1 == 2 # => nil • Every method returns something • Methods return the value of the last statement they execute Friday, April 15, 2011
  12. Consistency For instance: class WhatIs def self.this?(obj) "Good sir, it

    appears you have sent me " + case obj when Array "an array." when String "a string." when Numeric "a number." else "something I don't recognize." end end end WhatIs.this?([1,2,3]) # => "Good sir, it appears you have sent me an array." WhatIs.this?(1.234) # => "Good sir, it appears you have sent me a number." WhatIs.this?(:key => 'value') # => "Good sir, it appears you have sent me something I don't recognize." Friday, April 15, 2011
  13. Consistency Conversion conventions 5.to_s # => "5" MyClass.to_s # =>

    "MyClass" 1.2.to_s # => "1.2" "1".to_i # => 1 "1.2".to_i # => 1 "not a number".to_i # => 0 "1".to_f # => 1.0 "1.2".to_f # => 1.2 Friday, April 15, 2011
  14. Expressiveness • Blocks (more on these, later) 2.times { puts

    "Ruby is awesome." } # => 2 # >> Ruby is awesome. # >> Ruby is awesome. Friday, April 15, 2011
  15. Expressiveness • Blocks (more on these, later) 2.times { puts

    "Ruby is awesome." } # => 2 # >> Ruby is awesome. # >> Ruby is awesome. • Bang methods str = 'Perl' str.sub!(/.*/, 'Ruby') str # => "Ruby" Friday, April 15, 2011
  16. Expressiveness • Blocks (more on these, later) 2.times { puts

    "Ruby is awesome." } # => 2 # >> Ruby is awesome. # >> Ruby is awesome. • Bang methods str = 'Perl' str.sub!(/.*/, 'Ruby') str # => "Ruby" • Interrogator methods 1.zero? # => false [].empty? # => true Friday, April 15, 2011
  17. Expressiveness • Blocks (more on these, later) 2.times { puts

    "Ruby is awesome." } # => 2 # >> Ruby is awesome. # >> Ruby is awesome. • Bang methods str = 'Perl' str.sub!(/.*/, 'Ruby') str # => "Ruby" • Interrogator methods 1.zero? # => false [].empty? # => true • Trailing conditions status = "awesome" if str == 'Ruby' # => "awesome" Friday, April 15, 2011
  18. Flexibility Open Classes: module MyMethods # A collection of methods.

    def whoami? "I am #{self}." end end class Object # Re-open the Object class include MyMethods end 4.whoami? # => "I am 4." 'a string'.whoami? # => "I am a string." # And, because classes and modules are also objects... Object.whoami? # => "I am Object." MyMethods.whoami? # => "I am MyMethods." Friday, April 15, 2011
  19. Flexibility A more interesting example class Fixnum def divisible_by_4? self

    % 4 == 0 end end 5.divisible_by_4? # => false 16.divisible_by_4? # => true Friday, April 15, 2011
  20. Flexibility Parentheses (almost always) optional class Spacey def my_method first_arg,

    second_arg, *more_args return first_arg, second_arg, more_args.join(', ') end end kevin = Spacey.new kevin.my_method 'this', 'is', 'my', 'list', 'of', 'arguments' # => ["this", "is", "my, list, of, arguments"] kevin.my_method 'this', 'list', 'needs', 'parens' + ["!"] # ~> -:27:in `+': can't convert Array into String (TypeError) # Oops! Let's try with parens... kevin.my_method('this', 'list', 'needs', 'parens') + ["!"] # => ["this", "list", "needs, parens", "!"] Friday, April 15, 2011
  21. Flexibility method_missing A method of last resort. class Parrot def

    method_missing(method_name, *args) "Squawk! #{method_name}: #{args.join(', ')}" end end petey = Parrot.new petey.repeat_after_me 1, 2, 3 # => "Squawk! repeat_after_me: 1, 2, 3" Friday, April 15, 2011
  22. Flexibility • method_missing is awesome • Helps us be lazy

    • Example class Fixnum def method_missing(method_id, *args, &block) if method_id.to_s =~ /^divisible_by_(\d+)\?$/ self % $1.to_i == 0 else super end end end 12.divisible_by_2? # => true 42.divisible_by_13? # => false Friday, April 15, 2011
  23. Flexibility Bundle data with code lines = DATA.read.split("\n") lines.join('--') #

    => "This is a line of data.--As is this line.--And this one, too!" __END__ This is a line of data. As is this line. And this one, too! Friday, April 15, 2011
  24. Flexibility • Duck typing • “If it looks like a

    duck, and it quacks like a duck...” Friday, April 15, 2011
  25. Flexibility • Duck typing • “If it looks like a

    duck, and it quacks like a duck...” • Allows us to care less about an object’s type, and more about its behavior Friday, April 15, 2011
  26. Flexibility • Duck typing • “If it looks like a

    duck, and it quacks like a duck...” • Allows us to care less about an object’s type, and more about its behavior • Design by contract Friday, April 15, 2011
  27. Enumerable • “Mixin” - Part of Ruby Core • Can

    be included into your classes Friday, April 15, 2011
  28. Enumerable • “Mixin” - Part of Ruby Core • Can

    be included into your classes • Only one requirement Friday, April 15, 2011
  29. Enumerable • “Mixin” - Part of Ruby Core • Can

    be included into your classes • Only one requirement • Class implements #each Friday, April 15, 2011
  30. Enumerable class DataStore include Enumerable attr_reader :data def initialize(*vals) @data

    = vals end def each @data.each do |ele| yield ele end end end ds = DataStore.new(5, 3, 2, 4, 1, 6) ds.all? {|d| Fixnum === d} # => true ds.sort # => [1, 2, 3, 4, 5, 6] Friday, April 15, 2011
  31. “Named” parameters • Ruby lacks first-class support for named parameters.

    • OR DOES IT?! puts(name: "Ernie", quest: "To seek the grail.", favorite_color: "blue") # => {:name=>"Ernie", :quest=>"To seek the grail.", :favorite_color=>"blue"} Friday, April 15, 2011
  32. “Named” parameters • Ruby lacks first-class support for named parameters.

    • OR DOES IT?! puts(name: "Ernie", quest: "To seek the grail.", favorite_color: "blue") # => {:name=>"Ernie", :quest=>"To seek the grail.", :favorite_color=>"blue"} • Yes, it does. :( Friday, April 15, 2011
  33. “Named” parameters • Ruby lacks first-class support for named parameters.

    • OR DOES IT?! puts(name: "Ernie", quest: "To seek the grail.", favorite_color: "blue") # => {:name=>"Ernie", :quest=>"To seek the grail.", :favorite_color=>"blue"} • Yes, it does. :( • But hashes work just fine. :) Friday, April 15, 2011
  34. Blocks • How would you count to 3? • This

    works for i in 1..3 puts i end Friday, April 15, 2011
  35. Blocks • How would you count to 3? • This

    works for i in 1..3 puts i end • So does this i = 0 while i < 3 i += 1 puts i end Friday, April 15, 2011
  36. Blocks • Rubyists don’t write code like that • A

    more idiomatic approach in Ruby: (1..3).each do |number| puts number end Friday, April 15, 2011
  37. Blocks • Rubyists don’t write code like that • A

    more idiomatic approach in Ruby: (1..3).each do |number| puts number end • Obvious intent, even to those not fluent in Ruby Friday, April 15, 2011
  38. Blocks • Rubyists don’t write code like that • A

    more idiomatic approach in Ruby: (1..3).each do |number| puts number end • Obvious intent, even to those not fluent in Ruby • |number| is supplied back to the caller’s closure via the yield keyword Friday, April 15, 2011
  39. Blocks Yield Example: class Blocky def self.step(first, increment, last) var

    = first while var <= last yield var var += increment end end end Blocky.step(0, 10, 100) do |num| puts num end Friday, April 15, 2011
  40. Blocks Yield Example: class Blocky def self.step(first, increment, last) var

    = first while var <= last yield var var += increment end end end Blocky.step(0, 10, 100) do |num| puts num end Output: 0 10 20 30 40 50 60 70 80 90 100 Friday, April 15, 2011
  41. Blocks • Not limited to a single value • Common

    pattern: num = 0 total = 0 while num < 10 num += 1 total += i end total # => 55 Friday, April 15, 2011
  42. Blocks • Not limited to a single value • Common

    pattern: num = 0 total = 0 while num < 10 num += 1 total += i end total # => 55 • Enumerable#inject (1..10).inject(0) {|memo, num| memo + num} # => 55 Friday, April 15, 2011
  43. Blocks • Convert block to object class Blocky def self.pass(&block)

    puts block.class block.call end end Blocky.pass do puts "In block" end # >> Proc # >> In block Friday, April 15, 2011
  44. Blocks • Convert block to object class Blocky def self.pass(&block)

    puts block.class block.call end end Blocky.pass do puts "In block" end # >> Proc # >> In block • & is a special argument prefix Blocky.pass 'hello' # wrong number of arguments (1 for 0) Friday, April 15, 2011
  45. Blocks • & == “I’m a block” • Except, what

    if I’m not? Friday, April 15, 2011
  46. Blocks • & == “I’m a block” • Except, what

    if I’m not? • Symbol#to_proc (1..3).map {|n| n.to_s} # => ["1", "2", "3"] (1..3).map(&:to_s) # => ["1", "2", "3"] Friday, April 15, 2011
  47. Blocks • & == “I’m a block” • Except, what

    if I’m not? • Symbol#to_proc (1..3).map {|n| n.to_s} # => ["1", "2", "3"] (1..3).map(&:to_s) # => ["1", "2", "3"] • Creates a proc with a missing receiver stringifier = :to_s.to_proc stringifier.call `to_proc': no receiver given (ArgumentError) stringifier.call(3.141592654) # => "3.141592654" 3.141592654.to_s == stringifier.call(3.141592654) # => true Friday, April 15, 2011
  48. Blocks • Remember Enumerable#inject? (1..10).inject {|memo, num| memo + num}

    # => 55 • Consider: adder = :+.to_proc adder.call(1, 1) # => 2 Friday, April 15, 2011
  49. Blocks • Remember Enumerable#inject? (1..10).inject {|memo, num| memo + num}

    # => 55 • Consider: adder = :+.to_proc adder.call(1, 1) # => 2 • How can we rewrite the inject? Friday, April 15, 2011
  50. Blocks • Remember Enumerable#inject? (1..10).inject {|memo, num| memo + num}

    # => 55 • Consider: adder = :+.to_proc adder.call(1, 1) # => 2 • How can we rewrite the inject? • Answer! (1..10).inject(:+) # => 55 Friday, April 15, 2011
  51. Blocks def pay_for raise ArgumentError, "A block is required" unless

    block_given? if self.valid? begin transaction do yield self.purchase or raise ActiveMerchant::ActiveMerchantError end rescue ActiveMerchant::ActiveMerchantError false rescue Exception => e # Something else happened, let's store it in trans self.success = false self.authorization = nil self.message = e.message self.params = {:exception_handled => true} false ensure self.save unless self.success? unless self.params.is_a?(Hash) and self.params.has_key?(:exception_handled) self.errors.add_to_base("We were unable to process your payment. " + "Please check your information and try again.") end end end else false end end Friday, April 15, 2011
  52. Metaprogramming • We often write code to avoid doing repetitive

    tasks • What if the act of writing the code itself becomes repetitive? Friday, April 15, 2011
  53. Metaprogramming • We often write code to avoid doing repetitive

    tasks • What if the act of writing the code itself becomes repetitive? • Simple: write code to write your code! Friday, April 15, 2011
  54. Metaprogramming • We often write code to avoid doing repetitive

    tasks • What if the act of writing the code itself becomes repetitive? • Simple: write code to write your code! • Ruby excels at metaprogramming tasks Friday, April 15, 2011
  55. Metaprogramming • Why is an understanding of metaprogramming techniques important?

    • We’ll be talking about Rails Friday, April 15, 2011
  56. Metaprogramming • Why is an understanding of metaprogramming techniques important?

    • We’ll be talking about Rails • Rails feels like “magic” Friday, April 15, 2011
  57. Metaprogramming • Why is an understanding of metaprogramming techniques important?

    • We’ll be talking about Rails • Rails feels like “magic” • Understanding metaprogramming... Friday, April 15, 2011
  58. Metaprogramming • Why is an understanding of metaprogramming techniques important?

    • We’ll be talking about Rails • Rails feels like “magic” • Understanding metaprogramming... • ...pulls back the curtain on the “magic” Friday, April 15, 2011
  59. Metaprogramming • Why is an understanding of metaprogramming techniques important?

    • We’ll be talking about Rails • Rails feels like “magic” • Understanding metaprogramming... • ...pulls back the curtain on the “magic” • ...lets us make “magic” of our own Friday, April 15, 2011
  60. Metaprogramming Reflection Object#class Object#instance_variables Object#methods Object#private_methods Object#public_methods Object#singleton_methods Module#class_variables Module#constants

    Module#included_modules Module#instance_methods Module#name Module#instance_methods Module#private_instance_methods Module#protected_instance_methods Module#public_instance_methods Variables Object#instance_variable_get Object#instance_variable_set Object#remove_instance_variable Module#class_variable_get Module#class_variable_set Module#remove_class_variable Constants Module#const_get Module#const_set Module#remove_const Methods/Execution Object#send Object#instance_eval Module#define_method Module#remove_method Module#undef_method Module#module_eval Module#class_eval Kernel#eval Kernel#method_missing Friday, April 15, 2011
  61. Metaprogramming • Macros • DSLs • lazy method definition (lazy-loading)

    • lazy method definition (lazy programmer) Friday, April 15, 2011
  62. Metaprogramming • Macros • DSLs • lazy method definition (lazy-loading)

    • lazy method definition (lazy programmer) • Much, much more. Friday, April 15, 2011
  63. Macros class ExternalBook < Attachment PURCHASE_URLS = { :amazon_purchase_url =>

    'Amazon', :b_n_purchase_url => 'Barnes & Noble', :indiebound_purchase_url => 'Indiebound' }.freeze attr_bucket :body => PURCHASE_URLS.keys + [:description] end attr_bucket Friday, April 15, 2011
  64. Macros def attr_bucket(opts = {}) unless include? InstanceMethods include InstanceMethods

    class_attribute :_attr_bucketed_attributes self._attr_bucketed_attributes = [] class_attribute :_attr_bucket_methods # We define our methods on this module so we can override and super self._attr_bucket_methods = Module.new include self._attr_bucket_methods end return nil unless table_exists? opts.map do |bucket_name, attrs| bucket_column = self.columns_hash[bucket_name.to_s] unless bucket_column.type == :text raise ArgumentError, "#{bucket_name} is of type #{bucket_column.type}, not text" end serialize bucket_name, Hash if attrs.is_a?(Hash) attrs.map do|attr_name, attr_type| _define_bucket bucket_name, attr_name, attr_type, bucket_column.class end else Array.wrap(attrs).each do |attr_name| _define_bucket bucket_name, attr_name, :string, bucket_column.class end end end end alias :i_has_a_bucket :attr_bucket Friday, April 15, 2011
  65. DSLs Article.joins{person.comments}.where{person.comments.body =~ '%hello%'}.to_sql # => SELECT "articles".* FROM "articles"

    INNER JOIN "people" ON "people"."id" = "articles"."person_id" INNER JOIN "comments" ON "comments"."person_id" = "people"."id" WHERE "comments"."body" LIKE '%hello%' Person.where{(salary - 40000) < 0}.to_sql # => SELECT "people".* FROM "people" WHERE "people"."salary" - 40000 < 0 Squeel Friday, April 15, 2011
  66. DSLs module MetaWhere class DSL MetaWhere.evil_things do (instance_methods + private_instance_methods).each

    do |method| unless method.to_s =~ /^(__|instance_eval)/ undef_method method end end end def self.evaluate(&block) if block.arity > 0 yield self.new else self.new.instance_eval(&block) end end def method_missing(method_id, *args) if args.empty? Nodes::Stub.new method_id elsif (args.size == 1) && (Class === args[0]) Nodes::Join.new(method_id, Arel::InnerJoin, args[0]) else Nodes::Function.new method_id, args end end end end Friday, April 15, 2011
  67. Lazy Method Definition • method_missing has overhead • So does

    having 20,000 methods in a class Friday, April 15, 2011
  68. Lazy Method Definition • method_missing has overhead • So does

    having 20,000 methods in a class • Define methods on an as-needed basis Friday, April 15, 2011
  69. Lazy Method Definition class Meta def method_missing(method_id, *args, &block) puts

    "In method_missing" if method_id.to_s =~ /^find_by_(\w+)$/ self.class.class_eval <<-END def #{method_id}(*args) puts "I'm gonna find me some records by #{$1}" end END send(method_id, *args) else super end end end finder = Meta.new finder.methods.grep(/find_by/) # => [] finder.find_by_first_name # >> In method_missing # >> I'm gonna find me some records by first_name finder.methods.grep(/find_by/) # => ["find_by_first_name"] finder.find_by_first_name # >> I'm gonna find me some records by first_name Friday, April 15, 2011
  70. Lazy Method Definition • Sometimes we need lots of similar

    methods • Could copy/paste/edit Friday, April 15, 2011
  71. Lazy Method Definition • Sometimes we need lots of similar

    methods • Could copy/paste/edit • Repetitive work SUCKS Friday, April 15, 2011
  72. Lazy Method Definition • Sometimes we need lots of similar

    methods • Could copy/paste/edit • Repetitive work SUCKS • Write code to write the methods Friday, April 15, 2011
  73. Lazy Method Definition class Person %w(name address city state zip).each

    do |attribute| attr_writer attribute.to_sym define_method attribute do instance_variable_get("@#{attribute}") || '<not given>' end end end me = Person.new me.name = 'Ernie Miller' me.city = 'Louisville' me.state = 'KY' me.name # => "Ernie Miller" me.address # => "<not given>" Friday, April 15, 2011
  74. Recap • Consistency • Everything’s an object • Everything has

    a value • Conventions Friday, April 15, 2011
  75. Recap • Flexibility • Open classes • Forgiving parser (parens

    optional) • Duck typing Friday, April 15, 2011
  76. Recap • Flexibility • Open classes • Forgiving parser (parens

    optional) • Duck typing • Metaprogramming Friday, April 15, 2011
  77. Ruby on Rails • An MVC web application framework written

    in Ruby • Created by David Heinemeier Hansson Friday, April 15, 2011
  78. Ruby on Rails • An MVC web application framework written

    in Ruby • Created by David Heinemeier Hansson • v1.0 released in 2005 Friday, April 15, 2011
  79. Ruby on Rails • An MVC web application framework written

    in Ruby • Created by David Heinemeier Hansson • v1.0 released in 2005 • An extraction from 37signals’ Basecamp Friday, April 15, 2011
  80. Ruby on Rails • An MVC web application framework written

    in Ruby • Created by David Heinemeier Hansson • v1.0 released in 2005 • An extraction from 37signals’ Basecamp • OPINION: Rails’ success was possible because of Ruby, not the other way around Friday, April 15, 2011
  81. Rails Philosophy • Optimized for programmer happiness • You shouldn’t

    be writing boilerplate code Friday, April 15, 2011
  82. Rails Philosophy • Optimized for programmer happiness • You shouldn’t

    be writing boilerplate code • “My IDE will do it” is not sufficient Friday, April 15, 2011
  83. Rails Philosophy • Optimized for programmer happiness • You shouldn’t

    be writing boilerplate code • “My IDE will do it” is not sufficient • Convention over configuration Friday, April 15, 2011
  84. Rails Philosophy • Optimized for programmer happiness • You shouldn’t

    be writing boilerplate code • “My IDE will do it” is not sufficient • Convention over configuration • Sane defaults should be the norm Friday, April 15, 2011
  85. Rails Philosophy • Optimized for programmer happiness • You shouldn’t

    be writing boilerplate code • “My IDE will do it” is not sufficient • Convention over configuration • Sane defaults should be the norm • Change only what you need to Friday, April 15, 2011
  86. Rails Philosophy • Optimized for programmer happiness • You shouldn’t

    be writing boilerplate code • “My IDE will do it” is not sufficient • Convention over configuration • Sane defaults should be the norm • Change only what you need to • DRY = Don’t Repeat Yourself Friday, April 15, 2011
  87. Rails Components • ActiveSupport • ActionPack • ActionMailer • ActiveModel

    • ActiveRecord • ActiveResource Friday, April 15, 2011
  88. ActiveSupport • The “utility” libraries of Rails • Logging •

    Inflector • JSON/XML Friday, April 15, 2011
  89. ActiveSupport • The “utility” libraries of Rails • Logging •

    Inflector • JSON/XML • Caching Friday, April 15, 2011
  90. ActiveSupport • The “utility” libraries of Rails • Logging •

    Inflector • JSON/XML • Caching • Core extensions Friday, April 15, 2011
  91. ActionPack • ActionDispatch - Routing/request handling • ActionController - It

    puts the “C” in MVC • ActionView - Helpers/rendering Friday, April 15, 2011
  92. ActionMailer • A “controller” for sending e-mail • Handles HTML,

    plaintext, attachments Friday, April 15, 2011
  93. ActiveModel • Useful mixins for model classes • Validations •

    Naming • Serialization Friday, April 15, 2011
  94. ActiveRecord • Ruby implementation of the Active Record Pattern •

    Default ORM in Rails Friday, April 15, 2011
  95. ActiveRecord • Ruby implementation of the Active Record Pattern •

    Default ORM in Rails • Dogfoods ActiveModel Friday, April 15, 2011
  96. ActiveRecord • Ruby implementation of the Active Record Pattern •

    Default ORM in Rails • Dogfoods ActiveModel • Uses ARel for SQL generation Friday, April 15, 2011
  97. ActiveRecord • Ruby implementation of the Active Record Pattern •

    Default ORM in Rails • Dogfoods ActiveModel • Uses ARel for SQL generation • Compatible with many DBs: SQLite, MySQL, PostgreSQL, Oracle, MS SQL Server Friday, April 15, 2011
  98. ActiveResource • Does for RESTful web services what ActiveRecord does

    for DBs • Built on ActiveModel as well Friday, April 15, 2011
  99. ActiveResource • Does for RESTful web services what ActiveRecord does

    for DBs • Built on ActiveModel as well • Validations Friday, April 15, 2011
  100. ActiveResource • Does for RESTful web services what ActiveRecord does

    for DBs • Built on ActiveModel as well • Validations • Serialization Friday, April 15, 2011
  101. Let’s get started! • Stuff we’ll need: • Ruby •

    Git (optionally) Friday, April 15, 2011
  102. Let’s get started! • Stuff we’ll need: • Ruby •

    Git (optionally) • Rails Friday, April 15, 2011
  103. Let’s get started! • Stuff we’ll need: • Ruby •

    Git (optionally) • Rails • Our favorite text editor Friday, April 15, 2011
  104. Git • Optional, but recommended if you intend to continue

    developing with Ruby/Rails Friday, April 15, 2011
  105. Git • Optional, but recommended if you intend to continue

    developing with Ruby/Rails • GitHub is central to the Ruby/Rails ecosystem Friday, April 15, 2011
  106. Git • Optional, but recommended if you intend to continue

    developing with Ruby/Rails • GitHub is central to the Ruby/Rails ecosystem • msysgit Friday, April 15, 2011
  107. Git • Optional, but recommended if you intend to continue

    developing with Ruby/Rails • GitHub is central to the Ruby/Rails ecosystem • msysgit • http://code.google.com/p/msysgit/ Friday, April 15, 2011
  108. Git • Optional, but recommended if you intend to continue

    developing with Ruby/Rails • GitHub is central to the Ruby/Rails ecosystem • msysgit • http://code.google.com/p/msysgit/ • Download latest 1.7.4 Friday, April 15, 2011
  109. Our App • A blog • Yes, it’s cliché, but...

    • Well-understood problem space Friday, April 15, 2011
  110. Our App • A blog • Yes, it’s cliché, but...

    • Well-understood problem space • Lets us exercise a number of Rails fundamentals Friday, April 15, 2011
  111. Creating the App • rails new blog -TJ • Skipping

    Test::Unit Friday, April 15, 2011
  112. Creating the App • rails new blog -TJ • Skipping

    Test::Unit • Skipping Prototype JS library Friday, April 15, 2011