send in the clowns

Posted by fakebenjay on May 14, 2017

Flatiron’s been over for two weeks now, and I’ve enjoyed the opportunity to spend time reviewing some foundational Ruby and tinkering around with some new libraries. I learned the basics of scraping with Nokogiri and OpenURI, both essential for pulling and organizing online data (or at least until every website in the world has a well-organized and intuitive API with no rate limits.)

One interesting challenge came in the final exercise, which involved scraping data about students from a web page, and then creating Ruby objects based on various attributes about them. Doing so required using the #send method, which I had barely used previously.

Ruby’s documentation on #send is a bit unclear, but it has the power to dynamically set object attributes, incredibly useful for people who create objects and hate typing long lists of things.

If we want to instantiate an object in the Student class based on scraped hash data, #send is key to establishing its attributes.

  def initialize(student_hash)
    student_hash.each do |attribute, value|
      self.send("#{attribute}=", value)
    end

    @@all << self
  end

By passing in a student hash that looks something like this…

{
        name: "Homer Simpson",
	age: 36,
	hometown: "Springfield",
	school: "Krusty's Clown College",
	weaknesses: ["riding the tiny bicycle around the loop", "encounters with Fat Tony"],
	strengths: ["donuts", "beer"]
}

…we can iterate through each item, and set our Student’s attributes en masse.

self.send("name=", "Homer Simpson")
self.send("age=", 36)
self.send("hometown=", "Springfield")

etc. etc. etc.

The best way to think about #send is as if it were a kind of string interpolation. You take your category type, “category=”, and the relevant attribute, “attribute”, and send nicely interprets that as “@category=attribute,” which will become “{category: attribute}” upon instantiation.

The method above works largely the same way as the ones immediately below this sentence, but with considerably more flexibility to set attribute types. You don’t have to dynamically set the attribute names, but it’s pretty nice to be able to. (Note: the first argument in #send can be either a string or symbol. Ruby will automatically convert the string into a symbol.)

  def initialize(name, age, hometown, school, weaknesses, strengths)
    self.send("name=", name)
		self.send("age=", age)
		self.send("hometown=", hometown)
		self.send("school=", school)
		self.send("weaknesses=", weaknesses)
		self.send("strengths=", strengths)
  end
	
	def initialize(name, age, hometown, school, weaknesses, strengths)
    @name=name
		@age=age
		@hometown=hometown
		@school=school
		@weaknesses=weaknesses
		@strengths=strengths
  end

And if we want to give our student additional attributes after he/she has been instantiated…

{
       spouse: {
	       name: "Marge Simpson",
		age: 34,
		hometown: "Springfield",
		school: "Springfield High School",
		weaknesses: ["http://www.avclub.com/article/bojack-horseman-creator-tweets-heartbreaking-ode-m-241156"],
		strengths: ["mothering", "hair", "Tennessee Williams"]
	}
	children: [
	  {
		       name: "Bart Simpson",
			age: 10,
			hometown: "Springfield",
			school: "Springfield Elementary",
			weaknesses: ["academics"],
			strengths: ["hellraising", "feeding shorts to others", "The Bartman"]
		}, 
		{
			name: "Lisa Simpson",
			age: 8,
			hometown: "Springfield",
			school: "Springfield Elementary",
			weaknesses: ["popularity"],
			strengths: ["saxophone", "academics", "general excellence"]
		}, 
		{
			name: "Maggie Simpson",
			age: 1,
			hometown: "Springfield",
			school: "Ayn Rand School for Tots",
			weaknesses: ["talking"],
			strengths: ["pacifier use", "organizing infant espionage"]
		}
	]
}

…doing so just requires calling a modified version of our #initialize method.

  def add_student_attributes(attributes_hash)
    attributes_hash.each do |attribute, value|
      self.send("#{attribute}=", value)
    end

    return self
  end

Now get out there and instantiate some objects!