== 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
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
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
• 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
=> "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
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
• 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
• 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
• 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
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
= 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
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
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
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
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
• We’ll be talking about Rails • Rails feels like “magic” • Understanding metaprogramming... • ...pulls back the curtain on the “magic” Friday, April 15, 2011
• 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
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
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
"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
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
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
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
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
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
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