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

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

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.

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