Class: Hoshi::View

Inherits:
Object
  • Object
show all
Defined in:
lib/hoshi/view.rb,
lib/hoshi/view-tag.rb,
lib/hoshi/view/html.rb,
lib/hoshi/view/rss2.rb,
lib/hoshi/view/html3.rb,
lib/hoshi/view/html4.rb,
lib/hoshi/view/html5.rb,
lib/hoshi/view/xhtml.rb,
lib/hoshi/view/xhtml1.rb,
lib/hoshi/view/xhtml2.rb,
lib/hoshi/view/xhtml1_strict.rb,
lib/hoshi/view/html4_frameset.rb,
lib/hoshi/view/xhtml1_frameset.rb,
lib/hoshi/view/html4_transitional.rb,
lib/hoshi/view/xhtml1_transitional.rb

Overview

The View class is the super-class for views you create with Hoshi. More likely, though, you’ll be using one of View’s many sub-classes as the super-class for your view, like this:

class MyView < Hoshi::View :html4

or

class MyView < Hoshi::View::XHTML1Frameset

Of course, using View[] is the preferred method for the sake of brevity. When you create a view class, you’ll want to define one or more methods that eventually call View#render, which turns your view into HTML. (Private methods and methods that build up state do not need to do so.)

Direct Known Subclasses

HTML, RSS2

Defined Under Namespace

Classes: HTML, HTML3, HTML4, HTML4Frameset, HTML4Transitional, HTML5, RSS2, ValidationError, XHTML, XHTML1, XHTML1Frameset, XHTML1Transitional, XHTML2

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeView

Returns a new instance of View.



96
97
98
99
# File 'lib/hoshi/view.rb', line 96

def initialize
  @tcache = {}
  clear!
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(mname, *args, &b) ⇒ Object

Dynamically add tags if the view class for this object is permissive.



194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/hoshi/view.rb', line 194

def method_missing(mname, *args, &b)
  if self.class.permissive?
    self.class.tag mname
    if b
      send mname, *args, &b
    else
      send mname, *args
    end
  else
    super
  end
end

Class Method Details

.[](doctype) ⇒ Object

This method choses, based on the provided doctype, the proper sub-class of View. Generally, you’ll be using this rather than sub-classing View directly. The doctype argument is case- and underscore-insensitive, and valid arguments are names of View subclasses that are inside the View namespace.



40
41
42
43
44
45
46
47
# File 'lib/hoshi/view.rb', line 40

def self.[] doctype
  doctype = doctype.to_s.downcase.gsub('_', '')
  const_get(constants.find { |c| 
    cl = const_get c
    (cl.ancestors.include?(self) && 
     c.to_s.downcase == doctype) rescue false
  }) rescue nil
end

.build(*args, &block) ⇒ Object

Create and render a view via a block.



81
82
83
84
85
# File 'lib/hoshi/view.rb', line 81

def self.build(*args, &block)
  c = new(*args)
  c.instance_eval(&block)
  c.render
end

.content_typeObject

This is overridden in HTML/XHTML, and you’ll definitely want to override it if you subclass View directly.



89
90
91
# File 'lib/hoshi/view.rb', line 89

def self.content_type
  'application/octet-stream'
end

.dtd!(dtd) ⇒ Object

Sets the doctype declaration for this class.



50
51
52
53
# File 'lib/hoshi/view.rb', line 50

def self.dtd! dtd
  dtd += "\n"
  define_method(:doctype) { append! dtd }
end

.open_tags(*names) ⇒ Object

A short-hand for creating multiple tags that are left open.



27
28
29
# File 'lib/hoshi/view.rb', line 27

def self.open_tags *names
  names.map { |n| tag n, :none }
end

.permissive!Object

Free-form tags. Basically, dynamic tag creation by method_missing.



60
61
62
# File 'lib/hoshi/view.rb', line 60

def self.permissive!
  @permissive = true
end

.permissive?Boolean

Returns true if we add tags to this class on the fly.

Returns:

  • (Boolean)


65
66
67
# File 'lib/hoshi/view.rb', line 65

def self.permissive?
  @permissive
end

.self_closing_tags(*names) ⇒ Object



31
32
33
# File 'lib/hoshi/view.rb', line 31

def self.self_closing_tags *names
  names.map { |n| tag n, :self }
end

.strict!Object

Only the tags already specified are allowed. No dynamic tag creation. This is the default.



71
72
73
# File 'lib/hoshi/view.rb', line 71

def self.strict!
  @permissive = false
end

.strict?Boolean

Returns true if we do not add tags to the class on the fly.

Returns:

  • (Boolean)


76
77
78
# File 'lib/hoshi/view.rb', line 76

def self.strict?
  !permissive?
