Changeset 798

Show
Ignore:
Timestamp:
10/30/07 21:27:37 (1 year ago)
Author:
iv..@gweezlebur.com
Message:

Move provides() et al to ResponderMixin?, and add serious documentation

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/autotest/merbsource_rspec.rb

    r685 r798  
    2020      }, 
    2121      %r%^lib/merb/(.*)\.rb$% => kernel.proc { |_, m|  
    22         ["spec/#{m[1]}_spec.rb", "spec/merb/#{m[1]}_spec.rb"]  
     22        ["spec/#{m[0]}_spec.rb", "spec/merb/#{m[0]}_spec.rb"]  
    2323      }, 
    2424      %r%^lib/merb\.rb$% => kernel.proc {  
  • trunk/lib/merb/controller.rb

    r775 r798  
    4848        cont 
    4949      end 
    50     end 
    51      
    52      
    53     class_inheritable_accessor :class_provided_formats 
    54     self.class_provided_formats = [:html] 
    55  
    56     # set controller-level provides 
    57     def self.provides(*formats) 
    58       formats.each {|fmt| 
    59         self.class_provided_formats << fmt unless class_provided_formats.include?(fmt) 
    60       } 
    61     end 
    62      
    63     def self.only_provides(*formats) 
    64       self.class_provided_formats = formats 
    65     end 
    66      
    67     def self.does_not_provide(*formats) 
    68       self.class_provided_formats -= formats 
    69     end 
    70      
    71     def provided_formats 
    72       @_provided_formats ||= class_provided_formats 
    73     end 
    74      
    75     def provided_formats=(*formats) 
    76       raise "Cannot modify provided_formats because content_type has already been set" if content_type_set? 
    77       @_provided_formats = formats.flatten 
    78     end 
    79        
    80     def provides(*formats) 
    81       formats.each {|fmt| 
    82         self.provided_formats += [fmt] unless provided_formats.include?(fmt) 
    83       } 
    84     end 
    85      
    86     def only_provides(*formats) 
    87       self.provided_formats = formats.flatten 
    88     end 
    89      
    90     def does_not_provide(*formats) 
    91       self.provided_formats -= formats.flatten 
    9250    end 
    9351     
  • trunk/lib/merb/mixins/responder.rb

    r795 r798  
    88  end 
    99   
    10   # use this in your controllers to switch output based on 
    11   # the HTTP_ACCEPT header. like so: 
    12   # respond_to do |type| 
    13   #   type.js { render_js } 
    14   #   type.html { render } 
    15   #   type.xml { @foo.to_xml } 
    16   #   type.yaml { @foo.to_yaml } 
    17   # end   
    18   # 
    19   # Any specific outgoing headers should be included here.  These are not the content-type header 
    20   # but anything in addition to it. 
    21   # +tranform_method+ should be set to a symbol of the method used to transform a resource into this mime type. 
    22   # for example for the :xml mime type an object might be transformed by calling :to_xml 
    23   # or for the :js mime type, :to_json 
    24   # If there is no transform method, use nil 
    25    
     10  # Any specific outgoing headers should be included here.  These are not 
     11  # the content-type header but anything in addition to it. 
     12  # +tranform_method+ should be set to a symbol of the method used to 
     13  # transform a resource into this mime type. 
     14  # For example for the :xml mime type an object might be transformed by 
     15  # calling :to_xml, or for the :js mime type, :to_json. 
     16  # If there is no transform method, use nil. 
    2617  def self.add_mime_type(key,transform_method, values,new_response_headers = {}) 
    2718    raise ArgumentError unless key.is_a?(Symbol) && values.is_a?(Array) 
     
    7566    Merb.add_mime_type(:json,:to_json,%w[application/json  text/x-json  ]) 
    7667  end 
    77  
    78    
     68   
     69 
     70  # The ResponderMixin adds methods that help you manage what 
     71  # formats your controllers have available, determine what format(s) 
     72  # the client requested and is capable of handling, and perform 
     73  # content negotiation to pick the proper content format to 
     74  # deliver. 
     75  #  
     76  # If you hear someone say "Use provides" they're talking about the 
     77  # Responder.  If you hear someone ask "What happened to respond_to?" 
     78  # it was replaced by provides and the other Responder methods. 
     79  #  
     80  # == A simple example 
     81  #  
     82  # The best way to understand how all of these pieces fit together is 
     83  # with an example.  Here's a simple web-service ready resource that 
     84  # provides a list of all the widgets we know about.  The widget list is  
     85  # available in 3 formats: :html (the default), plus :xml and :text. 
     86  #  
     87  #     class Widgets < Application 
     88  #       provides :html   # This is the default, but you can 
     89  #                        # be explicit if you like. 
     90  #       provides :xml, :text 
     91  #        
     92  #       def index 
     93  #         @widgets = Widget.fetch 
     94  #         render @widgets 
     95  #       end 
     96  #     end 
     97  #  
     98  # Let's look at some example requests for this list of widgets.  We'll 
     99  # assume they're all GET requests, but that's only to make the examples 
     100  # easier; this works for the full set of RESTful methods. 
     101  #  
     102  # 1. The simplest case, /widgets.html 
     103  #    Since the request includes a specific format (.html) we know 
     104  #    what format to return.  Since :html is in our list of provided 
     105  #    formats, that's what we'll return.  +render+ will look 
     106  #    for an index.html.erb (or another template format 
     107  #    like index.html.mab; see the documentation on Template engines) 
     108  #  
     109  # 2. Almost as simple, /widgets.xml 
     110  #    This is very similar.  They want :xml, we have :xml, so 
     111  #    that's what they get.  If +render+ doesn't find an  
     112  #    index.xml.builder or similar template, it will call +to_xml+ 
     113  #    on @widgets.  This may or may not do something useful, but you can  
     114  #    see how it works. 
     115  # 
     116  # 3. A browser request for /widgets 
     117  #    This time the URL doesn't say what format is being requested, so 
     118  #    we'll look to the HTTP Accept: header.  If it's '*/*' (anything), 
     119  #    we'll use the first format on our list, :html by default. 
     120  #     
     121  #    If it parses to a list of accepted formats, we'll look through  
     122  #    them, in order, until we find one we have available.  If we find 
     123  #    one, we'll use that.  Otherwise, we can't fulfill the request:  
     124  #    they asked for a format we don't have.  So we raise 
     125  #    406: Not Acceptable. 
     126  #  
     127  # == A more complex example 
     128  #  
     129  # Sometimes you don't have the same code to handle each available  
     130  # format. Sometimes you need to load different data to serve 
     131  # /widgets.xml versus /widgets.txt.  In that case, you can use 
     132  # +content_type+ to determine what format will be delivered. 
     133  #  
     134  #     class Widgets < Application 
     135  #       def action1 
     136  #         if content_type == :text 
     137  #           Widget.load_text_formatted(params[:id]) 
     138  #         else 
     139  #           render 
     140  #         end 
     141  #       end 
     142  #        
     143  #       def action2 
     144  #         case content_type 
     145  #         when :html 
     146  #           handle_html() 
     147  #         when :xml 
     148  #           handle_xml() 
     149  #         when :text 
     150  #           handle_text() 
     151  #         else 
     152  #           render 
     153  #         end 
     154  #       end 
     155  #     end 
     156  #  
     157  # You can do any standard Ruby flow control using +content_type+.  If 
     158  # you don't call it yourself, it will be called (triggering content 
     159  # negotiation) by +render+. 
     160  # 
     161  # Once +content_type+ has been called, the output format is frozen, 
     162  # and none of the provides methods can be used. 
    79163  module ResponderMixin 
    80164     
    81     # def respond_to(&block) 
    82     #   responder = Rest::Responder.new(request.accept, params) 
    83     #   block.call(responder) 
    84     #   responder.respond(headers) 
    85     #   @_status = responder.status if responder.status 
    86     #   responder.body 
    87     # end 
    88      
    89     def perform_content_negotiation 
     165    def self.included(base) # :nodoc: 
     166      base.extend(ClassMethods) 
     167      base.class_eval "class_inheritable_accessor :class_provided_formats" 
     168      base.class_provided_formats = [:html] 
     169    end 
     170 
     171    module ClassMethods 
     172 
     173      # Adds symbols representing formats to the controller's 
     174      # default list of provided_formats.  These will apply to 
     175      # every action in the controller, unless modified in the action. 
     176      def provides(*formats) 
     177        formats.each {|fmt| 
     178          self.class_provided_formats << fmt unless class_provided_formats.include?(fmt) 
     179        } 
     180      end 
     181       
     182      # Overwrites the controller's list of provided_formats. These 
     183      # will apply to every action in the controller, unless modified 
     184      # in the action. 
     185      def only_provides(*formats) 
     186        self.class_provided_formats = formats 
     187      end 
     188       
     189      # Removes formats from the controller's 
     190      # default list of provided_formats.  These will apply to 
     191      # every action in the controller, unless modified in the action. 
     192      def does_not_provide(*formats) 
     193        self.class_provided_formats -= formats 
     194      end 
     195    end 
     196     
     197    # Returns the current list of formats provided for this instance 
     198    # of the controller.  It starts with what has been set in the controller 
     199    # (or :html by default) but can be modifed on a per-action basis. 
     200    def provided_formats 
     201      @_provided_formats ||= class_provided_formats 
     202    end 
     203     
     204    # Sets the provided formats for this action.  Usually, you would 
     205    # use a combination of +provides+, +only_provides+ and +does_not_provide+ 
     206    # to manage this, but you can set it directly. 
     207    def provided_formats=(*formats) 
     208      raise "Cannot modify provided_formats because content_type has already been set" if content_type_set? 
     209      @_provided_formats = formats.flatten 
     210    end 
     211     
     212    # Adds formats to the list of provided formats for this particular  
     213    # request.  Usually used to add formats to a single action.  See also 
     214    # the controller-level provides that affects all actions in a controller. 
     215    def provides(*formats) 
     216      formats.each {|fmt| 
     217        self.provided_formats += [fmt] unless provided_formats.include?(fmt) 
     218      } 
     219    end 
     220     
     221    # Sets list of provided formats for this particular  
     222    # request.  Usually used to limit formats to a single action.  See also 
     223    # the controller-level provides that affects all actions in a controller. 
     224    def only_provides(*formats) 
     225      self.provided_formats = formats.flatten 
     226    end 
     227     
     228    # Removes formats from the list of provided formats for this particular  
     229    # request.  Usually used to remove formats from a single action.  See 
     230    # also the controller-level provides that affects all actions in a 
     231    # controller. 
     232    def does_not_provide(*formats) 
     233      self.provided_formats -= formats.flatten 
     234    end 
     235     
     236    # Do the content negotiation: 
     237    # 1. if params[:format] is there, and provided, use it 
     238    # 2. Parse the Accept header 
     239    # 3. If it's */*, use the first provided format 
     240    # 4. Look for one that is provided, in order of request 
     241    # 5. Raise 406 if none found 
     242    def perform_content_negotiation # :nodoc: 
    90243      raise Merb::ControllerExceptions::NotAcceptable if provided_formats.empty? 
    91244      if fmt = params[:format] 
     
    109262    end 
    110263     
     264    # Checks to see if content negotiation has already been performed. 
     265    # If it has, you can no longer modify the list of provided formats. 
    111266    def content_type_set? 
    112267      !@_content_type.nil? 
    113268    end 
    114269     
     270    # Returns the output format for this request, based on the  
     271    # provided formats, <tt>params[:format]</tt> and the client's HTTP 
     272    # Accept header. 
     273    # 
     274    # The first time this is called, it triggers content negotiation 
     275    # and caches the value.  Once you call +content_type+ you can 
     276    # not set or change the list of provided formats. 
     277    # 
     278    # Called automatically by +render+, so you should only call it if 
     279    # you need the value, not to trigger content negotiation.  
    115280    def content_type 
    116281      unless content_type_set? 
     
    123288    end 
    124289     
     290    # Sets the output content_type for this request.  Normally you 
     291    # should use +provides+, +does_not_provide+ and +only_provides+ 
     292    # and then let the content negotiation process determine the proper 
     293    # content_type.  However, in some circumstances you may want to 
     294    # set it directly, or override what content negotiation picks. 
    125295    def content_type=(new_type) 
    126296      @_content_type = new_type 
  • trunk/spec/merb/controller_spec.rb

    r775 r798  
    1313    c = new_controller 
    1414    c._layout.should == :application 
    15   end 
    16 end 
    17  
    18 class FormattedBasic < Merb::Controller ; end 
    19 class WithXml < Merb::Controller ; provides :xml ; end 
    20 class XmlOnly < Merb::Controller ; only_provides :xml ; end 
    21 class ProvidesNothing < Merb::Controller ; does_not_provide :html ; end 
    22 class HtmlAgain < Merb::Controller ; provides :html ; end 
    23  
    24 class ManyProvides < Merb::Controller 
    25   provides :html, :xml, :txt 
    26   def noextra ; end 
    27   def extra ; provides :json, :yaml ; end 
    28 end 
    29  
    30 describe "Merb::Controller", "provided formats (class)" do 
    31   it "should have the default class_provided_formats of [:html]" do 
    32     c = new_controller("index",FormattedBasic) 
    33     c.class_provided_formats.should == [:html] 
    34   end 
    35    
    36   it "should add :xml to class_provided_formats when called with provides :xml" do 
    37     c = new_controller("index",WithXml) 
    38     c.class_provided_formats.should == [:html, :xml] 
    39   end 
    40  
    41   it "should only have :xml in class_provided_formats when called with only_provides :xml" do 
    42     c = new_controller("index",XmlOnly) 
    43     c.class_provided_formats.should == [:xml] 
    44   end 
    45    
    46   it "should have an empty class_provided_formats when called with does_not_provide :html" do 
    47     c = new_controller("index",ProvidesNothing) 
    48     c.class_provided_formats.should == [] 
    49   end 
    50  
    51   it "should only have :html in class_provided_formats when called with provides :html (after other controllers have set it)" do 
    52     c = new_controller("index",HtmlAgain) 
    53     c.class_provided_formats.should == [:html] 
    54   end 
    55      
    56   it "should handle multiple provides added via class method" do 
    57     c = new_controller("noextra",ManyProvides) 
    58     c.provided_formats.should == [:html, :xml, :txt] 
    59   end 
    60 end 
    61  
    62 class ActionProvides < Merb::Controller 
    63   def basic ; end 
    64   def with_xml ; provides :xml ; end 
    65   def xml_only ; only_provides :xml ; end 
    66   def nothing ; does_not_provide :html ; end 
    67 end 
    68  
    69 describe "Merb::Controller", "provided formats (action)" do 
    70   it "should have the default provided_formats" do 
    71     c = new_controller("basic",ActionProvides) 
    72     c.basic 
    73     c.provided_formats.should == [:html] 
    74   end 
    75    
    76   it "should add :xml to provided_formats when called with provides :xml" do 
    77     c = new_controller("with_xml",ActionProvides) 
    78     c.with_xml 
    79     c.provided_formats.should == [:html, :xml] 
    80   end 
    81    
    82   it "should only have :xml in provided_formats when called with only_provides :xml" do 
    83     c = new_controller("xml_only",ActionProvides) 
    84     c.xml_only 
    85     c.provided_formats.should == [:xml] 
    86   end 
    87  
    88   it "should have an empty provided_formats when called with does_not_provide :html" do 
    89     c = new_controller("nothing",ActionProvides) 
    90     c.nothing 
    91     c.provided_formats.should be_empty 
    9215  end 
    9316end 
  • trunk/spec/merb/responder_spec.rb

    r795 r798  
    479479  end 
    480480end 
     481 
     482class FormattedBasic < Merb::Controller ; end 
     483class WithXml < Merb::Controller ; provides :xml ; end 
     484class XmlOnly < Merb::Controller ; only_provides :xml ; end 
     485class ProvidesNothing < Merb::Controller ; does_not_provide :html ; end 
     486class HtmlAgain < Merb::Controller ; provides :html ; end 
     487 
     488class ManyProvides < Merb::Controller 
     489  provides :html, :xml, :txt 
     490  def noextra ; end 
     491  def extra ; provides :json, :yaml ; end 
     492end 
     493 
     494describe "Responder", "managing provided formats (class)" do 
     495  it "should have the default class_provided_formats of [:html]" do 
     496    c = new_controller("index",FormattedBasic) 
     497    c.class_provided_formats.should == [:html] 
     498  end 
     499   
     500  it "should add :xml to class_provided_formats when called with provides :xml" do 
     501    c = new_controller("index",WithXml) 
     502    c.class_provided_formats.should == [:html, :xml] 
     503  end 
     504 
     505  it "should only have :xml in class_provided_formats when called with only_provides :xml" do 
     506    c = new_controller("index",XmlOnly) 
     507    c.class_provided_formats.should == [:xml] 
     508  end 
     509   
     510  it "should have an empty class_provided_formats when called with does_not_provide :html" do 
     511    c = new_controller("index",ProvidesNothing) 
     512    c.class_provided_formats.should == [] 
     513  end 
     514 
     515  it "should only have :html in class_provided_formats when called with provides :html (after other controllers have set it)" do 
     516    c = new_controller("index",HtmlAgain) 
     517    c.class_provided_formats.should == [:html] 
     518  end 
     519     
     520  it "should handle multiple provides added via class method" do 
     521    c = new_controller("noextra",ManyProvides) 
     522    c.provided_formats.should == [:html, :xml, :txt] 
     523  end 
     524end 
     525 
     526class ActionProvides < Merb::Controller 
     527  def basic ; end 
     528  def with_xml ; provides :xml ; end 
     529  def xml_only ; only_provides :xml ; end 
     530  def nothing ; does_not_provide :html ; end 
     531end 
     532 
     533describe "Responder", "managing provided formats (action)" do 
     534  it "should have the default provided_formats" do 
     535    c = new_controller("basic",ActionProvides) 
     536    c.basic 
     537    c.provided_formats.should == [:html] 
     538  end 
     539   
     540  it "should add :xml to provided_formats when called with provides :xml" do 
     541    c = new_controller("with_xml",ActionProvides) 
     542    c.with_xml 
     543    c.provided_formats.should == [:html, :xml] 
     544  end 
     545   
     546  it "should only have :xml in provided_formats when called with only_provides :xml" do 
     547    c = new_controller("xml_only",ActionProvides) 
     548    c.xml_only 
     549    c.provided_formats.should == [:xml] 
     550  end 
     551 
     552  it "should have an empty provided_formats when called with does_not_provide :html" do 
     553    c = new_controller("nothing",ActionProvides) 
     554    c.nothing 
     555    c.provided_formats.should be_empty 
     556  end 
     557end 
     558 
  • trunk/tools/allison/cache/BODY

    r598 r798  
    1 <html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta content="text/html; charset=utf-8" http-equiv="Content-Type"/><title>%title%</title><link type="text/css" rel="stylesheet" media="screen" href="%style_url%"/><script type="text/javascript"> 
     1<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head><meta content="text/html; charset=utf-8" http-equiv="Content-Type"/><title>%title%</title><link type="text/css" href="%style_url%" rel="stylesheet" media="screen"/><script type="text/javascript"> 
    22// Javascript for Allison RDoc template 
    33// Copyright 2006 Cloudburst LLC 
     
    402402</div> 
    403403ENDIF:methods 
    404 <div id="spacer"></div><div class="navigation dark index" id="class_wrapper"><div class="list_header"><h3>All classes</h3></div><div class="list_header_link"><a onclick="toggle('class'); toggleText('class_link'); return false;" href="#" id="class_link">Hide...</a></div><div class="clear"></div><div id="class"><form><label for="filter_class">Filter:&nbsp;&nbsp;</label><input type="text" onKeyUp="return filterList('class', this.value, event);" onKeyPress="return disableSubmit(event);" id="filter_class"></input></form></div></div><div class="navigation dark index" id="file_wrapper"><div class="list_header"><h3>All files</h3></div><div class="list_header_link"><a onclick="toggle('file'); toggleText('file_link'); return false;" href="#" id="file_link">Hide...</a></div><div class="clear"></div><div id="file"><form><label for="filter_file">Filter:&nbsp;&nbsp;</label><input type="text" onKeyUp="return filterList('file', this.value, event);" onKeyPress="return disableSubmit(event);" id="filter_file"></input></form></div></div><div class="navigation dark index" id="method_wrapper"><div class="list_header"><h3>All methods</h3></div><div class="list_header_link"><a onclick="toggle('method'); toggleText('method_link'); return false;" href="#" id="method_link">Show...</a></div><div class="clear"></div><div id="method"><form><label for="filter_method">Filter:&nbsp;&nbsp;</label><input type="text" onKeyUp="return filterList('method', this.value, event);" onKeyPress="return disableSubmit(event);" id="filter_method"></input></form></div></div><div class="curve" id="left_curve_0"></div><div class="curve" id="left_curve_1"></div><div class="curve" id="left_curve_2"></div><div class="curve" id="left_curve_3"></div><div class="curve" id="left_curve_4"></div><div class="curve" id="left_curve_5"></div><div class="curve" id="left_curve_6"></div><div class="curve" id="left_curve_7"></div><div class="curve" id="left_curve_8"></div><div class="curve" id="left_curve_9"></div></div><div id="content"> 
     404<div id="spacer"></div><div class="navigation dark index" id="class_wrapper"><div class="list_header"><h3>All classes</h3></div><div class="list_header_link"><a href="#" onclick="toggle('class'); toggleText('class_link'); return false;" id="class_link">Hide...</a></div><div class="clear"></div><div id="class"><form><label for="filter_class">Filter:&nbsp;&nbsp;</label><input type="text" onKeyPress="return disableSubmit(event);" id="filter_class" onKeyUp="return filterList('class', this.value, event);"></input></form></div></div><div class="navigation dark index" id="file_wrapper"><div class="list_header"><h3>All files</h3></div><div class="list_header_link"><a href="#" onclick="toggle('file'); toggleText('file_link'); return false;" id="file_link">Hide...</a></div><div class="clear"></div><div id="file"><form><label for="filter_file">Filter:&nbsp;&nbsp;</label><input type="text" onKeyPress="return disableSubmit(event);" id="filter_file" onKeyUp="return filterList('file', this.value, event);"></input></form></div></div><div class="navigation dark index" id="method_wrapper"><div class="list_header"><h3>All methods</h3></div><div class="list_header_link"><a href="#" onclick="toggle('method'); toggleText('method_link'); return false;" id="method_link">Show...</a></div><div class="clear"></div><div id="method"><form><label for="filter_method">Filter:&nbsp;&nbsp;</label><input type="text" onKeyPress="return disableSubmit(event);" id="filter_method" onKeyUp="return filterList('method', this.value, event);"></input></form></div></div><div class="curve" id="left_curve_0"></div><div class="curve" id="left_curve_1"></div><div class="curve" id="left_curve_2"></div><div class="curve" id="left_curve_3"></div><div class="curve" id="left_curve_4"></div><div class="curve" id="left_curve_5"></div><div class="curve" id="left_curve_6"></div><div class="curve" id="left_curve_7"></div><div class="curve" id="left_curve_8"></div><div class="curve" id="left_curve_9"></div></div><div id="content"> 
    405405IF:title 
    406406<h1 id="item_name">%title%</h1> 
     
    572572 
    573573IF:sourcecode 
    574 <p class="source_link" id="%aref%-show-link"><a onclick="toggle('%aref%-source'); toggleText('%aref%-link'); return false;" href="#" id="%aref%-link">Show source...</a></p><div class="source" id="%aref%-source"><pre>%sourcecode%</pre></div> 
     574<p class="source_link" id="%aref%-show-link"><a href="#" onclick="toggle('%aref%-source'); toggleText('%aref%-link'); return false;" id="%aref%-link">Show source...</a></p><div class="source" id="%aref%-source"><pre>%sourcecode%</pre></div> 
    575575ENDIF:sourcecode 
    576576</div></div> 
  • trunk/tools/allison/cache/INDEX

    r598 r798  
    1 <html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta content="text/html; charset=utf-8" http-equiv="Content-Type"/><title>%title%</title><link type="text/css" rel="stylesheet" media="screen" href="rdoc-style.css"/><meta content="0;url=%initial_page%" http-equiv="refresh"/></head><body><div id="container"><div class="curve" id="preheader_curve_0"></div><div class="curve" id="preheader_curve_1"></div><div class="curve" id="preheader_curve_2"></div><div class="curve" id="preheader_curve_3"></div><div class="curve" id="preheader_curve_4"></div><div class="curve" id="preheader_curve_5"></div><div class="curve" id="preheader_curve_6"></div><div class="curve" id="preheader_curve_7"></div><div class="curve" id="preheader_curve_8"></div><div class="curve" id="preheader_curve_9"></div><div id="header"><span id="title"><p>&nbsp;</p><h1>Ruby Documentation</h1></span></div><div class="clear"></div><div id="redirect"><a href="%initial_page%"><h1>Redirect</h1></a></div></div></body></html> 
     1<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head><meta content="text/html; charset=utf-8" http-equiv="Content-Type"/><title>%title%</title><link type="text/css" href="rdoc-style.css" rel="stylesheet" media="screen"/><meta content="0;url=%initial_page%" http-equiv="refresh"/></head><body><div id="container"><div class="curve" id="preheader_curve_0"></div><div class="curve" id="preheader_curve_1"></div><div class="curve" id="preheader_curve_2"></div><div class="curve" id="preheader_curve_3"></div><div class="curve" id="preheader_curve_4"></div><div class="curve" id="preheader_curve_5"></div><div class="curve" id="preheader_curve_6"></div><div class="curve" id="preheader_curve_7"></div><div class="curve" id="preheader_curve_8"></div><div class="curve" id="preheader_curve_9"></div><div id="header"><span id="title"><p>&nbsp;</p><h1>Ruby Documentation</h1></span></div><div class="clear"></div><div id="redirect"><a href="%initial_page%"><h1>Redirect</h1></a></div></div></body></html>