23e6178f295b9cb7473d44d9e501a2b3

When you work with APIs that return JSON response, its common to have field named "type".

The problem is that "type" is a reserved word in ruby. As soon as you transform your hash into an object with that type method collision happen, type will return the class name.
(this method is depreciated for class instead, but the problem is still the same even if its less common to see "class" field in APIs)

I'm using a simple gsub to fix the problem and access the field, but as you can see, its neither robust or elegant.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyFancyClass
  def initialize(hash)
...
  end
end

@json = '{ "type": "world", "message": "hello" }'

@json.gsub!("type", "mtype")

post = JSON.parse(@json)
test = MyFancyClass.new(post)

do_something(test.mtype)

Refactorings

No refactoring yet !

36ad787d1b5306e65e89b441e0cade8f

Dragan Cvetinovic

June 17, 2010, June 17, 2010 09:00, permalink

No rating. Login to rate!

One way to solve this is by using "BlankSlate" technique :
"BlankSlate provides an abstract base class with no predefined
methods (except for __send__ and __id__(*)).
BlankSlate is useful as a base class when writing classes that
depend upon method_missing (e.g. dynamic proxies)."
(for more details see for instance BlankSlate implementation in hpricot gem by Jim Weirich)

(*) or whatever method is excepted by your implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class BlankSlate
  class << self
    def hide(name)
      undef_method name if instance_methods.include?(name) and name !~ /^(__|instance_eval)/
    end
  end
  instance_methods.each { |m| hide(m) }
end

class MyFancyClass < BlankSlate
  
  def initialize(hash)
       ...
  end
end

@json = '{ "type": "world", "message": "hello" }'

post = JSON.parse(@json)
test = MyFancyClass.new(post)

test.type # => world
36ad787d1b5306e65e89b441e0cade8f

Dragan Cvetinovic

June 21, 2010, June 21, 2010 14:51, permalink

No rating. Login to rate!

For instance :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
require 'json'

class BlankSlate
  class << self
    def hide(name)
      undef_method name if instance_methods.include?(name) and name !~ /^(__|instance_eval|object_id)/
    end
  end
  instance_methods.each { |m| hide(m) }
end

class MyFancyClass < BlankSlate
  
  def initialize(hash)
       @attributes = hash
  end
  def method_missing(name, *args)
      attribute = name.to_s
      super unless @attributes[attribute]
      @attributes[attribute]
  end
end

@json = '{ "type": "world", "message": "hello" }'

#@json.gsub!("type", "mtype")

post = JSON.parse(@json)
test = MyFancyClass.new(post)

puts test.type #=> world

Your refactoring





Format Copy from initial code

or Cancel