tiny 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .DS_Store
6
+ *.swp
7
+ *.swo
8
+ doc
9
+ .yardoc
10
+ tags
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in markup_helpers.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,388 @@
1
+ # tiny
2
+
3
+ Tiny is a framework agnostic markup builder. It is useful for defining
4
+ view helpers or generating HTML markup using ruby objects, leveraging
5
+ inheritance and composition while defining templates.
6
+
7
+ It is inspired by Erector and Markaby but with a minimalistic aproach
8
+ and it opts for evaluating content blocks in their original context
9
+ rather than using instance\_eval thus instance variables need not to be
10
+ "smuggled in". It also attempts to be a tiny framework for defining view
11
+ helpers to be used in ERB and HAML templates from Rails, Sinatra or any
12
+ other framework.
13
+
14
+ It provides a mixin for inline building HTML markup from any class or to
15
+ define pure ruby object templates with all object-oriented programming
16
+ advantages such as inheritance and encapsulation.
17
+
18
+ Tiny is pretty much fully documented. Please check [Tiny
19
+ Rdoc](http://rdoc.info/github/maca/tiny/master/file/README.md) for more
20
+ info.
21
+
22
+
23
+ ## Install
24
+
25
+ $ gem install tiny
26
+
27
+ ## Usage
28
+
29
+ require 'tiny'
30
+
31
+ class MyPage < Tiny::Widget
32
+ def markup
33
+ html do
34
+ head do
35
+ title "Hello"
36
+ end
37
+ body do
38
+ h1 "Hello"
39
+ p :class => 'content' do
40
+ text "Lorem ipsum..."
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ MyPage.new.to_html
47
+ # => <html>
48
+ <head>
49
+ <title>Hello</title>
50
+ </head>
51
+ <body>
52
+ <h1>Hello</h1>
53
+ <p>
54
+ Lorem ipsum...
55
+ </p>
56
+ </body>
57
+ </html>
58
+
59
+
60
+ ## Markup Helpers
61
+
62
+ There are a few was of generating markup with Tiny, while one is by
63
+ encapsulated classes inheriting from `Tiny::Widget`, another one is
64
+ doing it inline by including `Tiny::Helpers` in any context.
65
+
66
+ Including `Tiny::Helpers` gives access to a handfull of methods, the
67
+ basic one is `html_tag` aliased as `tag`.
68
+
69
+ include Tiny::Helpers
70
+
71
+ tag(:ul) do
72
+ tag(:li) do
73
+ tag :a, 'Home', :class => 'home', :href => '/'
74
+ end
75
+ ...
76
+ end
77
+ # => <ul>
78
+ <li>
79
+ <a class="home" href="/">Home</a>
80
+ </li>
81
+ ...
82
+ </ul>
83
+
84
+ ## HTML tags
85
+
86
+ Tags are self closed or explicitly closed depeding on the tag name.
87
+ Attributes are HTML-escaped and mapped as follows:
88
+
89
+ tag(:link, :href => 'my-styles.css')
90
+ # => <link href="my-styles.css" />
91
+ tag(:li, 'Bicycle', :class => ['with-discount', 'in-stock'])
92
+ # => <li class="with-discount in-stock">Bicycle</li>
93
+ tag(:textarea, :disabled => true)
94
+ # => <textarea disabled></textarea>
95
+ tag(:textarea, :disabled => false)
96
+ # => <textarea></textarea>
97
+
98
+ Tag content can be defined either by passing a string and optionally an
99
+ attributes hash or by passing a content block.
100
+
101
+ ## Markup
102
+
103
+ Other methods for generating markup are `text` for appending HTML
104
+ escaped `text`, `text!` or `append!` for appending HTML, `comment`, `cdata`
105
+ and `doctype`.
106
+
107
+ The method `with_buffer` is for capturing template content, just like
108
+ Rails `capture` but it also serves for concatenating content.
109
+
110
+ with_buffer do
111
+ tag(:h1, "Hello")
112
+ tag(:p, "Lorem ipsum...")
113
+ end
114
+ # => <h1>Hello</h1>
115
+ <p>Lorem ipsum...</p>
116
+
117
+ ## Rails
118
+
119
+ Tiny ActionView helpers are allready included in ActionView, no further
120
+ step is required for using Tiny in Rails view helpers, just use `html_tag`
121
+ instead of `tag` because ActionView allready defines `tag`.
122
+
123
+ The advantage over Rails' markup method such as `tag` and `content\_tag` is
124
+ that generated strings need not to be explicitly concatenated.
125
+
126
+ In addition to defining view helpers to be used from templates, a
127
+ `Widget` can substitute a template view with the benefit of inheritance.
128
+ No template handler es provided but is not cumbersome explicitly
129
+ rendering the Widget.
130
+
131
+ controller Products
132
+ def index
133
+ products = Product.all
134
+ render :text => MyProductList.new(products).to_html
135
+ end
136
+ ...
137
+ end
138
+
139
+ ## Sinatra
140
+
141
+ For using the markup helpers:
142
+
143
+ class MyApp < Sinatra::Base
144
+ helpers Tiny::Helpers
145
+
146
+ get '/home' do
147
+ with_buffer do
148
+ doctype
149
+ html do
150
+ ...
151
+ end
152
+ end
153
+ end
154
+ end
155
+
156
+ Rendering `Tiny::Widgets`
157
+
158
+ class MyApp < Sinatra::Base
159
+ get '/products' do
160
+ products = Product.all
161
+ MyProductList.new(products).to_html
162
+ end
163
+ end
164
+
165
+
166
+ ## Shortcuts
167
+
168
+ Including `Tiny::HTML` gives access to shortcuts for HTML tags. Caution
169
+ must be exercised because its quite a few methods.
170
+
171
+ ## View Inheritance
172
+
173
+ class Template < Tiny::Widget
174
+ def markup
175
+ doctype
176
+ html do
177
+ head do
178
+ title @title
179
+ end
180
+ body do
181
+ navigation
182
+ section(:id => 'content') do
183
+ yield
184
+ end
185
+ footer_content
186
+ end
187
+ end
188
+ end
189
+
190
+ def navigation
191
+ nav(:id) do
192
+ tag(:ul) do
193
+ tag(:li) do
194
+ tag :a, 'Home', :class => 'home', :href => '/'
195
+ end
196
+ tag(:li) do
197
+ tag :a, 'About', :class => 'about', :href => '/about'
198
+ end
199
+ tag(:li) do
200
+ tag :a, 'Home', :class => 'products', :href => '/products'
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ def footer_content
207
+ footer "© 2012"
208
+ end
209
+ end
210
+
211
+ class HomePage < Template
212
+ def initialize
213
+ @title = "Home"
214
+ end
215
+
216
+ def markup
217
+ super do
218
+ h1 "Welcome!!"
219
+ p "Lorem ipsum..."
220
+ end
221
+ end
222
+ end
223
+
224
+ HomePage.new.to_html
225
+ # => <!DOCTYPE html>
226
+ <html>
227
+ <head>
228
+ <title>Home</title>
229
+ </head>
230
+ <body>
231
+ <nav>
232
+ <ul>
233
+ <li>
234
+ <a class="home" href="/">Home</a>
235
+ </li>
236
+ <li>
237
+ <a class="about" href="/about">About</a>
238
+ </li>
239
+ <li>
240
+ <a class="products" href="/products">Home</a>
241
+ </li>
242
+ </ul>
243
+ </nav>
244
+ <section id="content">
245
+ <h1>Welcome!!</h1>
246
+ <p>Lorem ipsum...</p>
247
+ </section>
248
+ <footer>
249
+ © 2012
250
+ </footer>
251
+ </body>
252
+ </html>
253
+
254
+
255
+ ## View helpers for HAML and ERB templates
256
+
257
+ One of the Tiny's main goals is providing facilities for defining view
258
+ helpers that can be used from Ruby or templating laguages regardless of
259
+ the web framework.
260
+
261
+ A Widget can take a block while calling `to_html`. Tiny can determine
262
+ wether the block was originated in an ERB or HAML template or not and
263
+ treat it accordingly. #to\_html forwards the passed block to #markup but
264
+ concatenates the result of calling it.
265
+
266
+ class MyForm < Tiny::Widget
267
+ def initialize(action)
268
+ @action = action
269
+ end
270
+
271
+ def markup
272
+ form(:action => @action) do
273
+ fieldset do
274
+ yield(self)
275
+ end
276
+ end
277
+ end
278
+
279
+ def text_input(name, value)
280
+ TextInput.new(name, value).to_html
281
+ end
282
+ end
283
+
284
+ class TextInput < Tiny::Widget
285
+ def initialize(name, value)
286
+ @name, @value = name, value
287
+ end
288
+
289
+ def markup
290
+ label(@name.capitalize, :for => @name)
291
+ input(:type => 'text', :id => @name, :name => @name, :value => @value)
292
+ end
293
+ end
294
+
295
+ def my_form(action, &block)
296
+ # the block is forwarded to MyForm#to_html
297
+ MyForm.new(action).to_html(&block)
298
+ end
299
+
300
+ Using the helper from an ERB template, note that Tiny allows explicitly
301
+ concatenating calls with blocks just like with Rails ERB.
302
+
303
+ <%= my_form('/login') do |form| %>
304
+ <%= form.text_input 'email', 'email@example.com' %>
305
+ <% end %>
306
+ # => <form action="/login">
307
+ ...
308
+ <fieldset>
309
+ <label for="email">Email</label>
310
+ <input type="text" id="email" name="email" value="email@example.com" />
311
+ </fieldset>
312
+ ...
313
+ </form>
314
+
315
+ Using the same helper from Ruby:
316
+
317
+ my_form('/login') do |form|
318
+ append! form.text_input 'email', 'email@example.com'
319
+ end
320
+ # => <form action="/login">
321
+ ...
322
+ <fieldset>
323
+ <label for="email">Email</label>
324
+ <input type="text" id="email" name="email" value="email@example.com" />
325
+ </fieldset>
326
+ ...
327
+ </form>
328
+
329
+ ## HTML Representation For Any Object.
330
+
331
+ By including the `Rendering` any object can emit it's HTML representation.
332
+ Whether this is or isn't a good idea is up to you.
333
+
334
+ class User < Model
335
+ include Tiny::Rendering
336
+
337
+ def markup
338
+ div(:id => "user-#{self.id}") do
339
+ img :src => self.avatar_url
340
+ dl do
341
+ dt "First Name"
342
+ dd self.first_name
343
+ dt "Last Name"
344
+ dd self.last_name
345
+ end
346
+ end
347
+ end
348
+ end
349
+
350
+ user = User.create(:first_name => 'Macario',
351
+ :last_name => 'Ortega',
352
+ :avatar_url => 'http://example.com/profile/dbg.jpeg')
353
+ user.to_html
354
+ # => <div id="user-1">
355
+ <img src="http://example.com/profile/dbg.jpeg" />
356
+ <dl>
357
+ <dt>First Name</dt>
358
+ <dd>Macario</dd>
359
+ <dt>Last Name</dt>
360
+ <dd>Ortega</dd>
361
+ </dl>
362
+ </div>
363
+
364
+
365
+ == LICENSE:
366
+
367
+ (The MIT License)
368
+
369
+ Copyright (c) 2012 Macario Ortega
370
+
371
+ Permission is hereby granted, free of charge, to any person obtaining
372
+ a copy of this software and associated documentation files (the
373
+ 'Software'), to deal in the Software without restriction, including
374
+ without limitation the rights to use, copy, modify, merge, publish,
375
+ distribute, sublicense, and/or sell copies of the Software, and to
376
+ permit persons to whom the Software is furnished to do so, subject to
377
+ the following conditions:
378
+
379
+ The above copyright notice and this permission notice shall be
380
+ included in all copies or substantial portions of the Software.
381
+
382
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
383
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
384
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
385
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
386
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
387
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
388
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/lib/tiny.rb ADDED
@@ -0,0 +1,519 @@
1
+ require 'erubis'
2
+ require 'haml'
3
+ require 'tilt'
4
+ require 'escape_utils'
5
+
6
+ require 'tiny/version'
7
+ require 'tiny/erubis'
8
+ require 'tiny/safe_string'
9
+ require 'tiny/tag'
10
+ require 'tiny/html'
11
+
12
+ module Tiny
13
+ # Provides basic markup generation support.
14
+ module Markup
15
+ # Generates an HTML tag or structured HTML markup
16
+ #
17
+ # This method is the basis for defining html helper functions or
18
+ # constructing html markup using pure ruby.
19
+ #
20
+ # HTML attributes are HTML-escaped and tags are explicitly or self
21
+ # closed depeding on the tag name.
22
+ #
23
+ # Calls to markup generating methods within the content block are
24
+ # appended to the tag's content.
25
+ # See {Markup} and {HTML}.
26
+ #
27
+ # Content blocks originating from HAML or ERB templates are
28
+ # correctly captured and handled.
29
+ #
30
+ # @param name [Symbol, String] The name of the tag.
31
+ # @param attrs_or_content [Hash, String] Tag's attributes or content.
32
+ # @param attrs [Hash] Tag's attributes if content string passed.
33
+ # @yield Content block.
34
+ #
35
+ # @return [String] HTML markup
36
+ #
37
+ # @example Attribute mapping
38
+ #
39
+ # html_tag(:link, :href => 'my-styles.css')
40
+ # # => <link href="my-styles.css" />
41
+ # html_tag(:li, 'Bicycle', :class => ['with-discount', 'in-stock'])
42
+ # # => <li class="with-discount in-stock">Bicycle</li>
43
+ # html_tag(:textarea, :disabled => true)
44
+ # # => <textarea disabled></textarea>
45
+ # html_tag(:textarea, :disabled => false)
46
+ # # => <textarea></textarea>
47
+ #
48
+ # @example HTML-escaping
49
+ #
50
+ # html_tag(:a, 'Art&Copy', :href => '/art&copy')
51
+ # # => <a href="/art&amp;copy">Art&amp;copy</a>
52
+ #
53
+ # @example Tag closing
54
+ #
55
+ # html_tag(:p) # => <p></p>
56
+ # html_tag(:link) # => <link />
57
+ #
58
+ # @example Content block
59
+ #
60
+ # html_tag(:ul) do
61
+ # html_tag(:li, 'Cheese')
62
+ # html_tag(:li, 'Ham')
63
+ # html_tag(:li, 'Milk')
64
+ # end
65
+ # # => <ul>
66
+ # <li>Cheese</li>
67
+ # <li>Haml</li>
68
+ # <li>Milk</li>
69
+ # </ul>
70
+ #
71
+ # html_tag(:p) do
72
+ # text 'Neque porro quisquam est qui dolorem...'
73
+ # 'this string will be ignored'
74
+ # end
75
+ # # => <p>
76
+ # Neque porro quisquam est qui dolorem...
77
+ # </p>
78
+ #
79
+ # @example ERB blocks
80
+ #
81
+ # !!!ruby
82
+ # # application_helper.rb
83
+ # module ApplicationHelper
84
+ # def my_form(url, &block)
85
+ # html_tag(:form, :action => url) do
86
+ # ...
87
+ # html_tag(:fieldset, &block)
88
+ # ...
89
+ # end
90
+ # end
91
+ #
92
+ # def text_input(name, value)
93
+ # html_tag(:input, :type => 'text', :name => name, :value => value)
94
+ # end
95
+ # end
96
+ #
97
+ # # form.erb
98
+ # <%= my_form '/login' do %>
99
+ # <%= text_input 'email', @email %>
100
+ # <% end %>
101
+ # # => <form action="/login">
102
+ # ...
103
+ # <fieldset>
104
+ # <input type="text" name="email" value="email@example.com" />
105
+ # </fieldset>
106
+ # ...
107
+ # </form>
108
+ #
109
+ #
110
+ def html_tag name, attrs_or_content = {}, attrs = nil, &block
111
+ append! Tag.new(name, attrs_or_content, attrs).render(&block)
112
+ end
113
+
114
+ # Appends an HTML coment to the content.
115
+ #
116
+ # div do
117
+ # comment 'foo'
118
+ # end
119
+ # # => <div>
120
+ # <!-- foo -->
121
+ # </div>
122
+ #
123
+ # @return [SafeString] HTML content.
124
+ #
125
+ def comment content
126
+ append! "<!-- #{content.to_s.gsub(/-(?=-)/, '- ')} -->"
127
+ end
128
+
129
+ # Appends a CDATA section to the content.
130
+ #
131
+ # div do
132
+ # cdata 'foo'
133
+ # end
134
+ # # => <div>
135
+ # <![CDATA[foo]]>
136
+ # </div>
137
+ #
138
+ # @return [String] CDATA section.
139
+ #
140
+ def cdata content
141
+ content = content.to_s.gsub(']]>', ']]]]><![CDATA[>')
142
+ append! "<![CDATA[#{content}]]>"
143
+ end
144
+
145
+ # Appends the doctype to the content
146
+ #
147
+ # with_buffer do
148
+ # doctype
149
+ # html_tag(:html) do
150
+ # ...
151
+ # end
152
+ # end
153
+ # # => <!DOCTYPE html>
154
+ # <html>
155
+ # ...
156
+ # </html>
157
+ #
158
+ # @return [String] Doctype.
159
+ #
160
+ def doctype
161
+ append! "<!DOCTYPE html>"
162
+ end
163
+ end
164
+
165
+ # Buffering and capturing support.
166
+ module Buffering
167
+ # Appends sanitized text to the content.
168
+ #
169
+ # html_tag(:p) do
170
+ # text 'Foo &'
171
+ # text 'Bar'
172
+ # end
173
+ # # => <p>
174
+ # Foo &amp;
175
+ # Bar
176
+ # </p>
177
+ #
178
+ # @return [String] HTML-escaped.
179
+ #
180
+ def append string
181
+ string = Helpers.sanitize(string)
182
+ if working_buffer
183
+ working_buffer << string.gsub(/(?<!^|\n)\z/, "\n")
184
+ else
185
+ string
186
+ end
187
+ end
188
+ alias text append
189
+
190
+ # Appends non HTML-escaped text to the content.
191
+ #
192
+ # html_tag(:p) do
193
+ # append! 'Foo & Bar'
194
+ # append! '<evil>'
195
+ # end
196
+ # # => <p>
197
+ # Foo & Bar
198
+ # <evil>
199
+ # </p>
200
+ #
201
+ # Shortcut for
202
+ #
203
+ # append raw(content)
204
+ #
205
+ # @return [SafeString] Non HTML-escaped.
206
+ #
207
+ def append! content
208
+ append raw(content)
209
+ end
210
+ alias text! append!
211
+
212
+ # Returns content that won't be HTML escaped when appended to content.
213
+ #
214
+ # @return [SafeString] Considered html safe.
215
+ #
216
+ def raw val
217
+ SafeString.new val.to_s
218
+ end
219
+
220
+ # Buffers calls to markup generating methods.
221
+ # @see Markup
222
+ # @see HTML
223
+ #
224
+ # @example Not using #with_buffer Only the last tag is returned.
225
+ # def my_helper
226
+ # html_tag(:span, 'Foo')
227
+ # html_tag(:span, 'Bar')
228
+ # end
229
+ # my_helper()
230
+ # # => <span>Bar</span>
231
+ #
232
+ # @example By using #with_buffer structured markup is generated.
233
+ # def my_helper
234
+ # with_buffer do
235
+ # html_tag(:span, 'Foo')
236
+ # html_tag(:span, 'Bar')
237
+ # end
238
+ # end
239
+ # my_helper()
240
+ # # => <span>Foo</span>
241
+ # <span>Bar</span>
242
+ #
243
+ # @param args [any] n number of arguments to be passed to block evaluation.
244
+ # @yield [*args] Content block.
245
+ #
246
+ # @return [String] HTML markup.
247
+ #
248
+ def with_buffer *args, &block
249
+ buffer_stack << ''
250
+ yield *args
251
+ buffer_stack.pop
252
+ end
253
+
254
+ private
255
+ # Pushing and popping.
256
+ def buffer_stack
257
+ @buffer_stack ||= []
258
+ end
259
+
260
+ # Current buffer from the buffer stack.
261
+ def working_buffer
262
+ buffer_stack.last
263
+ end
264
+ end
265
+
266
+ # Provides support for using Tiny helpers within a HAML template.
267
+ module HamlTemplating
268
+ include Buffering
269
+ include Markup
270
+
271
+ # Extracts a section of a HAML template or buffers a block not
272
+ # originated from an HAML template. Akin to Rails capture method.
273
+ #
274
+ # @see Buffering#with_buffer
275
+ #
276
+ # @param args [any] n number of arguments to be passed to block evaluation.
277
+ # @yield [*args] HAML block or content block.
278
+ # @return [String] HTML markup.
279
+ #
280
+ def with_buffer *args, &block
281
+ ::Haml::Helpers.block_is_haml?(block) ? capture_haml(*args, &block) : super
282
+ end
283
+ end
284
+
285
+ # Provides support for using Tiny helpers within an Erubis template.
286
+ module ErubisTemplating
287
+ include Buffering
288
+ include Markup
289
+
290
+ # Extracts a section of a ERB template or buffers a block not
291
+ # originated from an ERB template. Akin to Rails capture method.
292
+ #
293
+ # @see Buffering#with_buffer
294
+ #
295
+ # @param args [any] n number of arguments to be passed to block evaluation.
296
+ # @yield [*args] ERB block or content block.
297
+ # @return [String] HTML markup.
298
+ #
299
+ def with_buffer *args, &block
300
+ erb_block?(block) ? capture_erb(*args, &block) : super
301
+ end
302
+
303
+ # Capture a section of an ERB template.
304
+ #
305
+ # @param args [any] n number of arguments to be passed to block evaluation
306
+ # @return [String] HTML markup
307
+ # @yield [*args]
308
+ #
309
+ def capture_erb *args, &block
310
+ output_buffer = eval('_buf', block.binding)
311
+ buffer = output_buffer.dup
312
+ output_buffer.clear and yield(*args)
313
+ return output_buffer.dup
314
+ ensure
315
+ output_buffer.replace buffer
316
+ end
317
+
318
+ # Was the block defined within an ERB template?
319
+ #
320
+ # @param block [Proc] a Proc object
321
+ #
322
+ def erb_block? block
323
+ block && eval('defined?(__in_erb_template)', block.binding)
324
+ end
325
+ end
326
+
327
+ # Include this module anywhere you want to use markup generation, or
328
+ # define view helpers using Tiny.
329
+ module Helpers
330
+ include Buffering
331
+ include Markup
332
+ include ErubisTemplating
333
+ include HamlTemplating
334
+
335
+ # Alias for {Markup#html_tag}
336
+ #
337
+ # @return [String] HTML markup
338
+ #
339
+ def tag name, attrs_or_content = {}, attrs = nil, &block
340
+ html_tag name, attrs_or_content, attrs, &block
341
+ end
342
+
343
+ # HTML-escapes the passed value unless the content is considered
344
+ # safe ({SafeString#html_safe? html_safe?} is implemented and
345
+ # returns true)
346
+ #
347
+ # @param value [String, Object]
348
+ # @return [String]
349
+ #
350
+ def self.sanitize value
351
+ if value.respond_to?(:html_safe?) && value.html_safe?
352
+ value.to_s
353
+ else
354
+ EscapeUtils.escape_html value.to_s
355
+ end
356
+ end
357
+ end
358
+
359
+ # Support for HTML markup generation for classes, can be included in
360
+ # any class that is to be represented with HTML markup.
361
+ # @see Widget
362
+ #
363
+ # @example
364
+ # class User < Model
365
+ # include Tiny::Rendering
366
+ #
367
+ # def markup
368
+ # div(:id => "user-#{self.id}") do
369
+ # img :src => self.avatar_url
370
+ # dl do
371
+ # dt "First Name"
372
+ # dd self.first_name
373
+ # dt "Last Name"
374
+ # dd self.last_name
375
+ # end
376
+ # end
377
+ # end
378
+ # end
379
+ #
380
+ # user = User.create(:first_name => 'Macario',
381
+ # :last_name => 'Ortega',
382
+ # :avatar_url => 'http://example.com/profile/dbg.jpeg')
383
+ # user.to_html
384
+ # # => <div id="user-1">
385
+ # <img src="http://example.com/profile/dbg.jpeg" />
386
+ # <dl>
387
+ # <dt>First Name</dt>
388
+ # <dd>Macario</dd>
389
+ # <dt>Last Name</dt>
390
+ # <dd>Ortega</dd>
391
+ # </dl>
392
+ # </div>
393
+ #
394
+ module Rendering
395
+ include HTML
396
+ include Helpers
397
+
398
+ # Override this method with specific markup.
399
+ #
400
+ # @yield [self] Content block (from calling to {#render}).
401
+ # @see Widget
402
+ #
403
+ def markup
404
+ raise NotImplementedError
405
+ end
406
+
407
+ # Renders the html markup specified by #markup.
408
+ #
409
+ # @return [String] HTML markup.
410
+ # @see Widget.
411
+ #
412
+ def render &block
413
+ with_buffer do
414
+ next markup unless block_given?
415
+ markup do |args|
416
+ context = eval('self', block.binding)
417
+ append! context.instance_eval{ with_buffer(*args, &block) }
418
+ end
419
+ end
420
+ end
421
+ alias to_html render
422
+ end
423
+
424
+ module ActionViewAdditions
425
+ include Buffering
426
+ include Markup
427
+
428
+ # Extracts a section of a template or buffers a block not
429
+ # originated from an template.
430
+ #
431
+ # @see Buffering#with_buffer
432
+ #
433
+ # @param args [any] n number of arguments to be passed to block evaluation.
434
+ # @yield [*args] HAML block or content block.
435
+ # @return [String] HTML markup.
436
+ #
437
+ def with_buffer *args, &block
438
+ block_from_template?(block) ? capture(*args, &block) : super
439
+ end
440
+
441
+ # Appends sanitized text to the content.
442
+ # @see Buffering#append
443
+ def append markup
444
+ super(markup).html_safe
445
+ end
446
+
447
+ # Returns true if the block was originated in an ERB or HAML template.
448
+ def block_from_template? block
449
+ block && eval('defined?(output_buffer)', block.binding) == 'local-variable'
450
+ end
451
+ end
452
+
453
+ # @example
454
+ # class MyForm < Tiny::Widget
455
+ # def initialize(action)
456
+ # @action = action
457
+ # end
458
+ #
459
+ # def markup
460
+ # form(:action => @action) do
461
+ # fieldset do
462
+ # yield(self)
463
+ # end
464
+ # end
465
+ # end
466
+ #
467
+ # def text_input(name, value)
468
+ # TextInput.new(name, value).to_html
469
+ # end
470
+ # end
471
+ #
472
+ # class TextInput < Tiny::Widget
473
+ # def initialize(name, value)
474
+ # @name, @value = name, value
475
+ # end
476
+ #
477
+ # def markup
478
+ # label(@name.capitalize, :for => @name)
479
+ # input(:type => 'text', :id => @name, :name => @name, :value => @value)
480
+ # end
481
+ # end
482
+ #
483
+ # def my_form(action, &block)
484
+ # MyForm.new(action).to_html(&block)
485
+ # end
486
+ #
487
+ # my_form('/login') do |form|
488
+ # append! form.text_input 'email', 'email@example.com'
489
+ # end
490
+ # # => <form action="/login">
491
+ # ...
492
+ # <fieldset>
493
+ # <label for="email">Email</label>
494
+ # <input type="text" id="email" name="email" value="email@example.com" />
495
+ # </fieldset>
496
+ # ...
497
+ # </form>
498
+ #
499
+ # # from template
500
+ # <%= my_form('/login') do |form| %>
501
+ # <%= form.text_input 'email', 'email@example.com' %>
502
+ # <% end %>
503
+ # # => <form action="/login">
504
+ # ...
505
+ # <fieldset>
506
+ # <label for="email">Email</label>
507
+ # <input type="text" id="email" name="email" value="email@example.com" />
508
+ # </fieldset>
509
+ # ...
510
+ # </form>
511
+ #
512
+ # @see Rendering
513
+ #
514
+ class Widget
515
+ include Rendering
516
+ end
517
+
518
+ ActionView::Base.send :include, ActionViewAdditions if defined?(ActionView)
519
+ end