""" webpages.cobra This is the beginning of a framework for generating or serving web content. Initially, its only aim is to spit out static HTML files while saving work by making use of OO inheritance to share common page parts. It's partly inspired by "Webware for Python", an open source project that was founded in 2000 by the author of Cobra. The current code is informative and can be extended. But currently this "program" isn't doing anything. TODO [ ] Finish Page class [ ] Make something for the typical page format of header, sidebar, content and footer. DeluxePage [ ] Get a real example going [ ] Consider a way to use these classes on non-Cobra content that a non-programer could author [ ] Incorporate extras.web.PageExtrasMixIn """ @ref 'System.Web' use System.Web interface IToHtml """ Exposes a .toHtml property which returns a string containing HTML source body representing the object that received the message. Implementations should incorporate the HTML of their "subobjects" as appropriate. If a property is inappropriate for your class because the HTML source could be potentially voluminous and/or take a long time to compute then implement IWriteHtml instead. The "Cobra Web Pages" framework supports both. See also: HtmlUtils """ get toHtml as String interface IWriteHtml """ Exposes a .writeHtml method whose duty is to write HTML source that represents the object, to a TextWriter. Implementations should incorporate the HTML of their "subobjects" as appropriate. If the HTML source is likely to be small, or you simply wish to return a string rather than write to a TextWriter then implement IWriteHtml. The "Cobra Web Pages" framework supports both. See also: HtmlUtils """ def writeHtml(dest as TextWriter) class HtmlUtils shared def toHtml(obj) as String """ Returns the HTML source for the given object. Recognizes objects that implement IToHtml and IWriteHtml. """ if obj is nil return 'nil' else if obj inherits IToHtml return obj.toHtml else if obj inherits IWriteHtml sw = StringWriter() obj.writeHtml(sw) return sw.toString else return obj.toString def writeHtml(dest as TextWriter, obj) """ Writes the HTML source for the given object. Recognizes objects that implement IToHtml and IWriteHtml. """ if obj is nil dest.write('nil') else if obj inherits IWriteHtml obj.writeHtml(dest) else if obj inherits IToHtml dest.write(obj.toHtml) else dest.write(obj.toString) class HtmlWriter inherits BaseObject implements IWriteHtml """ HtmlWriter provides a concrete implementation of IWriteHtml with some conveniences that you can inherit and make use of. Subclasses should override .writeHtml. """ get dest from var as TextWriter? def writeHtml(dest as TextWriter) _dest = dest try .writeHtml finally _dest = nil def writeHtml pass ## Self util ## def writeLine(s as String) _dest.writeLine(s) def htmlEncode(s as String) as String return HttpUtility.htmlEncode(s) to ! def htmlDecode(s as String) as String return HttpUtility.htmlDecode(s) to ! def urlEncode(s as String) as String return HttpUtility.urlEncode(s) to ! def urlDecode(s as String) as String return HttpUtility.urlDecode(s) to ! class BaseObject """ A base class common to all the classes in this framework. May come in handy down the road. """ pass class Transaction inherits BaseObject """ This class would hold the request, response, session, application and servlet references. """ pass class EndResponse inherits Exception """ Used internally by Servlet. When subclassing servlets, invoke .endResponse to do so. """ pass class Servlet inherits BaseObject test s = Servlet() assert s.name=='Servlet' s.name = 'foo' assert s.name=='foo' s.run(nil) assert not s.didEndResponse var _name as String? var _txn as Transaction? var _dest as TextWriter? var _didEndResponse as bool cue init base.init pro name as String """ Returns the name of the servlet, which is its class name by default. """ get return _name ? .getType.name set _name = value get dest from _dest def run(txn as Transaction?) _txn = txn _dest = Console.out # TODO: take from _txn if possible _didEndResponse = false try try .setUp .respond catch EndResponse _didEndResponse = true .tearDown finally _dest = nil _txn = nil def setUp pass def respond pass def tearDown pass get didEndResponse from var def log(message as String) """ Log important messages from the developer. These go to the console. """ .log(Console.out, message) def log(tw as TextWriter, message as String) message = '([.name]) ([DateTime.now]) [message]' tw.writeLine(message) def write(stuff as vari Object) for item in stuff _dest.write(.itemToString(item)) def itemToString(item) as String """ Used by .write to convert objects to strings. Returns 'nil' for `nil`. """ return if(item is nil, 'nil', item.toString) def writeLine(s as String) _dest.writeLine(s) def urlEncode(s as String) as String return HttpUtility.urlEncode(s) to ! def urlDecode(s as String) as String return HttpUtility.urlDecode(s) to ! def endResponse """ When this method is called during .setUp or .respond, servlet processing will end immediately, and the accumulated response will be sent. Note that .tearDown will still be called, providing a chance to clean up or free any resources. Also, it can check the boolean .didEndResponse to see if that took place. """ throw EndResponse() class HTTPServlet inherits Servlet """ This would get fleshed out if this class hierarchy became part of an application server. See "Webware for Python". """ pass class Page inherits HTTPServlet implements IWriteHtml """ Page is a type of HTTPServlet that is more convenient for servlets which represent HTML pages generated in response to GET and POST requests. In fact, this is the most common type of servlet. Subclasses typically override .writeHeader, .writeBody or .writeBodyParts, and writeFooter. They might also choose to override .writeHtml entirely. When developing a full-blown website, it's common to create a subclass of `Page` called SitePage which defines the common look and feel of the website and provides site-specific convenience methods. Then all other pages in your application inherit from SitePage. """ test p = Page() assert p.name == 'Page' assert p.title == 'Page' assert p.htTitle == 'Page' sw = StringWriter() p.writeHtml(sw) s = sw.toString.trim assert s.contains('') assert s.contains('
') assert s.contains('') assert s.endsWith('') get title as String """ Returns the title of the page absent any HTML tags. Subclasses often override this method to provide a custom title. This implementation returns .name. """ return .name get htTitle as String """ Returns the page title as HTML. Subclasses sometimes override this to provide an HTML enhanced version of the title. This is the property that should be used when including the page title in the actual page contents. This implementation returns .title. """ return .title get bodyArgs as MLArgs? return nil def writeHtml """ Writes all the HTML for the page. Subclasses may override this method (which is invoked by .respond) or more commonly its constituent methods, .writeDocType, .writeHead and .writeBody. You will want to override this method if: * you want to format the entire HTML page yourself * if you want to send an HTML page that has already been generated * if you want to use a template that generates the entire page * if you want to send non-HTML content (be sure to call `.response.setHeader('Content-type', 'mime/type')` in this case). """ .writeDocType .writeLine('') .writeHead .writeBody .writeLine('') get docTypeTransitional as String """ Returns """ return '' def writeDocType """ Writes the DOCTYPE tag. Invoked by .writeHtml to write the `` tag. By default this gives the HTML 4.01 Transitional DOCTYPE, which is a good guess for what most people send. Be warned, though, that some browsers render HTML differently based on the DOCTYPE (particular newer browsers, like Mozilla, do this). Subclasses may override to specify something else. You can find out more about doc types by searching for DOCTYPE on the web, or visiting: http://www.toHtmlhelp.com/tools/validator/doctype.toHtml """ .writeLine(.docTypeTransitional) def writeHead """ Writes the `` portion of the page by writing the `...` tags and invoking `writeHeadParts` in between. """ .writeLine('') .writeHeadParts .writeLine('') def writeHeadParts """ Writes the parts inside the `...` tags. Invokes .writeTitle and .writeStyleSheet. Subclasses can override this to add additional items and should invoke super. """ .writeTitle .writeStyleSheet def writeTitle """ Writes the `This page has not yet customized its content.
') def htmlEncode(s as String) as String return HttpUtility.htmlEncode(s) to ! def htmlDecode(s as String) as String return HttpUtility.htmlDecode(s) to ! ## implements IWriteHtml ## def writeHtml(dest as TextWriter) _dest = dest try .writeHtml finally _dest = nil def saveHtml(fileName as String) using f = File.createText(fileName) .writeHtml(f) class MLArgs inherits BaseObject implements IToHtml """ An MLArgs instance is a dictionary of named arguments, that when printed (toString) yield an argument string suitable for HTML, XML, SGML, et al: href="http://foo.com" class="menuLink" The motiviation for MLArgs is that if you store these arguments as in a single string: 'href="http://foo.com" class="menuLink"' ...then inspecting and updating individual arguments requires extra effort. For example, if a subclass wishes to set the 'class' argument, after its base class has initialized the arguments, it may need to parse the string. Using MLArgs, the subclass can simply say: .linkArgs['class'] = 'submenuLink' And outputing MLArgs is just as convenient as using a string: dest.write('') You can do the same thing using the .tag method: dest.write(.linkArgs.tag('a')) Markup languages don't have a concept of nil while Cobra does. Consequently, key/value pairs whose values are nil are not included in the output. Also, setting a key to nil deletes it. These policies can be useful when merging arguments via .copyAndSet and .copyIfSet. See also: Tag """ test args = MLArgs() assert args.count==0 args['width'] = 50 assert args.count == 1 assert args['width'] == 50 assert args.toString == 'width="50"' args['color'] = 'blue' assert args.toString == 'color="blue" width="50"' shared pro willSortKeysDefault from var = true var _values as Dictionary