From: Jean-Hugues ROBERT Date: 2002-05-24T01:50:24+09:00 Subject: Re: method_missing is cool (was: are there unit tests for cgi.rb?) Hello, At 00:44 24/05/2002 +0900, you wrote: >Hello, > >I think method_missing is not good. because, real error is hidden. > >On Thu, May 23, 2002 at 12:20:45PM +0900, > Jim Freeze wrote: > > > puts a.h1("Title") > > puts a.a({'href'=>'http://mylink'}) { a.h1 { "Title" } } > > puts a.a({'href'=>'http://mylink'}) { a.h1 { "Title" } }, a.br > >Isn't this enough? > >def a(element, attr = {}) > "<#{element}" + attr.collect{|k,v| %Q< #{k}="#{v}"> }.to_s + ">" + > if block_given? then yield.to_s + "" else "" end >end > >puts a(:h1){ "Title" } >puts a(:a, {'href'=>'http://mylink'}){ a(:h1){ "Title" } } >puts a(:a, {'href'=>'http://mylink'}){ a(:h1){ "Title" } }, a(:br) > > >-- >Wakou Aoyama Nature loves diversity. After this minimalist solution, here is the maximalist one ;-) Yours, Jean-Hugues # hmtl.rb # Tools to ease HTML code manipulation # # 02/05/23, JHR, creation # # (C) See at the end # This module defines a method .html() that calls a block within which calls, # to methods whose name is an HTML tag name, generate HTML code. # Warning: If the includer class wants to redefine method_missing() it must # do so *before* including this module *or* it must starts the definition with # "return super if html? method_id", where method_id is the first parameter # of the new method_missing() definition. # Warning: For

use P(), because p() will call Kernel.p(). module HTML unless defined? TAGS TAGS = \ begin tags = \ " A ADDRESS APPLET AREA B BASE BASEFONT BIG BLINK BLOCKQUOTE BODY"\ " BR CAPTION CENTER CITE CODE DD DIR DIV DL DT EM EMBED FONT FORM FRAME"\ " FRAMESET H1 HEAD HR HTML I ILAYER IMG INPUT ISINDEX KBD KEYGEN LAYER"\ " LI LINK MAP MENU META MULTICOL NOBR NOEMBED NOFRAMES NOLAYER NOSCRIPT"\ " OBJECT OL OPTION P PARAM PLAINTEXT PRE S SCRIPT SELECT SERVER SMALL"\ " SPACER SPAN STRIKE STRONG STYLE SUB SUP TABLE TD TEXTAREA TH TITLE TR"\ " TT U UL VAR WBR XMP H2 H3 H4 H5 H6" hash = {} tags.split( " ").each do | tag | hash[tag.downcase().intern()] = true hash[(tag.downcase()+"!").intern()] = "" end hash[:P] = true hash[:P!] = "

