Daily Code Reading #24 – RestClient::Response

Yesterday I looked at the Request side of RestClient so I’m reading through the RestClient::Response class today. RestClient::Request#execute returns this response object from it’s #process_result.

1
response = Response.new(Request.decode(res['content-encoding'], res.body), res, args)

The Code

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
module RestClient
 
  # A Response from RestClient, you can access the response body, the code or the headers.
  #
  class Response < AbstractResponse
 
    attr_reader :body
 
    WARNING_MESSAGE = '[warning] The Response is no more a String and the Response content is now accessed through Response.body, please update your code'
 
    def initialize body, net_http_res, args
      super net_http_res, args
      @body = body || ""
    end
 
    def method_missing symbol, *args
      if body.respond_to? symbol
        warn WARNING_MESSAGE
        body.send symbol, *args
      else
        super
      end
    end
 
    def == o
      if super
        true
      else
        equal_body = (body == o)
        if equal_body
          warn WARNING_MESSAGE
        end
        equal_body
      end
    end
 
    def to_s
      body.to_s
    end
 
    def size
      body.size
    end
 
  end
end

RestClient::Response is a pretty small class because there is a lot of common logic shared in it’s parent class RestClient::AbstractResponse.

RestClient::Response#initialize

1
2
3
4
    def initialize body, net_http_res, args
      super net_http_res, args
      @body = body || ""
    end

#initialize isn’t doing very much here, just calling it’s parent class (AbstractResponse) and initializing the @body attribute.

RestClient::Response#method_missing

1
2
3
4
5
6
7
8
    def method_missing symbol, *args
      if body.respond_to? symbol
        warn WARNING_MESSAGE
        body.send symbol, *args
      else
        super
      end
    end

This #method_missing is setting up a proxy to the body. Based on the warning message here, I think RestClient used to return a string but now it’s returning the Response object instead. Using #method_missing this way is interesting, it keeps backwards compatibility and gives other developers time to update their own code.

RestClient::Response#==

1
2
3
4
5
6
7
8
9
10
11
    def == o
      if super
        true
      else
        equal_body = (body == o)
        if equal_body
          warn WARNING_MESSAGE
        end
        equal_body
      end
    end

This comparison method is doing two things. First it’s checking if the two objects are the same object (using super). If that fails, it checks if the body contents are equal; if so it warns about the backwards compatibility (above) and then returns the results of that comparison.

RestClient::Response#to_s and RestClient::Response#size

1
2
3
4
5
6
7
    def to_s
      body.to_s
    end
 
    def size
      body.size
    end

Both of these methods are just delegated to the body object. At first I thought the #method_missing call would handle #to_s but then I remembered that all Ruby objects respond to #to_s. This would prevent #method_missing from being called and Response would just return a string like:

#<RestClient::Response:0xb6f9e5dc>

Tomorrow I’ll look into AbstractResponse class, since that’s where a lot of the processing logic for a Response is at.