Intro To Advanced Ruby

3,055 views
2,880 views

Published on

Intro talks never let you learn about the things that make a language truly cool. In this talk we'll discover how advanced features of Ruby help us write cleaner more modular code.

Published in: Technology
0 Comments
5 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,055
On SlideShare
0
From Embeds
0
Number of Embeds
57
Actions
Shares
0
Downloads
51
Comments
0
Likes
5
Embeds 0
No embeds

No notes for slide
  • Brian P. Hogan\n
  • Ruby is a language that we can bend to our will\n
  • It can be easy to read\n
  • We can call methods as strings, etc\n
  • \n
  • \n
  • \n
  • \n
  • Require loads in another Ruby file. It’s like import or include in other languages. \n
  • We want to call the ‘today’ method on the Date class to get the current date by using the system time. This method isn’t actually loaded for us by default. We require the ‘date’ library which adds those features to the language.\n
  • Unit testing in Ruby is built in. All we have to do is include the testing library.\n
  • To use tests in Ruby, we only need to require the testing library and define our methdos with a special naming convention. Let’s use a test to drive the development of a function that will tell us if a person is allowed to drive. We don’t write the code. We have no idea how the code will work, but we do know that if a person is under 16 then they cannot drive. So in our test, we create a new Person, set the person’s age to 15, and then we call a method called “can_drive?” which we assert should not be true. If the assert statement returns true, the test passes.\n
  • Our person class isn’t defined, so when we run this test, it fails. That’s ok. This is how we do test-driven development in Ruby. We writea simple test, then we write the class to make the test pass.\n
  • So here’s the class. We use attr_accessor to create a getter and setter for age, then we write the can_drive? method to ensure that the age is greater than or equal to 16. Remember that in Ruby, the return value of a fuction is implicit - the last evaluated statement is the return value.\n
  • Now our test passes and we can repeat this process each time we add a new feature.\n
  • Now let’s talk about classes and objects. In many languages, a class is a blueprint for an object. In Ruby, that’s kind of true, but you should really think of classes AS objects. That means classes can also have methods.\n
  • Instance methods are defined on the instance scope. To call them, we need to create an instance of the class to cal the method. This is the most common method. Class methods are methods we define on the class object itself. We can use the self. prefix to attach the method to the class object instead of the object instance. This is how we define the equivalent of a static method.\n
  • But the idea of methods is something of a Java thing. Under the hood, Ruby is really a message passing language. Method calls are transated into messages, and are basically a conveience layer.\n
  • We can use that to our advantage when writing more complex programs. send lets us call methods as a string. We can use respond_to? to ask if the method exists!\n
  • \n
  • \n
  • We can use respond_to? to ask if the method exists!\n
  • Lambdas let us execute code later. Sometimes we can't evaluate code right at runtime. Instead we need to context of other code instead.\n
  • \n
  • We use blocks all the time when we iterate\n
  • We can also use blocks to wrap other code. Let's build a navigation bar\n
  • We can also use blocks to wrap other code. Let's build a navigation bar\n
  • \n
  • The lambdas get evaluated when we fire the .call method. So here we declare a lambda and we take in one variable called “phrase”. We timestamp it. We pass the variables we want to the .call method and the time is displayed with a different value each time. We’re not actually running the code in the lambda until later.\n
  • We can reopen classes\n
  • In Ruby, we can reopen any class we want, even ones in the standard library. We can then make changes to the methods there. \n
  • This is the surest way to shoot yourself, and your team, in the face. \n
  • We still need to modify core classes at times. The Rails framework couldn’t exist without some patches like this, and modifying classes and objects is the way we write modular code.\n
  • Modules let us extend objects without inheritance.\n
  • A ninja is a person. Just because a person is now a ninja does not mean they are not a person anymore.\n
  • We use include to add the module's methods to the instance. In this case, every Person is now a Ninja. \n
  • We use the extend keyword to add the module's methods as class methods. Remember, classes are objects too. When we extend, we add the methods to the object.\n
  • \n
  • An instance is also an object. So instead of adding the Ninja module to the Person class, we can add it to just specific instances using extend, which is just a method that adds methods to the object.\n
  • Now each instance is independant and we can add our modules to each one without affecting the others.\n
  • This callback lets us hook into the class that includes the module. So getting back to our “blank” callback, let’s say we wanted all of our objects in our application to be able to have our “blank” method on it. Now we have a completely self-contained plugin that does not open any classes. This is how we cleanly modify Ruby code.\n
  • One really nice feature of self.included is that we can call any class methods of the class that includes the module. So we can make behaviors. This lets us separate our concerns.\n
  • Modules are inserted into the object's inheritence chain. When\nwe call a method, Ruby looks first in the object. If the method\nisn't found there, it looks at the parent object, and then it looks\nat the included modules. \n\n
  • class_eval lets us declare methods on the class. We can use it inside of self.included to safely override existing methods.\n
  • But these weird concepts let us organize our code in ways that don’t paint us into a corner later. We can make plugins that, just by adding a file to our app, we seamlessly prevent records from being deleted and instead just mark them as updated. \n
  • We’ll use all of these concepts to drive the development of an extension that lets us declare callbacks that should be run before we save an object. Assume the save method saves the data to some data store somewhere.\n
  • \n
  • \n
  • Intro To Advanced Ruby

    1. 1. Intro to Advanced Rubytwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    2. 2. What makes Ruby special?twitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    3. 3. Its expressive 5.times do puts "Hello World" endtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    4. 4. Its dynamic class Person [:first_name, :last_name, :gender].each do |method| attr_accessor method end endtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    5. 5. Its extendable module KeywordSearch def find_all_by_keywords(keywords) self.where(["name like ?", "%" + keywords + "%"]) end end class Project < ActiveRecord::Base extend KeywordSearch end class Task < ActiveRecord::Base extend KeywordSearch endtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    6. 6. Its flexible def add(*args) args.reduce(0){ |result, item| result + item} end add 1,1,1,1twitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    7. 7. Its dangerous! class String def nil? self == "" end endtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    8. 8. Our roadmap for today • Requiring files • Testing • Message passing • Classes vs Instances • Blocks and Lambdas • Monkeypatching • Modulestwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    9. 9. requiretwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    10. 10. require more functionality Date.today.to_s NoMethodError: undefined method today for Date:Class require date => true Date.today.to_s => "2011-04-09"twitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    11. 11. Testingtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    12. 12. A simple Unit test require test/unit class PersonTest < Test::Unit::TestCase def test_person_under_16_cant_drive p = Person.new p.age = 15 assert !p.can_drive? end endtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    13. 13. Make it fail first... Loaded suite untitled Started E Finished in 0.000325 seconds.   1) Error: test_person_under_16_cannot_drive(PersonTest): NameError: uninitialized constant PersonTest::Person method test_person_under_16_cannot_drive in untitled document at line 6 1 tests, 0 assertions, 0 failures, 1 errorstwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    14. 14. Now write the code class Person attr_accessor :age def can_drive? self.age >= 16 end endtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    15. 15. And pass the test! Loaded suite untitled Started . Finished in 0.00028 seconds. 1 tests, 1 assertions, 0 failures, 0 errorstwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    16. 16. Classes and Objects • A Ruby Class is an object of type Class • An Object is an instance of a Class • Classes can have methods just like objects • These are often used as static methods.twitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    17. 17. Instance vs class methods class Person class Person def sleep def self.sleep puts "ZZZZZZ" puts "ZZZZZZ" end end end end p = Person.new Person.sleep p.sleep "ZZZZZZ" "ZZZZZZ" => nil => niltwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    18. 18. Message passingtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    19. 19. .send class Person p = Person.new def name @name p.send(:name) end def name=(input) p.send(:name=, "Homer") @name = input end endtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    20. 20. We reduce complexity with this. class Sorter class Sorter def move_higher def move_higher puts "moved one higher" puts "moved one higher" end end def move_lower def move_lower puts "moved lower" puts "moved lower" end end def move(type) def move(type) case type self.send "move_" + type when "higher" then move_higher end when "lower" then move_lower end end end endtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    21. 21. Then we can add more methods. class Sorter def move_higher puts "moved one higher" end def move_lower puts "moved lower" end def move_top puts "moved to the top" end def move_bottom puts "moved to the bottom" end def move(type) self.send "move_" + type end endtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    22. 22. Send and respond_to class Person attr_accessor(:name) end p = Person.new p.send(:name) if p.respond_to?(:name) p.send(:age) if p.respond_to?(:age)twitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    23. 23. Blocks and Lambdastwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    24. 24. Blockstwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    25. 25. Iteration people.each do |person| puts person.name end people.select do |person| person.last_name == "Hogan" end adults = people.reject do |person| person.age <= 18 endtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    26. 26. Encapsulation result = wrap(:p) do "Hello world" end puts result => "<p>Hello world</p>” def wrap(tag, &block) output = "<#{tag}>#{yield}</#{tag}>" endtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    27. 27. yield can bind objects! navbar = Navbar.create do |menu| menu.add "Google", "http://www.google.com" menu.add "Amazon", "http://www.amazon.com" end class Menu class Navbar def self.create(&block) def add(name, link) menu = Menu.new @items ||= [] yield menu @items << "<li><a href=#{link}>#{name}</a></li>" menu.to_s(options) end end end def to_s(options) menu = @items.join("n") "<ul>n#{menu}n</ul>" end endtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    28. 28. Procs and Lambdastwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    29. 29. Delayed Execution a = lambda{|phrase| puts "#{phrase} at #{Time.now}" } puts a.call("Hello") sleep 1 puts a.call("Goodbye") Hello at Sat Apr 09 13:34:07 -0500 2011 Goodbye at Sat Apr 09 13:34:08 -0500 2011twitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    30. 30. Monkeypatchingtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    31. 31. Reopen a class! class Object def blank? self.nil? || self.to_s == "" end def orelse(other) self.blank? ? other : self end endtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    32. 32. Monkeypatching is an evil process!twitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    33. 33. Instead, we use Modulestwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    34. 34. Modules module OrElseMethods def blank? self.nil? || self.to_s == "" end def orelse(other) self.blank? ? other : self end endtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    35. 35. In Ruby, we don’t care about the type... we care about how the object works.twitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    36. 36. Including modules on objects module NinjaBehaviors def attack puts "Youve been killed silently!" end end class Person include NinjaBehaviors end p = Person.new p.attacktwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    37. 37. Extending classes with modules module NinjaCreator def new_ninja puts "Ive created a new Ninja" end end class Person extend NinjaCreator end Person.new_ninjatwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    38. 38. Person vs PersonNinjaRockStarAdmintwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    39. 39. Extending objects with modules module NinjaBehaviors def attack puts "Youve been killed silently!" end end module RockStarBehaviors def trash_hotel_room puts "What a mess!" end endtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    40. 40. Extending objects with modules ninja = Person.new ninja.extend NinjaBehaviors ninja.attack rockstar = Person.new rockstar.extend RockStarBehaviors rockstar.trash_hotel_room rockstar.extend NinjaBehaviors rockstar.attacktwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    41. 41. Inject code without reopening! module OrElseMethods def blank? self.nil? || self.to_s == "" end def orelse(other) self.blank? ? other : self end end Object.send :include, OrElseMethodstwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    42. 42. self.included class access module UserValidations def self.included(base) base.validates_presence_of :email, :login base.valudates_uniqueness_of :email, :login base.validates_confirmation_of :password end end class User < ActiveRecord::Base include UserValidations endtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    43. 43. Modules can’t contain methods that overwrite existing methods!twitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    44. 44. self.included + class_eval class Person module NoSave def save puts "Original save" def self.included(base) true base.class_eval do end def save end puts "New save" false end end end end Person.send :include, NoSavetwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    45. 45. Yes, that’s weird.twitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    46. 46. Putting it all together • Implement “before save” behavior - when we call “save” on our object, we want to declare other methods we want to hook in. class Person before_save, :foo, lambda{|p| p.name = "test"}, SuperFilter endtwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    47. 47. Demo https://github.com/napcs/intro_to_advanced_rubytwitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114
    48. 48. Questions?twitter: bphoganemail: brianhogan at napcs.comhttp://spkr8.com/t/7114

    ×