" hash end end # Call a block in a context where methods with name matching an HTML tag # name will return an HTML code fragment for that tag. def html( &block ) # An instance variable signals the context. Increment handles nesting. if @HTML then @HTML += 1 else @HTML = 1 end block.call() ensure # Exit context by removing the instance variable, unless nested call. remove_instance_variable( "@HTML") unless (@HTML -= 1) > 0 end # Check that some method name is an HTML tag name. Also handle xx! where # ! means "close tag". Case sensitive, please use lowercase only. def HTML::tag?( symb ) TAGS.include? symb end # Check if a symbol is an HTML tag name (never is when outside of .html()). # Case sensitive, please use lowercase only. def html?( symb ) @HTML and HTML::tag? symb end # Implement HTML tag generation methods. # tag() generates . # tag!() generates . # tag(a) generates a. # tag(hash) generates . # tag(x,y} generates something, either x or y is a hash. # tag() {block} is equivalent to tag( value_of_block ). # tag(a) {block} is equivalent to tag( a, value_of_block ). def method_missing( id, *args ) #puts "module HTML method_missing() called for id:#{id}" # Be transparent if not inside .html() or not an HTML tag return super unless @HTML and TAGS[id] #puts "module HTML method_missing() now expand #{id} into HTML tag" HTML::tag( id, *args) end # Do the job, called by method_missing(). def HTML::tag( id, *args ) tag = TAGS[id] # p "Called for: id=#{id}, args= #{args.inspect()}" # Handle close tag return tag if tag.kind_of? String # Get params, param order is actually irrelevant, we check param type later p1, p2 = args # Potential block becomes first param (and first param becomes second one) if block_given? then # blocks computes data p1, p2 = yield, p1 end # Params order is irrevelant, type is if p1.kind_of? Hash then attrs, data = p1, p2 else attrs, data = p2, p1 end attr_str = attrs.nil? ? "" : (attrs.collect do | key, value | " #{key.to_s().upcase()}=\"#{value}\"" end).join() # If there are data then we need a closing tag, else we don't if data then "<#{id}#{attr_str}>#{data.to_s()}" else "<#{id}#{attr_str}>" end end end # module HTML # This helper class makes it easier to build an HTML page. class HtmlBuf < Array # My redefined method_missing() pushes the HTML code into the array def method_missing( id, *args ) # puts "HtmlBuf's missing_method() called for id:#{id}" if HTML::tag? id then #puts "HtmlBuf is implementing #{id}" self << HTML::tag( id, *args) else #puts "Not an HTML tag in HtmlBuf's method_missing()" super end end # You may create a new object with some initial content, or not. def initialize( init_content = nil ) super() if init_content then self.replace( [init_content]) end end # Make + behave like += def +( stuff ) self.<<( stuff.to_s()) end # Add content with HTML special characters escaped def escape( stuff ) self.<< CGI.escapeHTML( stuff) end # Return HTML code, as a String, with some \n now and there. def content() flatten!() join( "\n") end end # class HtmlBuf class HtmlModuleTest include HTML def method_missing( id, *args ) puts "My Test method_missing called for id:#{id}" return super if html? id puts "My method_missing() calls super" super end def do() @name = "item1" html do puts b( @name ) # ok ! puts a('Google', :href => 'http://www.google.com/') # ok. puts ul(li(@name) + li('item2') + li('item3')) # ok. puts br # single entity puts h1 { "Hello" } # with a block puts br( :align => "Top") # with params puts P { {:align => "Top"} } # with params from block puts h1( :color => "#000000") { "Black" } # with params and block puts h1( "White") { {:color => "#FFFFFF"} } # ... reversed ... puts P! # close tag puts foo() # NameError or NoMethodError end puts a('Google', 'href' => '"http://www.google.com/"') # NameError or NoMethodError end end # Please uncomment next line to run the test #begin HtmlModuleTest.new().do(); rescue Exception => ex; puts ex.message; end class HtmlBufClassTest include HTML def do() html do out = HtmlBuf.new( head( title( "Ruby"))) out.body out. h1 "Ruby Rules" out. h2 out<< "See the numbers:" out. h2! out. table out. tr th( "Year") + th( "Ruby Users") out. tr out. td( "2000") + td( "10K") out. tr! out. tr out. td( "2001") . td( "100K") out. tr! out. table! out. << "Future is bright!" out.body! puts out.content() out.foo end end end # Please uncomment next line to run the test #begin HtmlBufClassTest.new().do(); rescue Exception => ex; puts ex.message; end # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is The Rever Reversible Reason. # # The Initial Developer of the Original Code is # Jean-Hugues ROBERT (born dec 25 1965 in Nimes, France). # Portions created by the Initial Developer are Copyright (C) 2002 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # ***** END LICENSE BLOCK ***** ------------------------------------------------------------------------- Web: http://hdl.handle.net/1030.37/1.1 Phone: +33 (0) 4 92 27 74 17