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

Advanced Ruby

Advanced Ruby

My afternoon training session at LSRC 2013

Code samples are online at https://gist.github.com/anthonylewis

Anthony Lewis

July 18, 2013
Tweet

More Decks by Anthony Lewis

Other Decks in Programming

Transcript

  1. About Me • Senior Engineer at Mass Relevance • @anthonylewis

    [email protected] • http://anthonylewis.com Thursday, July 18, 13
  2. Session Overview • Installation • Ruby Object Model • More

    About Classes • Modules / Mixins • Metaprogramming • Real-World Examples Thursday, July 18, 13
  3. Windows • Planning to use Rails? • Use Rails Installer

    • http://railsinstaller.org • Use Ruby Installer • http://rubyinstaller.org Thursday, July 18, 13
  4. Mac OS X • Planning to use Rails? • Use

    Rails Installer • http://railsinstaller.org • Use Homebrew? • brew install ruby • Try RVM Thursday, July 18, 13
  5. Linux • Check your package system • Does it have

    Ruby 1.9 or later? • Enjoy building from source? • Try RVM Thursday, July 18, 13
  6. Ruby Version Manager • For Mac OS X and Linux

    • Install multiple versions of Ruby • Builds from source • Dev Tools Required • http://rvm.io Thursday, July 18, 13
  7. A Simple Class class Person def initialize(name) @name = name

    end def greet puts "Hi, I'm #{@name}" end end Thursday, July 18, 13
  8. A Simple Class class Person def initialize(name) @name = name

    end def greet puts "Hi, I'm #{@name}" end end Thursday, July 18, 13
  9. • Calling a method is also sending a message send

    p.greet => "Hi, I'm Tony" p.send :greet => "Hi, I'm Tony" Thursday, July 18, 13
  10. • You’ve used class variables and instance variables, but have

    you ever used class instance variables? • Class instance variables look like regular instance variables used in a class method • Class instance variables are not shared between subclasses Class Instance Variables Thursday, July 18, 13
  11. • Here’s a question I love to ask when interviewing

    a Ruby developer: • “How would you implement your own Record class with a before_save callback like the one in ActiveRecord?” Class Instance Variables Thursday, July 18, 13
  12. Class Instance Variables class User < Record before_save :saving def

    saving puts "Saving..." end end Thursday, July 18, 13
  13. • Since before_save is a class method, your first instinct

    is to use a class variable to store a list of methods to call. • Since class variables are shared among subclasses, all subclasses will share the same list of before_save callbacks. Class Instance Variables Thursday, July 18, 13
  14. Class Instance Variables class Record def self.before_save(*methods) @callbacks ||= []

    @callbacks += methods end def self.callbacks @callbacks end Thursday, July 18, 13
  15. Class Instance Variables def save self.class.callbacks.each do |c| send c

    if respond_to? c end puts "Saved" end end Thursday, July 18, 13
  16. • Also known as an Eigenclass • An anonymous class

    for holding singleton methods • A singleton method is a method defined on an instance Singleton Class Thursday, July 18, 13
  17. Singleton Class superman = Person.new("Clark") => superman.greet => "Hi, I'm

    Clark" def superman.fly puts "Up, up and away!" end Thursday, July 18, 13
  18. • The method fly is store in superman’s eigenclass •

    It is not available to any other instance of the class Person Singleton Class superman.fly => "Up, up and away!" Thursday, July 18, 13
  19. • Add module methods as instance methods Include class Person

    include Speaker end p.speak => "Hello" Thursday, July 18, 13
  20. • Add module methods as class methods Extend class Person

    extend Learner end Person.can_learn? => true Thursday, July 18, 13
  21. • Called when a module is included in a class

    Included module Coder def self.included(base) base.extend ClassMethods end # continued... Thursday, July 18, 13
  22. Included def code(lines = 3) "Ruby" * lines end module

    ClassMethods def can_code? true end end end Thursday, July 18, 13
  23. Prepend • Include module methods before methods defined in the

    class class Fibonacci def calc(n) return n if n < 2 return calc(n - 1) + calc(n - 2) end end Thursday, July 18, 13
  24. • Calling super will call the method defined in the

    class Prepend module Memoize def calc(n) @@memo ||= {} @@memo[n] ||= super end end Thursday, July 18, 13
  25. Prepend class Fibonacci prepend Memoize def calc(n) return n if

    n < 2 return calc(n - 1) + calc(n - 2) end end Thursday, July 18, 13
  26. class_eval • Evaluate a code string as if it were

    typed directly into the class definition. • class_eval creates instance methods Thursday, July 18, 13
  27. Note: Ruby provides this functionality out of the box with

    attr_reader, attr_writer, and attr_accessor. Thursday, July 18, 13
  28. • Create a method at runtime define_method class Person define_method

    :punch do |arg| "Ouch!" * arg end end Thursday, July 18, 13
  29. • Called when a method is not found method_missing class

    Person def method_missing method, *args "What's a #{method}?" end end Thursday, July 18, 13
  30. Rails Attributes • Every ActiveRecord object stores the values for

    its attributes in a private variable called @attributes • ActiveRecord also adds methods for accessing each attribute such as title, title=, title?, etc. Thursday, July 18, 13
  31. Rails Attributes p = Post.new p.title = "Hello, World" p.title

    => "Hello, World" Thursday, July 18, 13
  32. Rails Dynamic Finders • Find a record using an attribute’s

    value p = Post.find_by_title("My Cat") Thursday, July 18, 13
  33. Rails Dynamic Finders def method_missing(meth, *args) if meth.to_s =~ /^find_by_(.+)$/

    attr = $1.split('_and_') find_by_attributes(attr, *args) else super end end Thursday, July 18, 13
  34. Rails Dynamic Finders def find_by_attributes(attr, *args) conditions = Hash[attr.map {

    |a| [a, args[attr.index(a)]] }] where(conditions).first end Thursday, July 18, 13
  35. Method Decorators • We’re working on software for a bank

    • They want to track every time the deposit method is called in their Account class • But, of course, we are not allowed to change code inside the deposit method Thursday, July 18, 13
  36. Method Decorators class Account def initialize(balance) @balance = balance end

    def deposit(n) @balance += n puts "Balance is #{@balance}" end end Thursday, July 18, 13
  37. Method Decorators • We decide to track method calls by

    adding a module with a method decorator • Our decorator is a class method that takes an instance method parameter and prints a message every time it is called Thursday, July 18, 13
  38. module TrackMethods def track(meth) self.class_eval do alias_method "old_#{meth}", meth define_method

    meth do |*args| puts "Calling #{meth} " + "with #{args.join(', ')}" self.send "old_#{meth}", *args end end end end Thursday, July 18, 13
  39. Method Decorators class Account extend TrackMethods # deposit is not

    changed track :deposit end Thursday, July 18, 13
  40. Method Decorators a = Account.new(10) => a.deposit 5 => "Calling

    deposit with 5" => "Balance is now 15" Thursday, July 18, 13
  41. User Features • Our boss would like the ability to

    define features that different users can use • He wants to be able to assign features and check them easily • In a real application, we would store these features in a database table • Instead we will store then in a Ruby Set Thursday, July 18, 13
  42. require 'set' class User def initialize @features = Set.new end

    # continued... User Features Thursday, July 18, 13
  43. def method_missing(meth, *args) if meth.to_s =~ /^can_(.*)\?$/ @features.include? $1.to_s elsif

    meth.to_s =~ /^can_(.*)$/ @features << $1.to_s self else super end end end Thursday, July 18, 13
  44. Learn More Ruby • Programming Ruby 1.9 & 2.0 •

    The PickAxe Book • by Dave Thomas, with Chad Fowler and Andy Hunt • Metaprogramming Ruby • by Paolo Perrotta Thursday, July 18, 13
  45. Go Ruby Crazy • why’s (poignant) Guide to Ruby •

    by why the lucky stiff Thursday, July 18, 13
  46. Learn Onscreen • Try Ruby • http://tryruby.org • Ruby Koans

    • http://rubykoans.com • Ruby has damaged your karma. Thursday, July 18, 13