Monday, February 24, 2014

Week 4 Day 1: Active Listening and ActiveRecord

Major Activities of the Day: Lecture. We were introduced to Rake in the morning, did a lab putting together some Rake tasks, and then at 10AM moved into the classroom for lecture. We got out at 5PM.

Well, to be fair, it wasn't without any breaks. We stopped for lunch, got a 45-minute sanity break around 2:45, and had a 7-minute break towards the end. But it was a marathon of sitting in a room with the lights off and a projector. I got really sleepy.

So what happened during lecture, exactly? First, we went over the lab from the weekend. Not surprisingly, as it was a difficult lab, it took a really long time. Hours and hours. But it was a good opportunity to watch TDD in action; this lab had some vague instructions and no tests, so he had to write the tests, make them pass, and then refactor. We watched methods broken down into simpler methods and code DRYed up.

When that was finally finished, we moved on to Rake and ActiveRecord. We got a glimpse of the awesome power of ActiveRecord, and I'm excited to use it in the future, saving myself all the frustration of the last few days' work with databases.

Finally, we got an assignment to play around with ActiveRecord. I discovered that if you take a YAML file and load it into Ruby, it generates objects, but the objects have never passed through the initialize method. So in order to get objects that have been initialized properly, you have to do this: `Class_name.create(object.as_json)`, which will initialize and save a new instance with the object's properties.

Remember my fancy method_missing from W3D4? Well, I discovered a better way to write it!  Here it is, with slightly different functionality (I'll explain, don't worry!):

def method_missing(method_name, *args)
  attribute = "@#{method_name.to_s}"
  if attribute.end_with?("=")
    instance_variable_set(attribute[0..-2], args[0])
  else
    instance_variable_get(attribute)
  end
end

And that's it!  This method creates an instance variable setter and getter for anything you want!  And unlike before, it doesn't use "eval", which is a supremely dangerous method (because it can let users have direct access to your system - bad idea!).  Here's me playing around with it in IRB:


2.0.0-p353 :001 > class Person
2.0.0-p353 :002?>   def method_missing(method_name, *args)
2.0.0-p353 :003?>       attribute = "@#{method_name.to_s}"
2.0.0-p353 :004?>       if attribute.end_with?("=")
2.0.0-p353 :005?>           instance_variable_set(attribute[0..-2], args[0])
2.0.0-p353 :006?>         else
2.0.0-p353 :007 >             instance_variable_get(attribute)
2.0.0-p353 :008?>         end
2.0.0-p353 :009?>     end
2.0.0-p353 :010?>   end
 => nil 
2.0.0-p353 :011 > person = Person.new
 => #<Person:0x000001011587e8> 
2.0.0-p353 :012 > person.height = '5\'11"'
 => "5'11\"" 
2.0.0-p353 :013 > puts person.height
5'11"
 => nil 
2.0.0-p353 :014 > person.happy? = true
SyntaxError: (irb):14: syntax error, unexpected '='
person.happy? = true
               ^
from /Users/flatironschool/.rvm/rubies/ruby-2.0.0-p353/bin/irb:12:in `<main>'
2.0.0-p353 :015 > person.happy = true
 => true 
2.0.0-p353 :016 > person.happy
 => true 
2.0.0-p353 :017 > person.favorite_author = "Ernest Hemingway"
 => "Ernest Hemingway" 
2.0.0-p353 :018 > person.favorite_author
 => "Ernest Hemingway" 
2.0.0-p353 :019 > person.favorite_book
 => nil 

So apparently our person can hold any variable we want.  Which is great, because people are multifaceted, and no person's variables will be quite like another's.  But you usually don't want to do this.

Also, note that `person.happy? = true` raised an error.  This is because Ruby doesn't allow happiness.

No, I'm just kidding!  Matz says that Ruby is all about making people happy.  The problem is that variable names can only include alphanumeric characters and underscores.  That means no punctuation.  Only method names can have that.  If I wanted to let you extract boolean values by querying with a question mark, here's an even fancier method_missing:


def method_missing(method_name, *args)
  attribute = "@#{method_name.to_s}"

  if attribute.end_with?("=")

    instance_variable_set(attribute[0..-2], args[0])
  elsif attribute.end_with?("?")
    !!instance_variable_get(attribute[0..-2])
  else
    instance_variable_get(attribute)
  end
end

And back to IRB:


2.0.0-p353 :001 > class Person

2.0.0-p353 :002?>   def method_missing(method_name, *args)
2.0.0-p353 :003?>       attribute = "@#{method_name.to_s}"
2.0.0-p353 :004?>       if attribute.end_with?("=")
2.0.0-p353 :005?>           instance_variable_set(attribute[0..-2], args[0])
2.0.0-p353 :006?>       elsif attribute.end_with?("?")
2.0.0-p353 :007?>           !!instance_variable_get(attribute[0..-2])
2.0.0-p353 :008?>       else
2.0.0-p353 :009 >            instance_variable_get(attribute)
2.0.0-p353 :010?>       end
2.0.0-p353 :011?>   end
2.0.0-p353 :012?> end
 => nil 
2.0.0-p353 :013 > person = Person.new
 => #<Person:0x0000010128e0e0> 
2.0.0-p353 :014 > person.happy = true
 => true 
2.0.0-p353 :015 > person.happy?
 => true 
2.0.0-p353 :016 > person.cool = false
 => false 
2.0.0-p353 :017 > person.cool?
 => false 
2.0.0-p353 :018 > person.employment = nil
 => nil 
2.0.0-p353 :019 > person.employment?
 => false 
2.0.0-p353 :020 > person.employment = "computer programmer"
 => "computer programmer" 
2.0.0-p353 :021 > person.employment
 => "computer programmer" 
2.0.0-p353 :022 > person.employment?
 => true 

Notice how I used the double-bang (`!!`) to force a truth value out of the variable, even if it's not a boolean.

It's amazing how a little IRB can add lots of length to an otherwise medium-sized blog post! :)

Skills developed: Rake, ActiveRecord, importing YAML files

No comments:

Post a Comment