Adding Hash-Friendly Models – RubyMotion

Adding Hash-Friendly Models

Let’s start with the models. The model code should be independent of any other classes, so filling them in is usually a good place to start. Resist the temptation to make explicit references to your views or controllers!

First up are the Color objects. The Colr API returns its colors as JSON objects in the following form:

 {
 "timestamp"​​:​ 1285886579,
 "hex"​​:​ ​"ff00ff"​,
 "id"​​:​ 3976,
 "tags"​​:​ [{
 "timestamp"​: 1108110851,
 "id"​: 2583,
 "name"​: ​"fuchsia"
  }]
 }

Right away, it looks like we need timestamp, hex, id, and tags properties. In particular, tags represents a has-many relationship with Tag objects.

Let’s get down to business. Open color.rb and start filling it in with our hash-based model template from Preparing Scalable Models.

 class​ Color
  PROPERTIES = [​:timestamp​, ​:hex​, ​:id​, ​:tags​]
 attr_accessor​ *PROPERTIES
 def​ initialize(hash = {})
  hash.each { |key, value|
 if​ PROPERTIES.member? key.to_sym
  self.send((key.to_s + ​"="​).to_s, value)
 end
  }
 end

Now we need to fix tags to be an array and coerce its contents into Tag objects. You should finish the definition of Color like this:

 def​ tags
  @tags ||= []
 end
 def​ tags=(tags)
 if​ tags.first.is_a? Hash
  tags = tags.collect { |tag| Tag.new(tag) }
 end
  tags.each { |tag|
 if​ not tag.is_a? Tag
 raise​ ​"Wrong class for attempted tag ​​#{​tag.inspect​}​​"
 end
  }
  @tags = tags
 end
 end

We override #tags to guarantee that it returns an Array even if no values have been given. This makes logical sense given the has-many relationship and allows us to not worry about checking for nil elsewhere.

The tags= setter ensures that every object in tags will be a Tag object if possible. We could just accept an array of hashes straight from the JSON, but then we lose the ability to perform straightforward KVO on those objects. Since we’re on the subject, it’s a good idea to start working on the Tag implementation.

As you saw in the JSON for colors, the API returns tags in the following form:

 {
 "timestamp"​​:​ 1108110851,
 "id"​​:​ 2583,
 "name"​​:​ ​"fuchsia"
 }

We’ll need timestamp, id, and name properties but no special relationships this time. In tag.rb, just use the scalable model template with those attributes to create our Tag class.

 class​ Tag
  PROPERTIES = [​:timestamp​, ​:id​, ​:name​]
 attr_accessor​ *PROPERTIES
 def​ initialize(hash = {})
  hash.each { |key, value|
 if​ PROPERTIES.member? key.to_sym
  self.send((key.to_s + ​"="​).to_s, value)
 end
  }
 end
 end

That’s all for our models. Pretty painless, right? Using these hash-friendly implementations lets us easily instantiate objects using the server’s JSON. Now on to the meat of our app, the controllers.