SlideShare a Scribd company logo
1 of 141
Download to read offline
Designing Beautiful
    Ruby APIs V.2
          ihower@gmail.com
           2010/4/25 & 6/26
    RubyConf Taiwan & RubyConf China
About Me
ā€¢              a.k.a. ihower
    ā€¢   http://ihower.tw

    ā€¢   http://twitter.com/ihower

ā€¢ Rails Developer since 2006
ā€¢ The Organizer of Ruby Taiwan Community
 ā€¢ http://ruby.tw
 ā€¢ http://rubyconf.tw
Ruby Taiwan
RubyConf Taiwan 2010
Agenda
                    10 techniques and a sub-talk
ā€¢   Arguments processing
ā€¢   Code blocks
ā€¢   Module
ā€¢   method_missing?
ā€¢   const_missing
ā€¢   Methods chaining
ā€¢   Core extension
ā€¢   (sub-talk) Ruby Object Model and Meta-programming
ā€¢   Class macro
ā€¢   instance_eval
ā€¢   Class.new
Deļ¬ne ā€œbeautifulā€

ā€¢ Readable: easy to understand
ā€¢ Efļ¬cient: easy to write
ā€¢ Flexible: easy to extend
1.Argument Processing
Pseudo-Keyword
   Arguments
def blah(options)
  puts options[:foo]
  puts options[:bar]
end

blah(:foo => "test", :bar => "test")
Treating Arguments as
       an Array
       def sum(*args)
           puts args[0]
           puts args[1]
           puts args[2]
           puts args[3]
       end

       sum(1,2,3)
       # 1
       # 2
       # 3
       # nil
Rails helper usage
             example
# USAGE-1 without block
<% link_to 'Posts list', posts_path, :class => 'posts'
%>

# USAGE-2 with block
<% link_to posts_path, :class => 'posts' do %>
  <p>Posts list</p>
<% end %>
# Rails3's source code
def link_to(*args, &block)
  if block_given?
    options       = args.first || {}
    html_options = args.second
    link_to(capture(&block), options, html_options)
  else
    name          = args[0]
    options       = args[1] || {}
    html_options = args[2]

    html_options = convert_options_to_data_attributes(options, html_options)
    url = url_for(options)

    if html_options
      html_options = html_options.stringify_keys
      href = html_options['href']
      tag_options = tag_options(html_options)
    else
      tag_options = nil
    end

    href_attr = "href="#{url}"" unless href
    "<a #{href_attr}#{tag_options}>#{ERB::Util.h(name || url)}</a>".html_safe
  end
end
ActiveSupport#extract_options!
            extract hash from *args


      def foobar(*args)
        options = args.extract_options!

      end

      foobar(1, 2)
      # options is {}

      foobar(1, 2, :a => :b)
      # options is { :a => :b }
2.Code Blocks
A trivial example 1
     def call_block
       puts "start"
       yield(" foobar")
       puts "end"
     end

     call_block do |str|
       puts " here"
       puts str
       puts " here"
     end

     # start
     # here
     #   foobar
     # here
     # end
A trivial example 2
    def call_block(&block)
      puts "start"
      block.call("foobar")
      puts "end"
    end

    call_block do |str|
      puts "here"
      puts str
      puts "here"
    end

    #   start
    #   here
    #   foobar
    #   here
    #   end
pre- and Post-processing
     usage example
   f = File.open("myfile.txt", 'w')
   f.write("Lorem ipsum dolor sit amet")
   f.write("Lorem ipsum dolor sit amet")
   f.close


   # using block
   File.open("myfile.txt", 'w') do |f|
     f.write("Lorem ipsum dolor sit amet")
     f.write("Lorem ipsum dolor sit amet")
   end
pre- and Post-
       processing
# without code block
def send_message(msg)
  socket = TCPSocket.new(@ip, @port) # Pre-
  socket.puts(msg)
  response = socket.gets
  ensure
    socket.close # Post-
  end
end
# with code block
def send_message(msg)
  connection do |socket|
    socket.puts("foobar")
    socket.gets
  end
end

def connection
  socket = TCPSocket.new(@ip, @port) # Pre-
    yield(socket)
    ensure
      socket.close # Post-
    end
end
Dynamic Callbacks
     Sinatra usage example

   get '/posts' do
     #.. show something ..
   end

   post '/posts' do
     #.. create something ..
   end

   put '/posts/:id' do
     #.. update something ..
   end

   delete '/posts/:id' do
     #.. annihilate something ..
   end
Dynamic Callbacks
server = Server.new

server.handle(/hello/) do
  puts "Hello at #{Time.now}"
end

server.handle(/goodbye/) do
  puts "goodbye at #{Time.now}"
end

server.execute("/hello")
# Hello at Wed Apr 21 17:33:31 +0800 2010
server.execute("/goodbye")
# goodbye at Wed Apr 21 17:33:42 +0800 2010
class Server

  def initialize
    @handlers = {}
  end

  def handle(pattern, &block)
    @handlers[pattern] = block
  end

  def execute(url)
    @handlers.each do |pattern, block|
      if match = url.match(pattern)
        block.call
        break
      end
    end
  end

end
Self Yield
      gemspec example

spec = Gem::Specification.new
spec.name = "foobar"
spec.version = "1.1.1"



# using block
Gem::Specification.new do |s|
  s.name        = "foobar"
  s.version     = "1.1.1"
  #...
end
Self Yield
            gemspec example


class Gem::Specification
    def initialize name = nil, version = nil
        # ...
        yield self if block_given?
        # ...
    end
end
3.Module
A trivial example
    module Mixin
        def foo
            puts "foo"
        end
    end

    class A
        include Mixin
    end

    A.new.foo # foo
obj.extend(Mod)
       Implementing class behavior


module Mixin
    def foo
        puts "foo"                   class A
    end                                extend Mixin
end                                  end

                       the same as
A.extend(Mixin)                      class << A
                                       include Mixin
A.foo # class method                 end
obj.extend(Mod)
 Implementing per-object behavior

  module Mixin
      def foo
          puts "foo"
      end
  end

  a = "test"                      class << a
                    the same as
  a.extend(Mixin)                   include Mixin
  a.foo # foo                     end

  b = "demo"
  b.foo # NoMethodError
Dual interface
module Logger
    extend self

      def log(message)
          $stdout.puts "#{message} at #{Time.now}"
      end
end

Logger.log("test") # as Loggerā€™s class method

class MyClass
    include Logger
end
MyClass.new.log("test") # as MyClassā€™s instance method
Mixin with class methods
      module Mixin
        def foo
          puts "foo"
        end

        module ClassMethods
          def bar
            puts "bar"
          end
        end
      end

      class MyClass
        include Mixin
        extend Mixin::ClassMethods
      end
module Mixin

  # self.included is a hook method
  def self.included(base)
    base.extend(ClassMethods)
  end

  def foo
    puts "foo"
  end

  module ClassMethods
    def bar
      puts "bar"
    end
  end
end

class MyClass
  include Mixin
end
module Mixin

  def self.included(base)
    base.extend(ClassMethods)
    base.send(:include, InstanceMethods)
  end

  module InstanceMethods
    def foo
      puts "foo"
    end
  end

  module ClassMethods
    def bar
      puts "bar"
    end
  end
end

class MyClass
  include Mixin
end
4.method_missing?
ActiveRecord example

 class Person < ActiveRecord::Base
 end

 p1 = Person.find_by_name("ihower")
 p2 = Person.find_by_email("ihower@gmail.com")
A trivial example
    car = Car.new

    car.go_to_taipei
    # go to taipei

    car.go_to_shanghai
    # go to shanghai

    car.go_to_japan
    # go to japan
class Car
    def go(place)
        puts "go to #{place}"
    end

      def method_missing(name, *args)
          if name.to_s =~ /^go_to_(.*)/
               go($1)
          else
               super
          end
      end
end

car = Car.new

car.go_to_taipei
# go to taipei

car.blah # NoMethodError: undefined method `blah`
Donā€™t abuse method
        missing
ā€¢ method_missing? is slow
ā€¢ only use it when you can not deļ¬ne
  method in advance
 ā€¢ Meta-programming can deļ¬ne methods
    dynamically
XML builder example
builder = Builder::XmlMarkup.new(:target=>STDOUT, :indent=>2)
builder.person do |b|
  b.name("Jim")
  b.phone("555-1234")
  b.address("Taipei, Taiwan")
end

# <person>
#   <name>Jim</name>
#   <phone>555-1234</phone>
#   <address>Taipei, Taiwan</address>
# </person>
We need BlankSlate or
  BasicObject to avoid name conļ¬‚ict
# Jim Weirich's BlankSlate from XML builder
>> BlankSlate.instance_methods
=> ["__send__", "instance_eval", "__id__"]

# Ruby 1.9
>> BasicObject.instance_methods
=> [:==, :equal?, :!, :!
=, :instance_eval, :instance_exec, :__send__]

# an easy BlankSlate
class BlankSlate
  instance_methods.each { |m| undef_method m unless m =~ /^__/ }
end

>> BlankSlate.instance_methods
=> ["__send__", "__id__"]
BlankSlate usage example
class Proxy < BlankSlate

  def initialize(obj)
    @obj = obj
  end

  def method_missing(sym, *args, &block)
    puts "Sending #{sym}(#{args.join(',')}) to obj"
    @obj.__send__(sym, *args, &block)
  end
end

s = Proxy.new("foo")
puts s.reverse
# Sending reverse() to obj
# "oof"
5.const_missing
ActiveSupport::Dependencies
 example (lazy class loading)

ā€¢   Person

ā€¢   Ruby calls const_missing

ā€¢   const_missing calls
    Dependencies.load_missing_constant(Object, :Person)

ā€¢   require or load person.rb in the list of load path
Global constant
class Module
    original_c_m = instance_method(:const_missing)

      define_method(:const_missing) do |name|
          if name.to_s =~ /^U([0-9a-fA-F]{4})$/
               [$1.to_i(16)].pack("U*")
          else
               original_c_m.bind(self).call(name)
          end
      end
end

puts U0123 # ģ
puts U9999 #
Localized constant
    (you can use super here)
  class Color

    def self.const_missing(name)
      if name.to_s =~ /[a-zA-Z]/
        const_set(name, new)
      else
        super
      end
    end

  end

  Color::RED
  #<Color:0x1018078a0>
  Color::GREEN
  #<Color:0x1018059d8>
6.Methods chaining
an Array example
[1,1,2,3,3,4,5].uniq!.reject!{ |i| i%2 == 0 }.reverse

# 5,3,1
a trivial example
     class Demo
       def foo
         puts "foo"
         self
       end

       def bar
         puts "bar"
         self
       end

       def baz
         puts "baz"
         self
       end

     end

     Demo.new.foo.bar.baz
     # foo
     # bar
     # baz
Object#tap
                          Ruby 1.8.7 later




puts "dog".reverse
          .tap{ |o| puts "reversed: #{o}" } #
          .upcase

#
reversed: god
GOD
Object#tap
   Ruby 1.8.7 later



  class Object

    def tap
      yield self
      self
    end

  end
7.Core extension
NilClass#try example
person = Person.find_by_email(params[:email])
# but we don't know @person exists or not

# Without try
@person ? @person.name : nil

# With try
@person.try(:name)
class NilClass
  def try(*args)
    nil
  end
end
Numeric#bytes example

    123.kilobytes # 125952
    456.megabytes # 478150656
    789.gigabytes # 847182299136
