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-tag-fallback.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.


100
101
102
# File 'lib/hoshi/view.rb', line 100

def initialize
	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.


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

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.


44
45
46
47
48
49
50
51
# File 'lib/hoshi/view.rb', line 44

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.


85
86
87
88
89
# File 'lib/hoshi/view.rb', line 85

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.


93
94
95
# File 'lib/hoshi/view.rb', line 93

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

.dtd!(dtd) ⇒ Object

Sets the doctype declaration for this class.


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

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.


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

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

.permissive!Object

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


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

def self.permissive!
	@permissive = true
end

.permissive?Boolean

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

Returns:

  • (Boolean)

69
70
71
# File 'lib/hoshi/view.rb', line 69

def self.permissive?
	@permissive
end

.self_closing_tags(*names) ⇒ Object


35
36
37
# File 'lib/hoshi/view.rb', line 35

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.


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

def self.strict!
	@permissive = false
end

.strict?Boolean

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

Returns:

  • (Boolean)

80
81
82
# File 'lib/hoshi/view.rb', line 80

def self.strict?
	!permissive?
end

.tag(name, close_type = nil) ⇒ Object

This method is a fallback for pre-1.8.7 Ruby; if a syntax error is encountered when hoshi/view.rb tries to load hoshi/view-tag.rb, this file is loaded. I have really been trying to avoid eval()ing strings (for religious reasons), but have to make this concession.


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.


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

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 => ‘/’.”


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

def _tag(tname, close_type = nil, inside = '', opts = {})
	t = Tag.new(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.


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

def append! x
	current << x
	x
end

#clear!Object

Clears the current state of this view.


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

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

#comment(*a) ⇒ Object

Adds a comment.


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

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.


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

def doc &b
	doctype
	html &b
end

#doctypeObject


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

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

#entity(e) ⇒ Object


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

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

#raw(*things) ⇒ Object

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


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

def raw *things
	append! things.join
end

#renderObject

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


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

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.


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

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.


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

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.


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

def tag(tname, close_type = nil, *opts, &b)
	t = Tag.new(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