end

.tag(name, close_type = nil) ⇒ Object

This creates an instance method for this view which appends a tag. Most of these are handled for you. The arguments to this method match those to Tag.new. See also View#permissive!. tag(‘h1’) def show_an_h1 h1 “I have been shown” end



10
11
12
13
14
15
16
17
18
19
20
# File 'lib/hoshi/view-tag.rb', line 10

def self.tag(name, close_type = nil)
  define_method(name) { |*opts,&b|
    if b
      tag name, close_type, *opts, &b
    else
      tag name, close_type, *opts
    end
  }
  # Inline tags.
  define_method("_#{name}") { |*opts| _tag name, close_type, *opts }
end

.tags(*names) ⇒ Object

A short-hand for creating multiple tags via View.tag. For tags that do not require closing, see View.open_tags.



22
23
24
# File 'lib/hoshi/view.rb', line 22

def self.tags *names
  names.map &method(:tag)
end

Instance Method Details

#_tag(tname, close_type = nil, inside = '', opts = {}) ⇒ Object

An inline tag; it just returns a string rather than updating the view object in place. Useful for things like p “Here is a paragraph and a #‘link’, :href => ‘/’.”



145
146
147
148
# File 'lib/hoshi/view.rb', line 145

def _tag(tname, close_type = nil, inside = '', opts = {})
  t = otag(tname, close_type)
  t.render(inside, opts)
end

#append!(x) ⇒ Object

Appends something to the document. The comment, decl, and various tag methods call this.



152
153
154
155
# File 'lib/hoshi/view.rb', line 152

def append! x
  current << x
  x
end

#clear!Object

Clears the current state of this view.



102
103
104
105
# File 'lib/hoshi/view.rb', line 102

def clear!
  self.tree = []
  self.current = tree
end

#comment(*a) ⇒ Object

Adds a comment.



112
113
114
115
116
117
118
# File 'lib/hoshi/view.rb', line 112

def comment(*a)
  if a.include?('--')
    raise ValidationError, "Comments can't include '--'."
  else
    append! "<!-- #{a} -->"
  end
end

#doc(&b) ⇒ Object

If you’re tired of typing “doctypenhtml” every single time.



158
159
160
161
# File 'lib/hoshi/view.rb', line 158

def doc &b
  doctype
  html &b
end

#doctypeObject



54
55
56
57
# File 'lib/hoshi/view.rb', line 54

def doctype
  comment "No doctype defined; are you sub-classing View directly " \
    "and not calling dtd!()?"
end

#entity(e) ⇒ Object



120
121
122
# File 'lib/hoshi/view.rb', line 120

def entity e
  raw "&#{e};"
end

#otag(*a) ⇒ Object



107
108
109
# File 'lib/hoshi/view.rb', line 107

def otag(*a)
  @tcache[a] ||= Tag.new(*a)
end

#raw(*things) ⇒ Object

Appends one or more non-escaped strings to the document.



170
171
172
# File 'lib/hoshi/view.rb', line 170

def raw *things
  append! things.join
end

#renderObject

Returns the string representation of the document. This is what you want to eventually call.



176
177
178
# File 'lib/hoshi/view.rb', line 176

def render
  tree.flatten.map(&:to_s).join
end

#render_cgi(extra_headers = {}) ⇒ Object

Prints the string representation of the docutment, with HTTP headers. Useful for one-off CGI scripts. Takes an optional hash argument for headers (Content-Type and Status are set by default). See CGI#header for information on how the header hash should look.



184
185
186
187
188
189
190
191
# File 'lib/hoshi/view.rb', line 184

def render_cgi(extra_headers = {})
  h = { 
    'type' => self.class.content_type,
    'status' => 'OK',
  }.merge(extra_headers)

  CGI.new.out(h) { render }
end

#safe(*things) ⇒ Object

Turns things in to strings, properly escapes them, and appends them to the document.



165
166
167
# File 'lib/hoshi/view.rb', line 165

def safe *things
  append! CGI.escapeHTML(things.map(&:to_s).join("\n"))
end

#tag(tname, close_type = nil, *opts, &b) ⇒ Object

Appends a tag to the current document, for when a tag is only needed once or has a name that is not a valid method name.



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/hoshi/view.rb', line 126

def tag(tname, close_type = nil, *opts, &b)
  t = otag(tname, close_type)

  if b
    old, self.current = current, []
    # These two lines let you do 'asdf { "jkl" }' like Markaby.
    r = b.call
    current << r.to_s if current.empty?
    inside, self.current = current.map(&:to_s).join, old
  elsif opts.first.kind_of? String
    inside = CGI.escapeHTML(opts.shift)
  end

  append! t.render(inside, opts.first || {})
end