class Numeric
  KILOBYTE = 1024
  MEGABYTE = KILOBYTE * 1024
  GIGABYTE = MEGABYTE * 1024

  def bytes
    self
  end
  alias :byte :bytes

  def kilobytes
    self * KILOBYTE
  end
  alias :kilobyte :kilobytes

  def megabytes
    self * MEGABYTE
  end
  alias :megabyte :megabytes

  def gigabytes
    self * GIGABYTE
  end
  alias :gigabyte :gigabytes

end
Object#blank? example
    [1,2,3].blank? # false
    "blah".blank? # false
    "".blank? # true

    class Demo
      def return_nil
      end
    end

    Demo.new.blank? # false
    Demo.new.return_nil.blank? # true
class Object

  def blank?
    respond_to?(:empty?) ? empty? : !self
  end

  def present?
    !blank?
  end

end

class NilClass
  def blank?
    true
  end
end

class FalseClass
  def blank?
    true
  end
end

class TrueClass
  def blank?
    false
  end
end
sub-talk



Ruby Object Model
       and
Meta-programming
self
(current object)
 class Demo
     puts self # Demo

       def blah
           puts self # <Demo:0x10180f398> object

             [1,2,3].each do |i|
                 puts self # <Demo:0x10180f398> object
             end
       end

       class AnotherDemo
           puts self # Demo::AnotherDemo
       end
 end
Everything in Ruby is
 object, even class.
Ruby Object Model
class A                          Object
end
                                    super
class B < A
end
                                   A
obj = B.new
                                    super
obj.class # B
B.superclass # A         class              class
B.class # Class
                   obj             B                Class
class object is an object
      of the class Class
class A                          Object
end
                                    super
class B < A                                         class
end
                                   A
obj = B.new                                     class
                                    super
obj.class # B
B.superclass # A         class              class
B.class # Class
                   obj             B                    Class   class
module
class A
end
                              A
module B
end                            super

module C                     Mixin
end                          B,C

class D < A                    super
  include B          class
  include C    obj            D
end
whatā€™s metaclass?
class A
  def foo                         Object
  end
end                                  super

obj1 = A.new              class
               obj2                 A
obj2 = A.new

                      class

               obj2
metaclass
            also known as singleton, eigenclass, ghost class, virtual class.
class A                every object has his own metaclass
  def foo
  end
end                                                                                        Object

obj1 = A.new                                                                                    super
obj2 = A.new
                                                                      class
def obj2.bar                                    obj2                                           A
  # only obj2 has bar method,
  # called singleton method
                                                                                            super
end
                                                           class      obj2ā€™s
                                                obj2                 metaclass
# another way
class << obj1
  def baz
    #only obj1 has baz method
  end
end
                                                            P.S. well, number and symbol have no metaclass
class object has his metaclass too.
class A     so the singleton method is class
  # way1
  def self.foo          method!!
  end

  # way2
  class << self
    def bar                      class
                     B
    end                                      Class
  end

end                                          super
                         class     Aā€™s
# way3               A           metaclass
def A.baz
end

A.foo
A.bar
A.baz
If we deļ¬ne method
   inside class Class
class Class
  def blah
    puts "all class has blah class method"
  end
end

class A
end

A.blah # all class has blah class method
String.blah # all class has blah class method
method deļ¬nition(current class)
    "def" deļ¬nes instance method for current class



           class Demo
               # the current class is Demo
               def foo
                   puts "foo"
               end
           end

           Demo.new.foo # foo
class << changes the method
   deļ¬nition(current class)
   class Demo
     class << self
       # the current class is Demo's metaclass
       def foo
           puts "foo"
       end
     end

   end

   Demo.foo # foo
class << also
changes the self (current object)

       class Demo

         puts self # Demo

         class << self
           puts self # Demo's metaclass
         end

       end

       "abc".metaclass
We can get metaclass by
    using class<<
    class Object
      def metaclass
        class << self; self; end end
      end
    end

    "abc".metaclass
    String.metaclass

    # Ruby 1.9.2

    "abc".singleton_class
    => #<Class:#<String:0x000001009e26f8>>
method deļ¬nition
  mechanism    self (current object)                      new scope?
                                        (current_class)




 class Foo            Foo                   Foo             yes



class << Foo      Fooā€™s                  Fooā€™s
                                                            yes
                 metaclass              metaclass
Meta-programming
 How to write code to write code?
Two types of meta-
     programming
ā€¢ Code Generation (Not talk about it today)
 ā€¢ eg. Rails scaffold
ā€¢ Reļ¬‚ection
 ā€¢ eg. Class Macro (talk later)
Speciļ¬cally, How to write a
method to deļ¬ne method?
class Demo
  # the current class is Demo

  def define_blah1
    # the current class is Demo
    def blah1
      puts "blah1"
    end
  end

  def self.define_blah2
    # the current class is Demo (the same as above)
    def blah2
      puts "blah2"
    end
  end

end

Demo.new.blah1 # NoMethodError: undefined method `blah1'
Demo.new.define_blah1
Demo.new.blah1 # blah1

Demo.new.blah2 # NoMethodError: undefined method `blah2'
Demo.define_blah2
Demo.new.blah2 #blah2
And how to write a method
to deļ¬ne singleton method?
class Demo

  def define_blah1
    # self is Demo's instance
    def self.blah1
      puts "blah1" # define singleton method
    end
  end

  def self.define_blah2
    #self is Demo
    def self.blah2
      puts "blah2" # define singleton method (class method)
    end
  end

end

a = Demo.new
a.define_blah1
a.blah1 # blah1
Demo.new.blah1 # NoMethodError: undefined method `blah1'

Demo.new.blah2 # NoMethodError: undefined method `blah2'
Demo.define_blah2
Demo.blah2 #blah2
Not useful really, we
need more dynamic
      power
The key of power is
  variable scope!!
