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
# File 'lib/hoshi/view.rb', line 96

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.



189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/hoshi/view.rb', line 189

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



140
141
142
143
# File 'lib/hoshi/view.rb', line 140

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.



147
148
149
150
# File 'lib/hoshi/view.rb', line 147

def append! x
	current << x
	x
end

#clear!Object

Clears the current state of this view.



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

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

#comment(*a) ⇒ Object

Adds a comment.



107
108
109
110
111
112
113
# File 'lib/hoshi/view.rb', line 107

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.



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

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



115
116
117
# File 'lib/hoshi/view.rb', line 115

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

#raw(*things) ⇒ Object

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



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

def raw *things
	append! things.join
end

#renderObject

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



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

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.



179
180
181
182
183
184
185
186
# File 'lib/hoshi/view.rb', line 179

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.



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

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.



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/hoshi/view.rb', line 121

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