Variable scope




 {
      module MyDemo
        var = 1




    {
        class Demo
          var = 2



       {
            def foo
              var = 3
            end

        end

      end
block variable scope
var1 = "foo"

[1,2,3].each do |i|
    puts var
    var1 = "bar"
    var2 = "baz"
end

puts var1 # foo
puts var2 # NameError: undefined local variable or method
deļ¬ne_method
unlike ā€œdefā€, you can access outside variable!!
      class Demo
          # define instance methods
          ["aaa", "bbb", "ccc"].each do |name|
              define_method(name) do
                  puts name.upcase
              end
          end

            # define class methods
            class << self
                ["xxx", "yyy", "zzz"].each do |name|
                   define_method(name) do
                     puts name.upcase
                   end
                 end
            end
      end

      Demo.new.aaa # AAA
      Demo.new.bbb # BBB

      Demo.yyy # YYY
      Demo.zzz # ZZZ
deļ¬ne_method will
deļ¬nes instance method
   for current object
   (itā€™s must be class object or module)
class Class
                    def define_more_methods
                      # define instance methods
                      ["aaa", "bbb", "ccc"].each do |name|


   Use class
                          define_method(name) do
                              puts name.upcase
                          end
                      end

  method to             # define class methods
                        class << self


deļ¬ne methods               ["xxx", "yyy", "zzz"].each do |name|
                               define_method(name) do
                                 puts name.upcase


    (global)
                               end
                             end
                        end
                      end
                end

                class Demo
                  define_more_methods
                end
module Mixin
                  module ClassMethods
                    def define_more_methods
                      # define instance methods
                      ["aaa", "bbb", "ccc"].each do |name|
                          define_method(name) do


   Use class
                              puts name.upcase
                          end
                      end



  method to           # define class methods
                      class << self
                          ["xxx", "yyy", "zzz"].each do |name|


deļ¬ne methods
                             define_method(name) do
                               puts name.upcase
                             end


  (localized)
                           end
                      end
                    end
                  end
                end

                class Demo
                  extend Mixin::ClassMethods
                  define_more_methods
                end
So unlike ā€œdefā€,
deļ¬ne_method will not
  create new scope
So we maybe need those methods
     inside deļ¬ne_method:
                (because the scope is not changed)



  ā€¢   Object#instance_variable_get

  ā€¢   Object#instance_variable_set

  ā€¢   Object#remove_instance_variable

  ā€¢   Module#class_variable_get

  ā€¢   Module#class_variable_set

  ā€¢   Module#remove_class_variable
Not dynamic enough?
  Because class <<
will create new scope
         still!!
we need even more
 dynamic power
var = 1
String.class_eval do

  puts var # 1
  puts self # the current object is String

  # the current class is String
  def foo
    puts "foo"
  end

  def self.bar
    puts "bar"
  end
                                       class_eval
                                       (only for class object or module)
  class << self
    def baz
      puts "baz"
    end
  end

end

"abc".foo # foo
String.bar # bar
String.baz # baz
class_eval + deļ¬ne_method
      name = "say"
      var = "itā€™s awesome"

      String.class_eval do

        define_method(name) do
          puts var
        end

      end

      "ihower".say # itā€™s awesome
But how to deļ¬ne
singleton method using
     class_eval and
    deļ¬ne_method?
Wrong!
name = "foo"
var = "bar"

String.class_eval do

  class << self
    define_method(name) do
      puts var
    end
  end

end

# ArgumentError: interning empty string
# we can not get name and var variable, because class << create new scope
Fixed!
   you need ļ¬nd out metaclass and class_eval it!

name = "foo"
var = "bar"

metaclass = (class << String; self; end)
metaclass.class_eval do
    define_method(name) do
        puts var
    end
end

String.foo # bar
How about apply to
   any object?
 (because class_eval only works on class object or module)
instance_eval for any
       object
 obj = "blah"
 obj.instance_eval do
   puts self # obj

   # the current class is obj's metaclass
   def foo
     puts "foo"
   end
 end

 obj.foo # singleton method
how about class object?
  String.instance_eval do
    puts self # String

    # the current class is String's metaclass
    def foo
      puts "bar"
    end

  end
  String.foo # singleton method (class method)
method deļ¬nition
   mechanism        self (current object)                      new scope?
                                             (current_class)



    class Foo               Foo                   Foo             yes



  class << Foo
                      Fooā€™s metaclass        Fooā€™s metaclass      yes



 Foo.class_eval             Foo                   Foo             no



Foo.instance_eval           Foo              Fooā€™s metaclass      no
8. Class Macro
 (Rubyā€™s declarative style)
ActiveRecord example
class User < ActiveRecord::Base

  validates_presence_of     :login
  validates_length_of       :login,   :within => 3..40
  validates_presence_of     :email

  belongs_to :group
  has_many :posts

end
Class Bodies Arenā€™t
         Special
class Demo
  a = 1
  puts a

  def self.say
    puts "blah"
  end

  say # you can execute class method in class body
end

# 1
# blah
Memorize example
class Account

  def calculate
    @calculate ||= begin
      sleep 10 # expensive calculation
      5
    end
  end

end

a = Account.new
a.caculate # need waiting 10s to get 5
a.caculate # 5
a.caculate # 5
a.caculate # 5
memoize method
class Account

  def calculate
      sleep 2 # expensive calculation
      5
  end

  memoize :calculate

end

a = Account.new
a.calculate # need waiting 10s to get 5
a.calculate # 5
class Class
  def memoize(name)
    original_method = "_original_#{name}"
    alias_method :"#{original_method}", name

    define_method name do
      cache = instance_variable_get("@#{name}")
      if cache
        return cache
      else
        result = send(original_method) # Dynamic Dispatches
        instance_variable_set("@#{name}", result)
        return result
      end
    end

  end
end
Itā€™s general for any class
    class Car

      def run
          sleep 100 # expensive calculation
          ā€œdoneā€
      end

      memoize :run

    end

    c = Car.new
    c.run # need waiting 100s to get done
    c.run # done
BTW, how to keep original
 method and call it later?
ā€¢ alias_method
 ā€¢ most common way
 ā€¢ example above
ā€¢ method binding
 ā€¢ can avoid to add new method
 ā€¢ example in const_missing
9.instance_eval
DSL calls it create implicit context
Rack example

 Rack::Builder.new do
   use Some::Middleware, param
   use Some::Other::Middleware
   run Application
 end
How is instance_eval
       doing?
ā€¢ It changes the ā€œselfā€ (current object) to
  caller
ā€¢ Any object can call instance_eval (unlike
  class_eval)
a trivial example
    class Demo
      def initialize
        @a = 99
      end
    end

    foo = Demo.new

    foo.instance_eval do
      puts self # foo instance
      puts @a # 99
    end
instance_eval with block
      class Foo
        attr_accessor :a,:b

        def initialize(&block)
          instance_eval &block
        end

        def use(name)
          # do some setup
        end
      end

      bar = Foo.new do
        self.a = 1
        self.b = 2
        use "blah"
        use "blahblah"
      end
Strings of Code
     eval*() family can accept string of code




ā€¢ No editorā€™s syntax highlight
ā€¢ Not report syntax error until be evaluated
  (in runtime!)
ā€¢ Security problem
10.Class.new
anonymous class
  klass = Class.new
    def move_left
      puts "left"
    end

    def move_right
      puts "right"
    end
  end

  object = klass.new
  object.move_left # left

  Car = klass # naming it Car
  car = Car.new
  car.move_right # right
variable scope matters
           you can access outside variable

var = "itā€™s awesome"
klass = Class.new

      puts var

      def my_method
          puts var
          # undefined local variable or method `var'
      end
end

puts klass.new.my_method
var = "itā€™s awesome"
klass = Class.new do

      puts var

      define_method :my_method do
          puts var
      end
end

puts klass.new.my_method
# itā€™s awesome
# itā€™s awesome
Subclassing with a
generator using Class.new
   def disable_string_class(method_name)
     Class.new(String) do
       undef_method method_name
     end
   end

   klass1 = disable_string_class(:reverse)

   a = klass1.new("foobar")
   a.reverse # NoMethodError

   klass2 = disable_string_class(:upcase)
   b = klass2.new("foobar")
   b.upcase # NoMethodError
Parameterized subclassing
    Camping example
      module Camping::Controllers
        class Edit < R '/edit/(d+)'
          def get(id)
            # ...
          end
        end
      end
Parameterized
subclassing example
     def Person(name)
       if name == "ihower"
         Class.new do
            def message
              puts "good"
            end
         end
       else
         Class.new do
            def message
              puts "bad"
            end
         end
       end
     end
class Foo < Person 'ihower'
  def name
    "foo"
  end
end

class Bar < Person 'not_ihower'
  def name
    "bar"
  end
end

f = Foo.new
f.message # good!

b = Bar.new
b.message # bad!
Conclusion
Story 1:
      DSL or NoDSL by JosƩ Valim
            at Euruko 2010
http://blog.plataformatec.com.br/2010/06/dsl-or-nodsl-at-euruko-2010/
a DSL
class ContactForm < MailForm::Base
  to "jose.valim@plataformatec.com.br"
  from "contact_form@app_name.com"
  subject "Contact form"

  attributes :name, :email, :message
end

ContactForm.new(params[:contact_form]).deliver
DSL fails
class ContactForm < MailForm::Base
  to :author_email
  from { |c| "#{c.name} <#{c.email}>" }
  subject "Contact form"

  headers { |c|
    { "X-Spam" => "True" } if c.honey_pot
  }

  attributes :name, :email, :message

  def author_email
    Author.find(self.author_id).email
  end
end
NoDSL
class ContactForm < MailForm::Base
  attributes :name, :email, :message

  def headers
    {
      :to => author_email,
      :from => "#{name} <#{email}>",
      :subject => "Contact form"
    }
  end

  def author_email
    Author.find(self.author_id).email
  end
end
Other examples

ā€¢ Rake v.s. Thor
ā€¢ RSpec v.s. Unit::test
Rake example
task :process do
  # do some processing
end

namespace :app do
  task :setup do
    # do some setup
    Rake::Task[:process].invoke
  end
end

rake app:setup
Thor example
 class Default < Thor
   def process
     # do some processing
   end
 end

 class App < Thor
   def setup
     # do some setup
     Default.new.process
   end
 end

 thor app:setup
JosĆ©ā€™s Conclusion

ā€¢ DSL or NoDSL - don't actually have
  answer
ā€¢ Replace "It provides a nice DSL" with
  "it relies on a simple contract".
Story 2:

Rails 2 to 3
 API changes
Routes
                            nice DSL
# Rails 2
map.resources :people, :member => { :dashboard => :get,
                                    :resend => :post,
                                    :upload => :put } do |people|
    people.resource :avatra
end

# Rails 3
resources :people do
    resource :avatar
    member do
        get :dashboard
        post :resend
        put :upload
    end
end
AR queries (1)
                     method chaining


# Rails 2
users = User.find(:all, :conditions => { :name =>
'ihower' }, :limit => 10, :order => 'age')

# Rails 3
users = User.where(:name => 'ihower').limit(20).order('age')
AR queries (2)
      Unify ļ¬nders, named_scope, with_scope to Relation
# Rails 2
users = User
users = users.some_named_scope if params[:some]
sort = params[:sort] || "id"
conditions = {}

if params[:name]
  conditions = User.merge_conditions( conditions, { :name => params[:name] } )
end

if params[:age]
  conditions = User.merge_conditions( conditions, { :age => params[:age] } )
end

find_conditions = { :conditions => conditions, :order => "#{sort} #{dir}" }
sort = params[:sort] || "id"

users = users.find(:all, :conditions => conditions, :order => sort )
AR queries (2)
Unify ļ¬nders, named_scope, with_scope to Relation


# Rails   3
users =   User
users =   users.some_scope if params[:some]
users =   users.where( :name => params[:name] ) if params[:name]
users =   users.where( :age => params[:age] ) if params[:age]
users =   users.order( params[:sort] || "id" )
AR queries (3)
Using class methods instead of scopes when you need lambda
    # Rails 3
    class Product < ActiveRecord::Base

      scope :discontinued, where(:discontinued => true)
      scope :cheaper_than, lambda { |price| where("price < ?", price) }

    end

    # Rails 3, prefer this way more
    class Product < ActiveRecord::Base

      scope :discontinued, where(:discontinued => true)

      def self.cheaper_than(price)
        where("price < ?", price)
      end

    end
AR validation (1)
# Rails 2
class User < ActiveRecord::Base
  validates_presence_of :email
  validates_uniqueness_of :email
  validates_format_of :email, :with => /^[wd]+$/ :on => :create, :message =>
"is invalid"
end

# Rails 3
class User < ActiveRecord::Base
  validates :email,
            :presence => true,
            :uniqueness => true,
            :format => { :with => /^([^@s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})$/i }
end




                                    http://asciicasts.com/episodes/211-validations-in-rails-3
AR validation (2)
                           custom validator

# Rails 3
class User < ActiveRecord::Base
  validates :email, :presence => true,
                    :uniqueness => true,
                    :email_format => true
end

class EmailFormatValidator < ActiveModel::EachValidator
  def validate_each(object, attribute, value)
    unless value =~ /^([^@s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})$/i
      object.errors[attribute] << (options[:message] || "is not formatted
properly")
    end
  end
end
ActionMailer
# Rails 2
class UserMailer < ActionMailer::Base

  def signup(user)
    recipients user.email
    from 'ihower@gmail.com'
    body :name => user.name
    subject "Signup"
  end

end

UserMailer.deliver_registration_confirmation(@user)
ActionMailer
# Rails 3
class UserMailer < ActionMailer::Base

  default :from => "ihower@gmail.com"

  def signup(user)
    @name = user.name
    mail(:to => user.email, :subject => "Signup" )
  end

end

UserMailer.registration_confirmation(@user).deliver
I think itā€™s a Ruby APIs
     paradigm shift
 Apparently, Rails is the most successful Ruby open source codebase
                        which you can learn from.
References
ā€¢   Ruby Best Practices, Oā€™Reilly

ā€¢   The Ruby Object Model and Metaprogrammin, Pragmatic

ā€¢   Programming Ruby 1.9, Pragmatic

ā€¢   Metaprogramming Ruby, Pragmatic

ā€¢   Advanced Rails, Oā€™Reilly

ā€¢   The Building Blocks of Ruby
    http://yehudakatz.com/2010/02/07/the-building-blocks-of-ruby/

ā€¢   The Importance of Executable Class Bodies
    http://yehudakatz.com/2009/06/04/the-importance-of-executable-class-bodies/

ā€¢   Metaprogramming in Ruby: Itā€™s All About the Self
    http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/

ā€¢   Three implicit contexts in Ruby
    http://yugui.jp/articles/846
The End

More Related Content

What's hot

Gradle恩恆恧恗悇恆
Gradle恩恆恧恗悇恆Gradle恩恆恧恗悇恆
Gradle恩恆恧恗悇恆Takuma Watabiki
Ā 
Karate - powerful and simple framework for REST API automation testing
Karate - powerful and simple framework for REST API automation testingKarate - powerful and simple framework for REST API automation testing
Karate - powerful and simple framework for REST API automation testingRoman Liubun
Ā 
Robot framework į„‹į…³į†Æ į„‹į…µį„‹į…­į†¼į„’į…”į†« į„€į…µį„‚į…³į†¼ į„į…¦į„‰į…³į„į…³ į„Œį…”į„ƒį…©į†¼į„’į…Ŗ
Robot framework į„‹į…³į†Æ į„‹į…µį„‹į…­į†¼į„’į…”į†« į„€į…µį„‚į…³į†¼ į„į…¦į„‰į…³į„į…³ į„Œį…”į„ƒį…©į†¼į„’į…ŖRobot framework į„‹į…³į†Æ į„‹į…µį„‹į…­į†¼į„’į…”į†« į„€į…µį„‚į…³į†¼ į„į…¦į„‰į…³į„į…³ į„Œį…”į„ƒį…©į†¼į„’į…Ŗ
Robot framework į„‹į…³į†Æ į„‹į…µį„‹į…­į†¼į„’į…”į†« į„€į…µį„‚į…³į†¼ į„į…¦į„‰į…³į„į…³ į„Œį…”į„ƒį…©į†¼į„’į…ŖJaehoon Oh
Ā 
Curso JavaScript - Aula sobre DOM e Ajax
Curso JavaScript - Aula sobre DOM e AjaxCurso JavaScript - Aula sobre DOM e Ajax
Curso JavaScript - Aula sobre DOM e AjaxTiago AntƓnio da Silva
Ā 
JMeter vs LoadRunner | Edureka
JMeter vs LoadRunner | EdurekaJMeter vs LoadRunner | Edureka
JMeter vs LoadRunner | EdurekaEdureka!
Ā 
ę—¢å­˜ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’Java11恫åƾåæœć•ć›ć‚‹éš›ć« ēŸ„ć£ć¦ćŠćć¹ćć“ćØ
ę—¢å­˜ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’Java11恫åƾåæœć•ć›ć‚‹éš›ć« ēŸ„ć£ć¦ćŠćć¹ćć“ćØę—¢å­˜ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’Java11恫åƾåæœć•ć›ć‚‹éš›ć« ēŸ„ć£ć¦ćŠćć¹ćć“ćØ
ę—¢å­˜ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’Java11恫åƾåæœć•ć›ć‚‹éš›ć« ēŸ„ć£ć¦ćŠćć¹ćć“ćØikeyat
Ā 
RESTful API å…„é–€
RESTful API å…„é–€RESTful API å…„é–€
RESTful API å…„é–€Keisuke Nishitani
Ā 
é–¢ę•°ćƒ—ćƒ­ć‚°ćƒ©ćƒŸćƒ³ć‚°å…„é–€
é–¢ę•°ćƒ—ćƒ­ć‚°ćƒ©ćƒŸćƒ³ć‚°å…„é–€é–¢ę•°ćƒ—ćƒ­ć‚°ćƒ©ćƒŸćƒ³ć‚°å…„é–€
é–¢ę•°ćƒ—ćƒ­ć‚°ćƒ©ćƒŸćƒ³ć‚°å…„é–€Hideyuki Tanaka
Ā 
JavaOne 2011 - JVM Bytecode for Dummies
JavaOne 2011 - JVM Bytecode for DummiesJavaOne 2011 - JVM Bytecode for Dummies
JavaOne 2011 - JVM Bytecode for DummiesCharles Nutter
Ā 
2ģž„. Runtime Data Areas
2ģž„. Runtime Data Areas2ģž„. Runtime Data Areas
2ģž„. Runtime Data Areasź¹€ ķ•œė„
Ā 
Android & iOS Automation Using Appium
Android & iOS Automation Using AppiumAndroid & iOS Automation Using Appium
Android & iOS Automation Using AppiumMindfire Solutions
Ā 
XML e Banco de Dados XML Nativo
XML e Banco de Dados XML NativoXML e Banco de Dados XML Nativo
XML e Banco de Dados XML NativoGPrimola
Ā 
Behavior driven development (bdd)
Behavior driven development (bdd)Behavior driven development (bdd)
Behavior driven development (bdd)Rohit Bisht
Ā 

What's hot (14)

Gradle恩恆恧恗悇恆
Gradle恩恆恧恗悇恆Gradle恩恆恧恗悇恆
Gradle恩恆恧恗悇恆
Ā 
Karate - powerful and simple framework for REST API automation testing
Karate - powerful and simple framework for REST API automation testingKarate - powerful and simple framework for REST API automation testing
Karate - powerful and simple framework for REST API automation testing
Ā 
Robot framework į„‹į…³į†Æ į„‹į…µį„‹į…­į†¼į„’į…”į†« į„€į…µį„‚į…³į†¼ į„į…¦į„‰į…³į„į…³ į„Œį…”į„ƒį…©į†¼į„’į…Ŗ
Robot framework į„‹į…³į†Æ į„‹į…µį„‹į…­į†¼į„’į…”į†« į„€į…µį„‚į…³į†¼ į„į…¦į„‰į…³į„į…³ į„Œį…”į„ƒį…©į†¼į„’į…ŖRobot framework į„‹į…³į†Æ į„‹į…µį„‹į…­į†¼į„’į…”į†« į„€į…µį„‚į…³į†¼ į„į…¦į„‰į…³į„į…³ į„Œį…”į„ƒį…©į†¼į„’į…Ŗ
Robot framework į„‹į…³į†Æ į„‹į…µį„‹į…­į†¼į„’į…”į†« į„€į…µį„‚į…³į†¼ į„į…¦į„‰į…³į„į…³ į„Œį…”į„ƒį…©į†¼į„’į…Ŗ
Ā 
Curso JavaScript - Aula sobre DOM e Ajax
Curso JavaScript - Aula sobre DOM e AjaxCurso JavaScript - Aula sobre DOM e Ajax
Curso JavaScript - Aula sobre DOM e Ajax
Ā 
JMeter vs LoadRunner | Edureka
JMeter vs LoadRunner | EdurekaJMeter vs LoadRunner | Edureka
JMeter vs LoadRunner | Edureka
Ā 
ę—¢å­˜ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’Java11恫åƾåæœć•ć›ć‚‹éš›ć« ēŸ„ć£ć¦ćŠćć¹ćć“ćØ
ę—¢å­˜ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’Java11恫åƾåæœć•ć›ć‚‹éš›ć« ēŸ„ć£ć¦ćŠćć¹ćć“ćØę—¢å­˜ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’Java11恫åƾåæœć•ć›ć‚‹éš›ć« ēŸ„ć£ć¦ćŠćć¹ćć“ćØ
ę—¢å­˜ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’Java11恫åƾåæœć•ć›ć‚‹éš›ć« ēŸ„ć£ć¦ćŠćć¹ćć“ćØ
Ā 
RESTful API å…„é–€
RESTful API å…„é–€RESTful API å…„é–€
RESTful API å…„é–€
Ā 
Cucumber and Spock Primer
Cucumber and Spock PrimerCucumber and Spock Primer
Cucumber and Spock Primer
Ā 
é–¢ę•°ćƒ—ćƒ­ć‚°ćƒ©ćƒŸćƒ³ć‚°å…„é–€
é–¢ę•°ćƒ—ćƒ­ć‚°ćƒ©ćƒŸćƒ³ć‚°å…„é–€é–¢ę•°ćƒ—ćƒ­ć‚°ćƒ©ćƒŸćƒ³ć‚°å…„é–€
é–¢ę•°ćƒ—ćƒ­ć‚°ćƒ©ćƒŸćƒ³ć‚°å…„é–€
Ā 
JavaOne 2011 - JVM Bytecode for Dummies
JavaOne 2011 - JVM Bytecode for DummiesJavaOne 2011 - JVM Bytecode for Dummies
JavaOne 2011 - JVM Bytecode for Dummies
Ā 
2ģž„. Runtime Data Areas
2ģž„. Runtime Data Areas2ģž„. Runtime Data Areas
2ģž„. Runtime Data Areas
Ā 
Android & iOS Automation Using Appium
Android & iOS Automation Using AppiumAndroid & iOS Automation Using Appium
Android & iOS Automation Using Appium
Ā 
XML e Banco de Dados XML Nativo
XML e Banco de Dados XML NativoXML e Banco de Dados XML Nativo
XML e Banco de Dados XML Nativo
Ā 
Behavior driven development (bdd)
Behavior driven development (bdd)Behavior driven development (bdd)
Behavior driven development (bdd)
Ā 

Viewers also liked

Introduction to Scala
Introduction to ScalaIntroduction to Scala
Introduction to ScalaBrian Hsu
Ā 
Magic in ruby
Magic in rubyMagic in ruby
Magic in rubyDavid Lin
Ā 
MongoDB - Ruby document store that doesn't rhyme with ouch
MongoDB - Ruby document store that doesn't rhyme with ouchMongoDB - Ruby document store that doesn't rhyme with ouch
MongoDB - Ruby document store that doesn't rhyme with ouchWynn Netherland
Ā 
Programming Android Application in Scala.
Programming Android Application in Scala.Programming Android Application in Scala.
Programming Android Application in Scala.Brian Hsu
Ā 
The Black Magic of Ruby Metaprogramming
The Black Magic of Ruby MetaprogrammingThe Black Magic of Ruby Metaprogramming
The Black Magic of Ruby Metaprogrammingitnig
Ā 
HTML Lecture Part 1 of 2
HTML Lecture Part 1 of 2HTML Lecture Part 1 of 2
HTML Lecture Part 1 of 2Sharon Wasden
Ā 
Ruby object model
Ruby object modelRuby object model
Ruby object modelChamnap Chhorn
Ā 
äŗŗ造äŗ¤č«‡čŖžč؀ (ä½æē”Ø꜉BNFēš„口čŖžé€éŽę©Ÿå™Øēæ»č­Æ和外國äŗŗęŗé€š)
äŗŗ造äŗ¤č«‡čŖžč؀  (ä½æē”Ø꜉BNFēš„口čŖžé€éŽę©Ÿå™Øēæ»č­Æ和外國äŗŗęŗé€š)äŗŗ造äŗ¤č«‡čŖžč؀  (ä½æē”Ø꜉BNFēš„口čŖžé€éŽę©Ÿå™Øēæ»č­Æ和外國äŗŗęŗé€š)
äŗŗ造äŗ¤č«‡čŖžč؀ (ä½æē”Ø꜉BNFēš„口čŖžé€éŽę©Ÿå™Øēæ»č­Æ和外國äŗŗęŗé€š)鍾čŖ  陳鍾čŖ 
Ā 
Unicode ncr
Unicode ncrUnicode ncr
Unicode ncrBrian Hsu
Ā 
ē”ØJavaScript åƦčøć€Šč»Ÿé«”å·„ēØ‹ć€‹ēš„é‚£äŗ›äŗ‹å…’ļ¼
ē”ØJavaScript  åƦčøć€Šč»Ÿé«”å·„ēØ‹ć€‹ēš„é‚£äŗ›äŗ‹å…’ļ¼ē”ØJavaScript  åƦčøć€Šč»Ÿé«”å·„ēØ‹ć€‹ēš„é‚£äŗ›äŗ‹å…’ļ¼
ē”ØJavaScript åƦčøć€Šč»Ÿé«”å·„ēØ‹ć€‹ēš„é‚£äŗ›äŗ‹å…’ļ¼é¾čŖ  陳鍾čŖ 
Ā 

Viewers also liked (12)

Introduction to Scala
Introduction to ScalaIntroduction to Scala
Introduction to Scala
Ā 
Magic in ruby
Magic in rubyMagic in ruby
Magic in ruby
Ā 
ZODB Tips and Tricks
ZODB Tips and TricksZODB Tips and Tricks
ZODB Tips and Tricks
Ā 
Ruby objects
Ruby objectsRuby objects
Ruby objects
Ā 
MongoDB - Ruby document store that doesn't rhyme with ouch
MongoDB - Ruby document store that doesn't rhyme with ouchMongoDB - Ruby document store that doesn't rhyme with ouch
MongoDB - Ruby document store that doesn't rhyme with ouch
Ā 
Programming Android Application in Scala.
Programming Android Application in Scala.Programming Android Application in Scala.
Programming Android Application in Scala.
Ā 
The Black Magic of Ruby Metaprogramming
The Black Magic of Ruby MetaprogrammingThe Black Magic of Ruby Metaprogramming
The Black Magic of Ruby Metaprogramming
Ā 
HTML Lecture Part 1 of 2
HTML Lecture Part 1 of 2HTML Lecture Part 1 of 2
HTML Lecture Part 1 of 2
Ā 
Ruby object model
Ruby object modelRuby object model
Ruby object model
Ā 
äŗŗ造äŗ¤č«‡čŖžč؀ (ä½æē”Ø꜉BNFēš„口čŖžé€éŽę©Ÿå™Øēæ»č­Æ和外國äŗŗęŗé€š)
äŗŗ造äŗ¤č«‡čŖžč؀  (ä½æē”Ø꜉BNFēš„口čŖžé€éŽę©Ÿå™Øēæ»č­Æ和外國äŗŗęŗé€š)äŗŗ造äŗ¤č«‡čŖžč؀  (ä½æē”Ø꜉BNFēš„口čŖžé€éŽę©Ÿå™Øēæ»č­Æ和外國äŗŗęŗé€š)
äŗŗ造äŗ¤č«‡čŖžč؀ (ä½æē”Ø꜉BNFēš„口čŖžé€éŽę©Ÿå™Øēæ»č­Æ和外國äŗŗęŗé€š)
Ā 
Unicode ncr
Unicode ncrUnicode ncr
Unicode ncr
Ā 
ē”ØJavaScript åƦčøć€Šč»Ÿé«”å·„ēØ‹ć€‹ēš„é‚£äŗ›äŗ‹å…’ļ¼
ē”ØJavaScript  åƦčøć€Šč»Ÿé«”å·„ēØ‹ć€‹ēš„é‚£äŗ›äŗ‹å…’ļ¼ē”ØJavaScript  åƦčøć€Šč»Ÿé«”å·„ēØ‹ć€‹ēš„é‚£äŗ›äŗ‹å…’ļ¼
ē”ØJavaScript åƦčøć€Šč»Ÿé«”å·„ēØ‹ć€‹ēš„é‚£äŗ›äŗ‹å…’ļ¼
Ā 

Similar to Designing Ruby APIs

Dsl
DslDsl
Dslphoet
Ā 
PostobjektovƩ programovanie v Ruby
PostobjektovƩ programovanie v RubyPostobjektovƩ programovanie v Ruby
PostobjektovƩ programovanie v RubyJano Suchal
Ā 
Module Magic
Module MagicModule Magic
Module MagicJames Gray
Ā 
Metaprogramovanie #1
Metaprogramovanie #1Metaprogramovanie #1
Metaprogramovanie #1Jano Suchal
Ā 
An introduction to Ruby
An introduction to RubyAn introduction to Ruby
An introduction to RubyWes Oldenbeuving
Ā 
Desarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosDesarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosEdgar Suarez
Ā 
Blocks by Lachs Cox
Blocks by Lachs CoxBlocks by Lachs Cox
Blocks by Lachs Coxlachie
Ā 
Ruby on Rails
Ruby on RailsRuby on Rails
Ruby on Railsbryanbibat
Ā 
Ruby ē؋式čŖžčØ€å…„é–€å°Žč¦½
Ruby ē؋式čŖžčØ€å…„é–€å°Žč¦½Ruby ē؋式čŖžčØ€å…„é–€å°Žč¦½
Ruby ē؋式čŖžčØ€å…„é–€å°Žč¦½Wen-Tien Chang
Ā 
Attributes Unwrapped: Lessons under the surface of active record
Attributes Unwrapped: Lessons under the surface of active recordAttributes Unwrapped: Lessons under the surface of active record
Attributes Unwrapped: Lessons under the surface of active record.toster
Ā 
A tour on ruby and friends
A tour on ruby and friendsA tour on ruby and friends
A tour on ruby and friendsę—»ē¦ ę½˜
Ā 
Metaprogramming + Ds Ls
Metaprogramming + Ds LsMetaprogramming + Ds Ls
Metaprogramming + Ds LsArrrrCamp
Ā 
Ruby 2: some new things
Ruby 2: some new thingsRuby 2: some new things
Ruby 2: some new thingsDavid Black
Ā 
Active Support Core Extensions (1)
Active Support Core Extensions (1)Active Support Core Extensions (1)
Active Support Core Extensions (1)RORLAB
Ā 
Rubyforjavaprogrammers 1210167973516759-9
Rubyforjavaprogrammers 1210167973516759-9Rubyforjavaprogrammers 1210167973516759-9
Rubyforjavaprogrammers 1210167973516759-9sagaroceanic11
Ā 
Rubyforjavaprogrammers 1210167973516759-9
Rubyforjavaprogrammers 1210167973516759-9Rubyforjavaprogrammers 1210167973516759-9
Rubyforjavaprogrammers 1210167973516759-9sagaroceanic11
Ā 

Similar to Designing Ruby APIs (20)

Ruby
RubyRuby
Ruby
Ā 
Dsl
DslDsl
Dsl
Ā 
PostobjektovƩ programovanie v Ruby
PostobjektovƩ programovanie v RubyPostobjektovƩ programovanie v Ruby
PostobjektovƩ programovanie v Ruby
Ā 
Module Magic
Module MagicModule Magic
Module Magic
Ā 
Metaprogramovanie #1
Metaprogramovanie #1Metaprogramovanie #1
Metaprogramovanie #1
Ā 
Ruby 2.0
Ruby 2.0Ruby 2.0
Ruby 2.0
Ā 
An introduction to Ruby
An introduction to RubyAn introduction to Ruby
An introduction to Ruby
Ā 
Desarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosDesarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutos
Ā 
Blocks by Lachs Cox
Blocks by Lachs CoxBlocks by Lachs Cox
Blocks by Lachs Cox
Ā 
Ruby on Rails
Ruby on RailsRuby on Rails
Ruby on Rails
Ā 
Ruby ē؋式čŖžčØ€å…„é–€å°Žč¦½
Ruby ē؋式čŖžčØ€å…„é–€å°Žč¦½Ruby ē؋式čŖžčØ€å…„é–€å°Žč¦½
Ruby ē؋式čŖžčØ€å…„é–€å°Žč¦½
Ā 
Attributes Unwrapped: Lessons under the surface of active record
Attributes Unwrapped: Lessons under the surface of active recordAttributes Unwrapped: Lessons under the surface of active record
Attributes Unwrapped: Lessons under the surface of active record
Ā 
A tour on ruby and friends
A tour on ruby and friendsA tour on ruby and friends
A tour on ruby and friends
Ā 
Metaprogramming + Ds Ls
Metaprogramming + Ds LsMetaprogramming + Ds Ls
Metaprogramming + Ds Ls
Ā 
Ruby tricks2
Ruby tricks2Ruby tricks2
Ruby tricks2
Ā 
Ruby 2: some new things
Ruby 2: some new thingsRuby 2: some new things
Ruby 2: some new things
Ā 
Steady with ruby
Steady with rubySteady with ruby
Steady with ruby
Ā 
Active Support Core Extensions (1)
Active Support Core Extensions (1)Active Support Core Extensions (1)
Active Support Core Extensions (1)
Ā 
Rubyforjavaprogrammers 1210167973516759-9
Rubyforjavaprogrammers 1210167973516759-9Rubyforjavaprogrammers 1210167973516759-9
Rubyforjavaprogrammers 1210167973516759-9
Ā 
Rubyforjavaprogrammers 1210167973516759-9
Rubyforjavaprogrammers 1210167973516759-9Rubyforjavaprogrammers 1210167973516759-9
Rubyforjavaprogrammers 1210167973516759-9
Ā 

More from Wen-Tien Chang

ā¼¤čŖžā¾”ęؔ型 LLM ꇉā½¤é–‹ē™¼å…„ā¾Ø
ā¼¤čŖžā¾”ęؔ型 LLM ꇉā½¤é–‹ē™¼å…„ā¾Øā¼¤čŖžā¾”ęؔ型 LLM ꇉā½¤é–‹ē™¼å…„ā¾Ø
ā¼¤čŖžā¾”ęؔ型 LLM ꇉā½¤é–‹ē™¼å…„ā¾ØWen-Tien Chang
Ā 
Ruby Rails 老åøę©Ÿåø¶é£›
Ruby Rails 老åøę©Ÿåø¶é£›Ruby Rails 老åøę©Ÿåø¶é£›
Ruby Rails 老åøę©Ÿåø¶é£›Wen-Tien Chang
Ā 
A brief introduction to Machine Learning
A brief introduction to Machine LearningA brief introduction to Machine Learning
A brief introduction to Machine LearningWen-Tien Chang
Ā 
ę·ŗ談 Startup 公åøēš„č»Ÿé«”é–‹ē™¼ęµē؋ v2
ę·ŗ談 Startup 公åøēš„č»Ÿé«”é–‹ē™¼ęµē؋ v2ę·ŗ談 Startup 公åøēš„č»Ÿé«”é–‹ē™¼ęµē؋ v2
ę·ŗ談 Startup 公åøēš„č»Ÿé«”é–‹ē™¼ęµē؋ v2Wen-Tien Chang
Ā 
RSpec on Rails Tutorial
RSpec on Rails TutorialRSpec on Rails Tutorial
RSpec on Rails TutorialWen-Tien Chang
Ā 
RSpec & TDD Tutorial
RSpec & TDD TutorialRSpec & TDD Tutorial
RSpec & TDD TutorialWen-Tien Chang
Ā 
ALPHAhackathon: How to collaborate
ALPHAhackathon: How to collaborateALPHAhackathon: How to collaborate
ALPHAhackathon: How to collaborateWen-Tien Chang
Ā 
Git ē‰ˆęœ¬ęŽ§åˆ¶ē³»ēµ± -- å¾žå¾®č§€åˆ°å®č§€
Git ē‰ˆęœ¬ęŽ§åˆ¶ē³»ēµ± -- å¾žå¾®č§€åˆ°å®č§€Git ē‰ˆęœ¬ęŽ§åˆ¶ē³»ēµ± -- å¾žå¾®č§€åˆ°å®č§€
Git ē‰ˆęœ¬ęŽ§åˆ¶ē³»ēµ± -- å¾žå¾®č§€åˆ°å®č§€Wen-Tien Chang
Ā 
Exception Handling: Designing Robust Software in Ruby (with presentation note)
Exception Handling: Designing Robust Software in Ruby (with presentation note)Exception Handling: Designing Robust Software in Ruby (with presentation note)
Exception Handling: Designing Robust Software in Ruby (with presentation note)Wen-Tien Chang
Ā 
Exception Handling: Designing Robust Software in Ruby
Exception Handling: Designing Robust Software in RubyException Handling: Designing Robust Software in Ruby
Exception Handling: Designing Robust Software in RubyWen-Tien Chang
Ā 
從 Classes 到 Objects: 那äŗ› OOP ꕙꈑēš„äŗ‹
從 Classes 到 Objects: 那äŗ› OOP ꕙꈑēš„äŗ‹å¾ž Classes 到 Objects: 那äŗ› OOP ꕙꈑēš„äŗ‹
從 Classes 到 Objects: 那äŗ› OOP ꕙꈑēš„äŗ‹Wen-Tien Chang
Ā 
Yet another introduction to Git - from the bottom up
Yet another introduction to Git - from the bottom upYet another introduction to Git - from the bottom up
Yet another introduction to Git - from the bottom upWen-Tien Chang
Ā 
A brief introduction to Vagrant ā€“ 原來 VirtualBox åÆ仄這ęØ£ēŽ©
A brief introduction to Vagrant ā€“ 原來 VirtualBox åÆ仄這ęØ£ēŽ©A brief introduction to Vagrant ā€“ 原來 VirtualBox åÆ仄這ęØ£ēŽ©
A brief introduction to Vagrant ā€“ 原來 VirtualBox åÆ仄這ęØ£ēŽ©Wen-Tien Chang
Ā 
Ruby ē؋式čŖžč؀ē¶œč¦½ē°”介
Ruby ē؋式čŖžč؀ē¶œč¦½ē°”介Ruby ē؋式čŖžč؀ē¶œč¦½ē°”介
Ruby ē؋式čŖžč؀ē¶œč¦½ē°”介Wen-Tien Chang
Ā 
A brief introduction to SPDY - 邁向 HTTP/2.0
A brief introduction to SPDY - 邁向 HTTP/2.0A brief introduction to SPDY - 邁向 HTTP/2.0
A brief introduction to SPDY - 邁向 HTTP/2.0Wen-Tien Chang
Ā 
RubyConf Taiwan 2012 Opening & Closing
RubyConf Taiwan 2012 Opening & ClosingRubyConf Taiwan 2012 Opening & Closing
RubyConf Taiwan 2012 Opening & ClosingWen-Tien Chang
Ā 
從 Scrum 到 Kanban: ē‚ŗ什éŗ¼ Scrum äøé©åˆ Lean Startup
從 Scrum 到 Kanban: ē‚ŗ什éŗ¼ Scrum äøé©åˆ Lean Startup從 Scrum 到 Kanban: ē‚ŗ什éŗ¼ Scrum äøé©åˆ Lean Startup
從 Scrum 到 Kanban: ē‚ŗ什éŗ¼ Scrum äøé©åˆ Lean StartupWen-Tien Chang
Ā 
Git Tutorial ꕙå­ø
Git Tutorial ꕙå­øGit Tutorial ꕙå­ø
Git Tutorial ꕙå­øWen-Tien Chang
Ā 
那äŗ› Functional Programming ꕙꈑēš„äŗ‹
那äŗ› Functional Programming ꕙꈑēš„äŗ‹é‚£äŗ› Functional Programming ꕙꈑēš„äŗ‹
那äŗ› Functional Programming ꕙꈑēš„äŗ‹Wen-Tien Chang
Ā 
RubyConf Taiwan 2011 Opening & Closing
RubyConf Taiwan 2011 Opening & ClosingRubyConf Taiwan 2011 Opening & Closing
RubyConf Taiwan 2011 Opening & ClosingWen-Tien Chang
Ā 

More from Wen-Tien Chang (20)

ā¼¤čŖžā¾”ęؔ型 LLM ꇉā½¤é–‹ē™¼å…„ā¾Ø
ā¼¤čŖžā¾”ęؔ型 LLM ꇉā½¤é–‹ē™¼å…„ā¾Øā¼¤čŖžā¾”ęؔ型 LLM ꇉā½¤é–‹ē™¼å…„ā¾Ø
ā¼¤čŖžā¾”ęؔ型 LLM ꇉā½¤é–‹ē™¼å…„ā¾Ø
Ā 
Ruby Rails 老åøę©Ÿåø¶é£›
Ruby Rails 老åøę©Ÿåø¶é£›Ruby Rails 老åøę©Ÿåø¶é£›
Ruby Rails 老åøę©Ÿåø¶é£›
Ā 
A brief introduction to Machine Learning
A brief introduction to Machine LearningA brief introduction to Machine Learning
A brief introduction to Machine Learning
Ā 
ę·ŗ談 Startup 公åøēš„č»Ÿé«”é–‹ē™¼ęµē؋ v2
ę·ŗ談 Startup 公åøēš„č»Ÿé«”é–‹ē™¼ęµē؋ v2ę·ŗ談 Startup 公åøēš„č»Ÿé«”é–‹ē™¼ęµē؋ v2
ę·ŗ談 Startup 公åøēš„č»Ÿé«”é–‹ē™¼ęµē؋ v2
Ā 
RSpec on Rails Tutorial
RSpec on Rails TutorialRSpec on Rails Tutorial
RSpec on Rails Tutorial
Ā 
RSpec & TDD Tutorial
RSpec & TDD TutorialRSpec & TDD Tutorial
RSpec & TDD Tutorial
Ā 
ALPHAhackathon: How to collaborate
ALPHAhackathon: How to collaborateALPHAhackathon: How to collaborate
ALPHAhackathon: How to collaborate
Ā 
Git ē‰ˆęœ¬ęŽ§åˆ¶ē³»ēµ± -- å¾žå¾®č§€åˆ°å®č§€
Git ē‰ˆęœ¬ęŽ§åˆ¶ē³»ēµ± -- å¾žå¾®č§€åˆ°å®č§€Git ē‰ˆęœ¬ęŽ§åˆ¶ē³»ēµ± -- å¾žå¾®č§€åˆ°å®č§€
Git ē‰ˆęœ¬ęŽ§åˆ¶ē³»ēµ± -- å¾žå¾®č§€åˆ°å®č§€
Ā 
Exception Handling: Designing Robust Software in Ruby (with presentation note)
Exception Handling: Designing Robust Software in Ruby (with presentation note)Exception Handling: Designing Robust Software in Ruby (with presentation note)
Exception Handling: Designing Robust Software in Ruby (with presentation note)
Ā 
Exception Handling: Designing Robust Software in Ruby
Exception Handling: Designing Robust Software in RubyException Handling: Designing Robust Software in Ruby
Exception Handling: Designing Robust Software in Ruby
Ā 
從 Classes 到 Objects: 那äŗ› OOP ꕙꈑēš„äŗ‹
從 Classes 到 Objects: 那äŗ› OOP ꕙꈑēš„äŗ‹å¾ž Classes 到 Objects: 那äŗ› OOP ꕙꈑēš„äŗ‹
從 Classes 到 Objects: 那äŗ› OOP ꕙꈑēš„äŗ‹
Ā 
Yet another introduction to Git - from the bottom up
Yet another introduction to Git - from the bottom upYet another introduction to Git - from the bottom up
Yet another introduction to Git - from the bottom up
Ā 
A brief introduction to Vagrant ā€“ 原來 VirtualBox åÆ仄這ęØ£ēŽ©
A brief introduction to Vagrant ā€“ 原來 VirtualBox åÆ仄這ęØ£ēŽ©A brief introduction to Vagrant ā€“ 原來 VirtualBox åÆ仄這ęØ£ēŽ©
A brief introduction to Vagrant ā€“ 原來 VirtualBox åÆ仄這ęØ£ēŽ©
Ā 
Ruby ē؋式čŖžč؀ē¶œč¦½ē°”介
Ruby ē؋式čŖžč؀ē¶œč¦½ē°”介Ruby ē؋式čŖžč؀ē¶œč¦½ē°”介
Ruby ē؋式čŖžč؀ē¶œč¦½ē°”介
Ā 
A brief introduction to SPDY - 邁向 HTTP/2.0
A brief introduction to SPDY - 邁向 HTTP/2.0A brief introduction to SPDY - 邁向 HTTP/2.0
A brief introduction to SPDY - 邁向 HTTP/2.0
Ā 
RubyConf Taiwan 2012 Opening & Closing
RubyConf Taiwan 2012 Opening & ClosingRubyConf Taiwan 2012 Opening & Closing
RubyConf Taiwan 2012 Opening & Closing
Ā 
從 Scrum 到 Kanban: ē‚ŗ什éŗ¼ Scrum äøé©åˆ Lean Startup
從 Scrum 到 Kanban: ē‚ŗ什éŗ¼ Scrum äøé©åˆ Lean Startup從 Scrum 到 Kanban: ē‚ŗ什éŗ¼ Scrum äøé©åˆ Lean Startup
從 Scrum 到 Kanban: ē‚ŗ什éŗ¼ Scrum äøé©åˆ Lean Startup
Ā 
Git Tutorial ꕙå­ø
Git Tutorial ꕙå­øGit Tutorial ꕙå­ø
Git Tutorial ꕙå­ø
Ā 
那äŗ› Functional Programming ꕙꈑēš„äŗ‹
那äŗ› Functional Programming ꕙꈑēš„äŗ‹é‚£äŗ› Functional Programming ꕙꈑēš„äŗ‹
那äŗ› Functional Programming ꕙꈑēš„äŗ‹
Ā 
RubyConf Taiwan 2011 Opening & Closing
RubyConf Taiwan 2011 Opening & ClosingRubyConf Taiwan 2011 Opening & Closing
RubyConf Taiwan 2011 Opening & Closing
Ā 

Recently uploaded

Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Zilliz
Ā 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
Ā 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbuapidays
Ā 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyKhushali Kathiriya
Ā 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
Ā 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel AraĆŗjo
Ā 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
Ā 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAndrey Devyatkin
Ā 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobeapidays
Ā 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
Ā 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...apidays
Ā 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
Ā 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesrafiqahmad00786416
Ā 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingEdi Saputra
Ā 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...apidays
Ā 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodJuan lago vƔzquez
Ā 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native ApplicationsWSO2
Ā 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024The Digital Insurer
Ā 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDropbox
Ā 

Recently uploaded (20)

Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Ā 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
Ā 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Ā 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
Ā 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
Ā 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Ā 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
Ā 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
Ā 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Ā 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
Ā 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
Ā 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Ā 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
Ā 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
Ā 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Ā 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Ā 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Ā 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
Ā 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
Ā 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
Ā 

Designing Ruby APIs

  • 1. Designing Beautiful Ruby APIs V.2 ihower@gmail.com 2010/4/25 & 6/26 RubyConf Taiwan & RubyConf China
  • 2. About Me ā€¢ a.k.a. ihower ā€¢ http://ihower.tw ā€¢ http://twitter.com/ihower ā€¢ Rails Developer since 2006 ā€¢ The Organizer of Ruby Taiwan Community ā€¢ http://ruby.tw ā€¢ http://rubyconf.tw
  • 5. Agenda 10 techniques and a sub-talk ā€¢ Arguments processing ā€¢ Code blocks ā€¢ Module ā€¢ method_missing? ā€¢ const_missing ā€¢ Methods chaining ā€¢ Core extension ā€¢ (sub-talk) Ruby Object Model and Meta-programming ā€¢ Class macro ā€¢ instance_eval ā€¢ Class.new
  • 6. Deļ¬ne ā€œbeautifulā€ ā€¢ Readable: easy to understand ā€¢ Efļ¬cient: easy to write ā€¢ Flexible: easy to extend
  • 8. Pseudo-Keyword Arguments def blah(options) puts options[:foo] puts options[:bar] end blah(:foo => "test", :bar => "test")
  • 9. Treating Arguments as an Array def sum(*args) puts args[0] puts args[1] puts args[2] puts args[3] end sum(1,2,3) # 1 # 2 # 3 # nil
  • 10. Rails helper usage example # USAGE-1 without block <% link_to 'Posts list', posts_path, :class => 'posts' %> # USAGE-2 with block <% link_to posts_path, :class => 'posts' do %> <p>Posts list</p> <% end %>
  • 11. # Rails3's source code def link_to(*args, &block) if block_given? options = args.first || {} html_options = args.second link_to(capture(&block), options, html_options) else name = args[0] options = args[1] || {} html_options = args[2] html_options = convert_options_to_data_attributes(options, html_options) url = url_for(options) if html_options html_options = html_options.stringify_keys href = html_options['href'] tag_options = tag_options(html_options) else tag_options = nil end href_attr = "href="#{url}"" unless href "<a #{href_attr}#{tag_options}>#{ERB::Util.h(name || url)}</a>".html_safe end end
  • 12. ActiveSupport#extract_options! extract hash from *args def foobar(*args) options = args.extract_options! end foobar(1, 2) # options is {} foobar(1, 2, :a => :b) # options is { :a => :b }
  • 14. A trivial example 1 def call_block puts "start" yield(" foobar") puts "end" end call_block do |str| puts " here" puts str puts " here" end # start # here # foobar # here # end
  • 15. A trivial example 2 def call_block(&block) puts "start" block.call("foobar") puts "end" end call_block do |str| puts "here" puts str puts "here" end # start # here # foobar # here # end
  • 16. pre- and Post-processing usage example f = File.open("myfile.txt", 'w') f.write("Lorem ipsum dolor sit amet") f.write("Lorem ipsum dolor sit amet") f.close # using block File.open("myfile.txt", 'w') do |f| f.write("Lorem ipsum dolor sit amet") f.write("Lorem ipsum dolor sit amet") end
  • 17. pre- and Post- processing # without code block def send_message(msg) socket = TCPSocket.new(@ip, @port) # Pre- socket.puts(msg) response = socket.gets ensure socket.close # Post- end end
  • 18. # with code block def send_message(msg) connection do |socket| socket.puts("foobar") socket.gets end end def connection socket = TCPSocket.new(@ip, @port) # Pre- yield(socket) ensure socket.close # Post- end end
  • 19. Dynamic Callbacks Sinatra usage example get '/posts' do #.. show something .. end post '/posts' do #.. create something .. end put '/posts/:id' do #.. update something .. end delete '/posts/:id' do #.. annihilate something .. end
  • 20. Dynamic Callbacks server = Server.new server.handle(/hello/) do puts "Hello at #{Time.now}" end server.handle(/goodbye/) do puts "goodbye at #{Time.now}" end server.execute("/hello") # Hello at Wed Apr 21 17:33:31 +0800 2010 server.execute("/goodbye") # goodbye at Wed Apr 21 17:33:42 +0800 2010
  • 21. class Server def initialize @handlers = {} end def handle(pattern, &block) @handlers[pattern] = block end def execute(url) @handlers.each do |pattern, block| if match = url.match(pattern) block.call break end end end end
  • 22. Self Yield gemspec example spec = Gem::Specification.new spec.name = "foobar" spec.version = "1.1.1" # using block Gem::Specification.new do |s| s.name = "foobar" s.version = "1.1.1" #... end
  • 23. Self Yield gemspec example class Gem::Specification def initialize name = nil, version = nil # ... yield self if block_given? # ... end end
  • 25. A trivial example module Mixin def foo puts "foo" end end class A include Mixin end A.new.foo # foo
  • 26. obj.extend(Mod) Implementing class behavior module Mixin def foo puts "foo" class A end extend Mixin end end the same as A.extend(Mixin) class << A include Mixin A.foo # class method end
  • 27. obj.extend(Mod) Implementing per-object behavior module Mixin def foo puts "foo" end end a = "test" class << a the same as a.extend(Mixin) include Mixin a.foo # foo end b = "demo" b.foo # NoMethodError
  • 28. Dual interface module Logger extend self def log(message) $stdout.puts "#{message} at #{Time.now}" end end Logger.log("test") # as Loggerā€™s class method class MyClass include Logger end MyClass.new.log("test") # as MyClassā€™s instance method
  • 29. Mixin with class methods module Mixin def foo puts "foo" end module ClassMethods def bar puts "bar" end end end class MyClass include Mixin extend Mixin::ClassMethods end
  • 30. module Mixin # self.included is a hook method def self.included(base) base.extend(ClassMethods) end def foo puts "foo" end module ClassMethods def bar puts "bar" end end end class MyClass include Mixin end
  • 31. module Mixin def self.included(base) base.extend(ClassMethods) base.send(:include, InstanceMethods) end module InstanceMethods def foo puts "foo" end end module ClassMethods def bar puts "bar" end end end class MyClass include Mixin end
  • 33. ActiveRecord example class Person < ActiveRecord::Base end p1 = Person.find_by_name("ihower") p2 = Person.find_by_email("ihower@gmail.com")
  • 34. A trivial example car = Car.new car.go_to_taipei # go to taipei car.go_to_shanghai # go to shanghai car.go_to_japan # go to japan
  • 35. class Car def go(place) puts "go to #{place}" end def method_missing(name, *args) if name.to_s =~ /^go_to_(.*)/ go($1) else super end end end car = Car.new car.go_to_taipei # go to taipei car.blah # NoMethodError: undefined method `blah`
  • 36. Donā€™t abuse method missing ā€¢ method_missing? is slow ā€¢ only use it when you can not deļ¬ne method in advance ā€¢ Meta-programming can deļ¬ne methods dynamically
  • 37. XML builder example builder = Builder::XmlMarkup.new(:target=>STDOUT, :indent=>2) builder.person do |b| b.name("Jim") b.phone("555-1234") b.address("Taipei, Taiwan") end # <person> # <name>Jim</name> # <phone>555-1234</phone> # <address>Taipei, Taiwan</address> # </person>
  • 38. We need BlankSlate or BasicObject to avoid name conļ¬‚ict # Jim Weirich's BlankSlate from XML builder >> BlankSlate.instance_methods => ["__send__", "instance_eval", "__id__"] # Ruby 1.9 >> BasicObject.instance_methods => [:==, :equal?, :!, :! =, :instance_eval, :instance_exec, :__send__] # an easy BlankSlate class BlankSlate instance_methods.each { |m| undef_method m unless m =~ /^__/ } end >> BlankSlate.instance_methods => ["__send__", "__id__"]
  • 39. BlankSlate usage example class Proxy < BlankSlate def initialize(obj) @obj = obj end def method_missing(sym, *args, &block) puts "Sending #{sym}(#{args.join(',')}) to obj" @obj.__send__(sym, *args, &block) end end s = Proxy.new("foo") puts s.reverse # Sending reverse() to obj # "oof"
  • 41. ActiveSupport::Dependencies example (lazy class loading) ā€¢ Person ā€¢ Ruby calls const_missing ā€¢ const_missing calls Dependencies.load_missing_constant(Object, :Person) ā€¢ require or load person.rb in the list of load path
  • 42. Global constant class Module original_c_m = instance_method(:const_missing) define_method(:const_missing) do |name| if name.to_s =~ /^U([0-9a-fA-F]{4})$/ [$1.to_i(16)].pack("U*") else original_c_m.bind(self).call(name) end end end puts U0123 # Ä£ puts U9999 #
  • 43. Localized constant (you can use super here) class Color def self.const_missing(name) if name.to_s =~ /[a-zA-Z]/ const_set(name, new) else super end end end Color::RED #<Color:0x1018078a0> Color::GREEN #<Color:0x1018059d8>
  • 45. an Array example [1,1,2,3,3,4,5].uniq!.reject!{ |i| i%2 == 0 }.reverse # 5,3,1
  • 46. a trivial example class Demo def foo puts "foo" self end def bar puts "bar" self end def baz puts "baz" self end end Demo.new.foo.bar.baz # foo # bar # baz
  • 47. Object#tap Ruby 1.8.7 later puts "dog".reverse .tap{ |o| puts "reversed: #{o}" } # .upcase # reversed: god GOD
  • 48. Object#tap Ruby 1.8.7 later class Object def tap yield self self end end
  • 50. NilClass#try example person = Person.find_by_email(params[:email]) # but we don't know @person exists or not # Without try @person ? @person.name : nil # With try @person.try(:name)
  • 51. class NilClass def try(*args) nil end end
  • 52. Numeric#bytes example 123.kilobytes # 125952 456.megabytes # 478150656 789.gigabytes # 847182299136
  • 53. class Numeric KILOBYTE = 1024 MEGABYTE = KILOBYTE * 1024 GIGABYTE = MEGABYTE * 1024 def bytes self end alias :byte :bytes def kilobytes self * KILOBYTE end alias :kilobyte :kilobytes def megabytes self * MEGABYTE end alias :megabyte :megabytes def gigabytes self * GIGABYTE end alias :gigabyte :gigabytes end
  • 54. Object#blank? example [1,2,3].blank? # false "blah".blank? # false "".blank? # true class Demo def return_nil end end Demo.new.blank? # false Demo.new.return_nil.blank? # true
  • 55. class Object def blank? respond_to?(:empty?) ? empty? : !self end def present? !blank? end end class NilClass def blank? true end end class FalseClass def blank? true end end class TrueClass def blank? false end end
  • 56. sub-talk Ruby Object Model and Meta-programming
  • 57. self (current object) class Demo puts self # Demo def blah puts self # <Demo:0x10180f398> object [1,2,3].each do |i| puts self # <Demo:0x10180f398> object end end class AnotherDemo puts self # Demo::AnotherDemo end end
  • 58. Everything in Ruby is object, even class.
  • 59. Ruby Object Model class A Object end super class B < A end A obj = B.new super obj.class # B B.superclass # A class class B.class # Class obj B Class
  • 60. class object is an object of the class Class class A Object end super class B < A class end A obj = B.new class super obj.class # B B.superclass # A class class B.class # Class obj B Class class
  • 61. module class A end A module B end super module C Mixin end B,C class D < A super include B class include C obj D end
  • 62. whatā€™s metaclass? class A def foo Object end end super obj1 = A.new class obj2 A obj2 = A.new class obj2
  • 63. metaclass also known as singleton, eigenclass, ghost class, virtual class. class A every object has his own metaclass def foo end end Object obj1 = A.new super obj2 = A.new class def obj2.bar obj2 A # only obj2 has bar method, # called singleton method super end class obj2ā€™s obj2 metaclass # another way class << obj1 def baz #only obj1 has baz method end end P.S. well, number and symbol have no metaclass
  • 64. class object has his metaclass too. class A so the singleton method is class # way1 def self.foo method!! end # way2 class << self def bar class B end Class end end super class Aā€™s # way3 A metaclass def A.baz end A.foo A.bar A.baz
  • 65. If we deļ¬ne method inside class Class class Class def blah puts "all class has blah class method" end end class A end A.blah # all class has blah class method String.blah # all class has blah class method
  • 66. method deļ¬nition(current class) "def" deļ¬nes instance method for current class class Demo # the current class is Demo def foo puts "foo" end end Demo.new.foo # foo
  • 67. class << changes the method deļ¬nition(current class) class Demo class << self # the current class is Demo's metaclass def foo puts "foo" end end end Demo.foo # foo
  • 68. class << also changes the self (current object) class Demo puts self # Demo class << self puts self # Demo's metaclass end end "abc".metaclass
  • 69. We can get metaclass by using class<< class Object def metaclass class << self; self; end end end end "abc".metaclass String.metaclass # Ruby 1.9.2 "abc".singleton_class => #<Class:#<String:0x000001009e26f8>>
  • 70. method deļ¬nition mechanism self (current object) new scope? (current_class) class Foo Foo Foo yes class << Foo Fooā€™s Fooā€™s yes metaclass metaclass
  • 71. Meta-programming How to write code to write code?
  • 72. Two types of meta- programming ā€¢ Code Generation (Not talk about it today) ā€¢ eg. Rails scaffold ā€¢ Reļ¬‚ection ā€¢ eg. Class Macro (talk later)
  • 73. Speciļ¬cally, How to write a method to deļ¬ne method?
  • 74. class Demo # the current class is Demo def define_blah1 # the current class is Demo def blah1 puts "blah1" end end def self.define_blah2 # the current class is Demo (the same as above) def blah2 puts "blah2" end end end Demo.new.blah1 # NoMethodError: undefined method `blah1' Demo.new.define_blah1 Demo.new.blah1 # blah1 Demo.new.blah2 # NoMethodError: undefined method `blah2' Demo.define_blah2 Demo.new.blah2 #blah2
  • 75. And how to write a method to deļ¬ne singleton method?
  • 76. class Demo def define_blah1 # self is Demo's instance def self.blah1 puts "blah1" # define singleton method end end def self.define_blah2 #self is Demo def self.blah2 puts "blah2" # define singleton method (class method) end end end a = Demo.new a.define_blah1 a.blah1 # blah1 Demo.new.blah1 # NoMethodError: undefined method `blah1' Demo.new.blah2 # NoMethodError: undefined method `blah2' Demo.define_blah2 Demo.blah2 #blah2
  • 77. Not useful really, we need more dynamic power
  • 78. The key of power is variable scope!!
  • 79. Variable scope { module MyDemo var = 1 { class Demo var = 2 { def foo var = 3 end end end
  • 80. block variable scope var1 = "foo" [1,2,3].each do |i| puts var var1 = "bar" var2 = "baz" end puts var1 # foo puts var2 # NameError: undefined local variable or method
  • 81. deļ¬ne_method unlike ā€œdefā€, you can access outside variable!! class Demo # define instance methods ["aaa", "bbb", "ccc"].each do |name| define_method(name) do puts name.upcase end end # define class methods class << self ["xxx", "yyy", "zzz"].each do |name| define_method(name) do puts name.upcase end end end end Demo.new.aaa # AAA Demo.new.bbb # BBB Demo.yyy # YYY Demo.zzz # ZZZ
  • 82. deļ¬ne_method will deļ¬nes instance method for current object (itā€™s must be class object or module)
  • 83. class Class def define_more_methods # define instance methods ["aaa", "bbb", "ccc"].each do |name| Use class define_method(name) do puts name.upcase end end method to # define class methods class << self deļ¬ne methods ["xxx", "yyy", "zzz"].each do |name| define_method(name) do puts name.upcase (global) end end end end end class Demo define_more_methods end
  • 84. module Mixin module ClassMethods def define_more_methods # define instance methods ["aaa", "bbb", "ccc"].each do |name| define_method(name) do Use class puts name.upcase end end method to # define class methods class << self ["xxx", "yyy", "zzz"].each do |name| deļ¬ne methods define_method(name) do puts name.upcase end (localized) end end end end end class Demo extend Mixin::ClassMethods define_more_methods end
  • 85. So unlike ā€œdefā€, deļ¬ne_method will not create new scope
  • 86. So we maybe need those methods inside deļ¬ne_method: (because the scope is not changed) ā€¢ Object#instance_variable_get ā€¢ Object#instance_variable_set ā€¢ Object#remove_instance_variable ā€¢ Module#class_variable_get ā€¢ Module#class_variable_set ā€¢ Module#remove_class_variable
  • 87. Not dynamic enough? Because class << will create new scope still!!
  • 88. we need even more dynamic power
  • 89. var = 1 String.class_eval do puts var # 1 puts self # the current object is String # the current class is String def foo puts "foo" end def self.bar puts "bar" end class_eval (only for class object or module) class << self def baz puts "baz" end end end "abc".foo # foo String.bar # bar String.baz # baz
  • 90. class_eval + deļ¬ne_method name = "say" var = "itā€™s awesome" String.class_eval do define_method(name) do puts var end end "ihower".say # itā€™s awesome
  • 91. But how to deļ¬ne singleton method using class_eval and deļ¬ne_method?
  • 92. Wrong! name = "foo" var = "bar" String.class_eval do class << self define_method(name) do puts var end end end # ArgumentError: interning empty string # we can not get name and var variable, because class << create new scope
  • 93. Fixed! you need ļ¬nd out metaclass and class_eval it! name = "foo" var = "bar" metaclass = (class << String; self; end) metaclass.class_eval do define_method(name) do puts var end end String.foo # bar
  • 94. How about apply to any object? (because class_eval only works on class object or module)
  • 95. instance_eval for any object obj = "blah" obj.instance_eval do puts self # obj # the current class is obj's metaclass def foo puts "foo" end end obj.foo # singleton method
  • 96. how about class object? String.instance_eval do puts self # String # the current class is String's metaclass def foo puts "bar" end end String.foo # singleton method (class method)
  • 97. method deļ¬nition mechanism self (current object) new scope? (current_class) class Foo Foo Foo yes class << Foo Fooā€™s metaclass Fooā€™s metaclass yes Foo.class_eval Foo Foo no Foo.instance_eval Foo Fooā€™s metaclass no
  • 98. 8. Class Macro (Rubyā€™s declarative style)
  • 99. ActiveRecord example class User < ActiveRecord::Base validates_presence_of :login validates_length_of :login, :within => 3..40 validates_presence_of :email belongs_to :group has_many :posts end
  • 100. Class Bodies Arenā€™t Special class Demo a = 1 puts a def self.say puts "blah" end say # you can execute class method in class body end # 1 # blah
  • 101. Memorize example class Account def calculate @calculate ||= begin sleep 10 # expensive calculation 5 end end end a = Account.new a.caculate # need waiting 10s to get 5 a.caculate # 5 a.caculate # 5 a.caculate # 5
  • 102. memoize method class Account def calculate sleep 2 # expensive calculation 5 end memoize :calculate end a = Account.new a.calculate # need waiting 10s to get 5 a.calculate # 5
  • 103. class Class def memoize(name) original_method = "_original_#{name}" alias_method :"#{original_method}", name define_method name do cache = instance_variable_get("@#{name}") if cache return cache else result = send(original_method) # Dynamic Dispatches instance_variable_set("@#{name}", result) return result end end end end
  • 104. Itā€™s general for any class class Car def run sleep 100 # expensive calculation ā€œdoneā€ end memoize :run end c = Car.new c.run # need waiting 100s to get done c.run # done
  • 105. BTW, how to keep original method and call it later? ā€¢ alias_method ā€¢ most common way ā€¢ example above ā€¢ method binding ā€¢ can avoid to add new method ā€¢ example in const_missing
  • 106. 9.instance_eval DSL calls it create implicit context
  • 107. Rack example Rack::Builder.new do use Some::Middleware, param use Some::Other::Middleware run Application end
  • 108. How is instance_eval doing? ā€¢ It changes the ā€œselfā€ (current object) to caller ā€¢ Any object can call instance_eval (unlike class_eval)
  • 109. a trivial example class Demo def initialize @a = 99 end end foo = Demo.new foo.instance_eval do puts self # foo instance puts @a # 99 end
  • 110. instance_eval with block class Foo attr_accessor :a,:b def initialize(&block) instance_eval &block end def use(name) # do some setup end end bar = Foo.new do self.a = 1 self.b = 2 use "blah" use "blahblah" end
  • 111. Strings of Code eval*() family can accept string of code ā€¢ No editorā€™s syntax highlight ā€¢ Not report syntax error until be evaluated (in runtime!) ā€¢ Security problem
  • 113. anonymous class klass = Class.new def move_left puts "left" end def move_right puts "right" end end object = klass.new object.move_left # left Car = klass # naming it Car car = Car.new car.move_right # right
  • 114. variable scope matters you can access outside variable var = "itā€™s awesome" klass = Class.new puts var def my_method puts var # undefined local variable or method `var' end end puts klass.new.my_method
  • 115. var = "itā€™s awesome" klass = Class.new do puts var define_method :my_method do puts var end end puts klass.new.my_method # itā€™s awesome # itā€™s awesome
  • 116. Subclassing with a generator using Class.new def disable_string_class(method_name) Class.new(String) do undef_method method_name end end klass1 = disable_string_class(:reverse) a = klass1.new("foobar") a.reverse # NoMethodError klass2 = disable_string_class(:upcase) b = klass2.new("foobar") b.upcase # NoMethodError
  • 117. Parameterized subclassing Camping example module Camping::Controllers class Edit < R '/edit/(d+)' def get(id) # ... end end end
  • 118. Parameterized subclassing example def Person(name) if name == "ihower" Class.new do def message puts "good" end end else Class.new do def message puts "bad" end end end end
  • 119. class Foo < Person 'ihower' def name "foo" end end class Bar < Person 'not_ihower' def name "bar" end end f = Foo.new f.message # good! b = Bar.new b.message # bad!
  • 121. Story 1: DSL or NoDSL by JosĆ© Valim at Euruko 2010 http://blog.plataformatec.com.br/2010/06/dsl-or-nodsl-at-euruko-2010/
  • 122. a DSL class ContactForm < MailForm::Base to "jose.valim@plataformatec.com.br" from "contact_form@app_name.com" subject "Contact form" attributes :name, :email, :message end ContactForm.new(params[:contact_form]).deliver
  • 123. DSL fails class ContactForm < MailForm::Base to :author_email from { |c| "#{c.name} <#{c.email}>" } subject "Contact form" headers { |c| { "X-Spam" => "True" } if c.honey_pot } attributes :name, :email, :message def author_email Author.find(self.author_id).email end end
  • 124. NoDSL class ContactForm < MailForm::Base attributes :name, :email, :message def headers { :to => author_email, :from => "#{name} <#{email}>", :subject => "Contact form" } end def author_email Author.find(self.author_id).email end end
  • 125. Other examples ā€¢ Rake v.s. Thor ā€¢ RSpec v.s. Unit::test
  • 126. Rake example task :process do # do some processing end namespace :app do task :setup do # do some setup Rake::Task[:process].invoke end end rake app:setup
  • 127. Thor example class Default < Thor def process # do some processing end end class App < Thor def setup # do some setup Default.new.process end end thor app:setup
  • 128. JosĆ©ā€™s Conclusion ā€¢ DSL or NoDSL - don't actually have answer ā€¢ Replace "It provides a nice DSL" with "it relies on a simple contract".
  • 129. Story 2: Rails 2 to 3 API changes
  • 130. Routes nice DSL # Rails 2 map.resources :people, :member => { :dashboard => :get, :resend => :post, :upload => :put } do |people| people.resource :avatra end # Rails 3 resources :people do resource :avatar member do get :dashboard post :resend put :upload end end
  • 131. AR queries (1) method chaining # Rails 2 users = User.find(:all, :conditions => { :name => 'ihower' }, :limit => 10, :order => 'age') # Rails 3 users = User.where(:name => 'ihower').limit(20).order('age')
  • 132. AR queries (2) Unify ļ¬nders, named_scope, with_scope to Relation # Rails 2 users = User users = users.some_named_scope if params[:some] sort = params[:sort] || "id" conditions = {} if params[:name] conditions = User.merge_conditions( conditions, { :name => params[:name] } ) end if params[:age] conditions = User.merge_conditions( conditions, { :age => params[:age] } ) end find_conditions = { :conditions => conditions, :order => "#{sort} #{dir}" } sort = params[:sort] || "id" users = users.find(:all, :conditions => conditions, :order => sort )
  • 133. AR queries (2) Unify ļ¬nders, named_scope, with_scope to Relation # Rails 3 users = User users = users.some_scope if params[:some] users = users.where( :name => params[:name] ) if params[:name] users = users.where( :age => params[:age] ) if params[:age] users = users.order( params[:sort] || "id" )
  • 134. AR queries (3) Using class methods instead of scopes when you need lambda # Rails 3 class Product < ActiveRecord::Base scope :discontinued, where(:discontinued => true) scope :cheaper_than, lambda { |price| where("price < ?", price) } end # Rails 3, prefer this way more class Product < ActiveRecord::Base scope :discontinued, where(:discontinued => true) def self.cheaper_than(price) where("price < ?", price) end end
  • 135. AR validation (1) # Rails 2 class User < ActiveRecord::Base validates_presence_of :email validates_uniqueness_of :email validates_format_of :email, :with => /^[wd]+$/ :on => :create, :message => "is invalid" end # Rails 3 class User < ActiveRecord::Base validates :email, :presence => true, :uniqueness => true, :format => { :with => /^([^@s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})$/i } end http://asciicasts.com/episodes/211-validations-in-rails-3
  • 136. AR validation (2) custom validator # Rails 3 class User < ActiveRecord::Base validates :email, :presence => true, :uniqueness => true, :email_format => true end class EmailFormatValidator < ActiveModel::EachValidator def validate_each(object, attribute, value) unless value =~ /^([^@s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})$/i object.errors[attribute] << (options[:message] || "is not formatted properly") end end end
  • 137. ActionMailer # Rails 2 class UserMailer < ActionMailer::Base def signup(user) recipients user.email from 'ihower@gmail.com' body :name => user.name subject "Signup" end end UserMailer.deliver_registration_confirmation(@user)
  • 138. ActionMailer # Rails 3 class UserMailer < ActionMailer::Base default :from => "ihower@gmail.com" def signup(user) @name = user.name mail(:to => user.email, :subject => "Signup" ) end end UserMailer.registration_confirmation(@user).deliver
  • 139. I think itā€™s a Ruby APIs paradigm shift Apparently, Rails is the most successful Ruby open source codebase which you can learn from.
  • 140. References ā€¢ Ruby Best Practices, Oā€™Reilly ā€¢ The Ruby Object Model and Metaprogrammin, Pragmatic ā€¢ Programming Ruby 1.9, Pragmatic ā€¢ Metaprogramming Ruby, Pragmatic ā€¢ Advanced Rails, Oā€™Reilly ā€¢ The Building Blocks of Ruby http://yehudakatz.com/2010/02/07/the-building-blocks-of-ruby/ ā€¢ The Importance of Executable Class Bodies http://yehudakatz.com/2009/06/04/the-importance-of-executable-class-bodies/ ā€¢ Metaprogramming in Ruby: Itā€™s All About the Self http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/ ā€¢ Three implicit contexts in Ruby http://yugui.jp/articles/846