padrino-helpers 0.1.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/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +7 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/lib/padrino-helpers.rb +16 -0
- data/lib/padrino-helpers/asset_tag_helpers.rb +97 -0
- data/lib/padrino-helpers/form_builder/abstract_form_builder.rb +139 -0
- data/lib/padrino-helpers/form_builder/standard_form_builder.rb +37 -0
- data/lib/padrino-helpers/form_helpers.rb +194 -0
- data/lib/padrino-helpers/format_helpers.rb +74 -0
- data/lib/padrino-helpers/output_helpers.rb +98 -0
- data/lib/padrino-helpers/render_helpers.rb +63 -0
- data/lib/padrino-helpers/tag_helpers.rb +42 -0
- data/test/active_support_helpers.rb +7 -0
- data/test/fixtures/markup_app/app.rb +61 -0
- data/test/fixtures/markup_app/views/capture_concat.erb +14 -0
- data/test/fixtures/markup_app/views/capture_concat.haml +13 -0
- data/test/fixtures/markup_app/views/content_for.erb +11 -0
- data/test/fixtures/markup_app/views/content_for.haml +9 -0
- data/test/fixtures/markup_app/views/content_tag.erb +11 -0
- data/test/fixtures/markup_app/views/content_tag.haml +9 -0
- data/test/fixtures/markup_app/views/fields_for.erb +8 -0
- data/test/fixtures/markup_app/views/fields_for.haml +6 -0
- data/test/fixtures/markup_app/views/form_for.erb +56 -0
- data/test/fixtures/markup_app/views/form_for.haml +47 -0
- data/test/fixtures/markup_app/views/form_tag.erb +57 -0
- data/test/fixtures/markup_app/views/form_tag.haml +45 -0
- data/test/fixtures/markup_app/views/link_to.erb +5 -0
- data/test/fixtures/markup_app/views/link_to.haml +4 -0
- data/test/fixtures/markup_app/views/mail_to.erb +3 -0
- data/test/fixtures/markup_app/views/mail_to.haml +3 -0
- data/test/fixtures/render_app/app.rb +53 -0
- data/test/fixtures/render_app/views/erb/test.erb +1 -0
- data/test/fixtures/render_app/views/haml/test.haml +1 -0
- data/test/fixtures/render_app/views/template/_user.haml +7 -0
- data/test/fixtures/render_app/views/template/haml_template.haml +1 -0
- data/test/fixtures/render_app/views/template/some_template.haml +2 -0
- data/test/helper.rb +73 -0
- data/test/test_asset_tag_helpers.rb +127 -0
- data/test/test_form_builder.rb +611 -0
- data/test/test_form_helpers.rb +406 -0
- data/test/test_format_helpers.rb +96 -0
- data/test/test_output_helpers.rb +63 -0
- data/test/test_render_helpers.rb +78 -0
- data/test/test_tag_helpers.rb +73 -0
- metadata +174 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
module Padrino
|
2
|
+
module Helpers
|
3
|
+
module FormatHelpers
|
4
|
+
|
5
|
+
# Returns escaped text to protect against malicious content
|
6
|
+
def escape_html(text)
|
7
|
+
Rack::Utils.escape_html(text)
|
8
|
+
end
|
9
|
+
alias h escape_html
|
10
|
+
alias sanitize_html escape_html
|
11
|
+
|
12
|
+
# Returns escaped text to protect against malicious content
|
13
|
+
# Returns blank if the text is empty
|
14
|
+
def h!(text, blank_text = ' ')
|
15
|
+
return blank_text if text.nil? || text.empty?
|
16
|
+
h text
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
# Smart time helper which returns relative text representing times for recent dates
|
21
|
+
# and absolutes for dates that are far removed from the current date
|
22
|
+
# time_in_words(10.days.ago) => '10 days ago'
|
23
|
+
def time_in_words(date)
|
24
|
+
date = date.to_date
|
25
|
+
date = Date.parse(date, true) unless /Date.*/ =~ date.class.to_s
|
26
|
+
days = (date - Date.today).to_i
|
27
|
+
|
28
|
+
return 'today' if days >= 0 and days < 1
|
29
|
+
return 'tomorrow' if days >= 1 and days < 2
|
30
|
+
return 'yesterday' if days >= -1 and days < 0
|
31
|
+
|
32
|
+
return "in #{days} days" if days.abs < 60 and days > 0
|
33
|
+
return "#{days.abs} days ago" if days.abs < 60 and days < 0
|
34
|
+
|
35
|
+
return date.strftime('%A, %B %e') if days.abs < 182
|
36
|
+
return date.strftime('%A, %B %e, %Y')
|
37
|
+
end
|
38
|
+
alias time_ago time_in_words
|
39
|
+
|
40
|
+
# Returns relative time in words referencing the given date
|
41
|
+
# relative_time_ago(Time.now) => 'about a minute ago'
|
42
|
+
def relative_time_ago(from_time)
|
43
|
+
distance_in_minutes = (((Time.now - from_time.to_time).abs)/60).round
|
44
|
+
case distance_in_minutes
|
45
|
+
when 0..1 then 'about a minute'
|
46
|
+
when 2..44 then "#{distance_in_minutes} minutes"
|
47
|
+
when 45..89 then 'about 1 hour'
|
48
|
+
when 90..1439 then "about #{(distance_in_minutes.to_f / 60.0).round} hours"
|
49
|
+
when 1440..2879 then '1 day'
|
50
|
+
when 2880..43199 then "#{(distance_in_minutes / 1440).round} days"
|
51
|
+
when 43200..86399 then 'about 1 month'
|
52
|
+
when 86400..525599 then "#{(distance_in_minutes / 43200).round} months"
|
53
|
+
when 525600..1051199 then 'about 1 year'
|
54
|
+
else "over #{(distance_in_minutes / 525600).round} years"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Used in xxxx.js.erb files to escape html so that it can be passed to javascript from sinatra
|
59
|
+
# escape_javascript("<h1>Hey</h1>")
|
60
|
+
def escape_javascript(html_content)
|
61
|
+
return '' unless html_content
|
62
|
+
javascript_mapping = { '\\' => '\\\\', '</' => '<\/', "\r\n" => '\n', "\n" => '\n' }
|
63
|
+
javascript_mapping.merge("\r" => '\n', '"' => '\\"', "'" => "\\'")
|
64
|
+
escaped_string = html_content.gsub(/(\\|<\/|\r\n|[\n\r"'])/) { javascript_mapping[$1] }
|
65
|
+
"\"#{escaped_string}\""
|
66
|
+
end
|
67
|
+
|
68
|
+
alias js_escape escape_javascript
|
69
|
+
alias js_escape_html escape_javascript
|
70
|
+
alias escape_for_javascript escape_javascript
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Padrino
|
2
|
+
module Helpers
|
3
|
+
module OutputHelpers
|
4
|
+
# Captures the html from a block of template code for erb or haml
|
5
|
+
# capture_html(&block) => "...html..."
|
6
|
+
def capture_html(*args, &block)
|
7
|
+
if self.respond_to?(:is_haml?) && is_haml?
|
8
|
+
block_is_haml?(block) ? capture_haml(*args, &block) : block.call
|
9
|
+
elsif has_erb_buffer?
|
10
|
+
result_text = capture_erb(*args, &block)
|
11
|
+
result_text.present? ? result_text : (block_given? && block.call(*args))
|
12
|
+
else # theres no template to capture, invoke the block directly
|
13
|
+
block.call(*args)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Outputs the given text to the templates buffer directly
|
18
|
+
# concat_content("This will be output to the template buffer in erb or haml")
|
19
|
+
def concat_content(text="")
|
20
|
+
if self.respond_to?(:is_haml?) && is_haml?
|
21
|
+
haml_concat(text)
|
22
|
+
elsif has_erb_buffer?
|
23
|
+
erb_concat(text)
|
24
|
+
else # theres no template to concat, return the text directly
|
25
|
+
text
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns true if the block is from an ERB or HAML template; false otherwise.
|
30
|
+
# Used to determine if html should be returned or concatted to view
|
31
|
+
# block_is_template?(block)
|
32
|
+
def block_is_template?(block)
|
33
|
+
block && (block_is_erb?(block) || (self.respond_to?(:block_is_haml?) && block_is_haml?(block)))
|
34
|
+
end
|
35
|
+
|
36
|
+
# Capture a block of content to be rendered at a later time.
|
37
|
+
# Your blocks can also receive values, which are passed to them by <tt>yield_content</tt>
|
38
|
+
# content_for(:name) { ...content... }
|
39
|
+
# content_for(:name) { |name| ...content... }
|
40
|
+
def content_for(key, &block)
|
41
|
+
content_blocks[key.to_sym] << block
|
42
|
+
end
|
43
|
+
|
44
|
+
# Render the captured content blocks for a given key.
|
45
|
+
# You can also pass values to the content blocks by passing them
|
46
|
+
# as arguments after the key.
|
47
|
+
# yield_content :include
|
48
|
+
# yield_content :head, "param1", "param2"
|
49
|
+
def yield_content(key, *args)
|
50
|
+
content_blocks[key.to_sym].map { |content|
|
51
|
+
capture_html(*args, &content)
|
52
|
+
}.join
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
# Retrieves content_blocks stored by content_for or within yield_content
|
58
|
+
# content_blocks[:name] => ['...', '...']
|
59
|
+
def content_blocks
|
60
|
+
@content_blocks ||= Hash.new {|h,k| h[k] = [] }
|
61
|
+
end
|
62
|
+
|
63
|
+
# Used to capture the html from a block of erb code
|
64
|
+
# capture_erb(&block) => '...html...'
|
65
|
+
def capture_erb(*args, &block)
|
66
|
+
erb_with_output_buffer { block_given? && block.call(*args) }
|
67
|
+
end
|
68
|
+
|
69
|
+
# Concats directly to an erb template
|
70
|
+
# erb_concat("Direct to buffer")
|
71
|
+
def erb_concat(text)
|
72
|
+
@_out_buf << text if has_erb_buffer?
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns true if an erb buffer is detected
|
76
|
+
# has_erb_buffer? => true
|
77
|
+
def has_erb_buffer?
|
78
|
+
!@_out_buf.nil?
|
79
|
+
end
|
80
|
+
|
81
|
+
# Used to determine if a block is called from ERB.
|
82
|
+
# NOTE: This doesn't actually work yet because the variable __in_erb_template
|
83
|
+
# hasn't been defined in ERB. We need to find a way to fix this.
|
84
|
+
def block_is_erb?(block)
|
85
|
+
has_erb_buffer? || block && eval('defined? __in_erb_template', block)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Used to direct the buffer for the erb capture
|
89
|
+
def erb_with_output_buffer(buf = '') #:nodoc:
|
90
|
+
@_out_buf, old_buffer = buf, @_out_buf
|
91
|
+
yield
|
92
|
+
@_out_buf
|
93
|
+
ensure
|
94
|
+
@_out_buf = old_buffer
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Padrino
|
2
|
+
module Helpers
|
3
|
+
module RenderHelpers
|
4
|
+
# Renders a erb template based on the relative path
|
5
|
+
# erb_template 'users/new'
|
6
|
+
def erb_template(template_path, options={})
|
7
|
+
render_template template_path, options.merge(:template_engine => :erb)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Renders a haml template based on the relative path
|
11
|
+
# haml_template 'users/new'
|
12
|
+
def haml_template(template_path, options={})
|
13
|
+
render_template template_path, options.merge(:template_engine => :haml)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Renders a template from a file path automatically determining rendering engine
|
17
|
+
# render_template 'users/new'
|
18
|
+
# options = { :template_engine => 'haml' }
|
19
|
+
def render_template(template_path, options={})
|
20
|
+
template_engine = options.delete(:template_engine) || resolve_template_engine(template_path)
|
21
|
+
render template_engine.to_sym, template_path.to_sym, options
|
22
|
+
end
|
23
|
+
|
24
|
+
# Partials implementation which includes collections support
|
25
|
+
# partial 'photo/_item', :object => @photo
|
26
|
+
# partial 'photo/_item', :collection => @photos
|
27
|
+
def partial(template, options={})
|
28
|
+
options.reverse_merge!(:locals => {}, :layout => false)
|
29
|
+
path = template.to_s.split(File::SEPARATOR)
|
30
|
+
object_name = path[-1].to_sym
|
31
|
+
path[-1] = "_#{path[-1]}"
|
32
|
+
template_path = File.join(path)
|
33
|
+
raise 'Partial collection specified but is nil' if options.has_key?(:collection) && options[:collection].nil?
|
34
|
+
if collection = options.delete(:collection)
|
35
|
+
options.delete(:object)
|
36
|
+
counter = 0
|
37
|
+
collection.collect do |member|
|
38
|
+
counter += 1
|
39
|
+
options[:locals].merge!(object_name => member, "#{object_name}_counter".to_sym => counter)
|
40
|
+
render_template(template_path, options.merge(:layout => false))
|
41
|
+
end.join("\n")
|
42
|
+
else
|
43
|
+
if member = options.delete(:object)
|
44
|
+
options[:locals].merge!(object_name => member)
|
45
|
+
end
|
46
|
+
render_template(template_path, options.merge(:layout => false))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
alias render_partial partial
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# Returns the template engine (i.e haml) to use for a given template_path
|
54
|
+
# resolve_template_engine('users/new') => :haml
|
55
|
+
def resolve_template_engine(template_path)
|
56
|
+
resolved_template_path = File.join(self.options.views, template_path.to_s + ".*")
|
57
|
+
template_file = Dir[resolved_template_path].first
|
58
|
+
raise "Template path '#{template_path}' could not be located in views!" unless template_file
|
59
|
+
template_engine = File.extname(template_file)[1..-1].to_sym
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Padrino
|
2
|
+
module Helpers
|
3
|
+
module TagHelpers
|
4
|
+
# Creates an html input field with given type and options
|
5
|
+
# input_tag :text, :class => "test"
|
6
|
+
def input_tag(type, options = {})
|
7
|
+
options.reverse_merge!(:type => type)
|
8
|
+
tag(:input, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Creates an html tag with given name, content and options
|
12
|
+
# content_tag(:p, "hello", :class => 'light')
|
13
|
+
# content_tag(:p, :class => 'dark') do ... end
|
14
|
+
# parameters: content_tag(name, content=nil, options={}, &block)
|
15
|
+
def content_tag(*args, &block)
|
16
|
+
name = args.first
|
17
|
+
options = args.extract_options!
|
18
|
+
tag_html = block_given? ? capture_html(&block) : args[1]
|
19
|
+
tag_result = tag(name, options.merge(:content => tag_html))
|
20
|
+
block_is_template?(block) ? concat_content(tag_result) : tag_result
|
21
|
+
end
|
22
|
+
|
23
|
+
# Creates an html tag with the given name and options
|
24
|
+
# tag(:br, :style => 'clear:both')
|
25
|
+
# tag(:p, :content => "hello", :class => 'large')
|
26
|
+
def tag(name, options={})
|
27
|
+
content = options.delete(:content)
|
28
|
+
identity_tag_attributes.each { |attr| options[attr] = attr.to_s if options[attr] }
|
29
|
+
html_attrs = options.collect { |a, v| v.blank? ? nil : "#{a}=\"#{v}\"" }.compact.join(" ")
|
30
|
+
base_tag = (html_attrs.present? ? "<#{name} #{html_attrs}" : "<#{name}")
|
31
|
+
base_tag << (content ? ">#{content}</#{name}>" : " />")
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
# Returns a list of attributes which can only contain an identity value (i.e selected)
|
37
|
+
def identity_tag_attributes
|
38
|
+
[:checked, :disabled, :selected, :multiple]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'haml'
|
3
|
+
|
4
|
+
class MarkupDemo < Sinatra::Base
|
5
|
+
register Padrino::Helpers
|
6
|
+
|
7
|
+
configure do
|
8
|
+
set :root, File.dirname(__FILE__)
|
9
|
+
end
|
10
|
+
|
11
|
+
get '/:engine/:file' do
|
12
|
+
show(params[:engine], params[:file].to_sym)
|
13
|
+
end
|
14
|
+
|
15
|
+
helpers do
|
16
|
+
# show :erb, :index
|
17
|
+
# show :haml, :index
|
18
|
+
def show(kind, template)
|
19
|
+
eval("#{kind.to_s} #{template.to_sym.inspect}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def captured_content(&block)
|
23
|
+
content_html = capture_html(&block)
|
24
|
+
"<p>#{content_html}</p>"
|
25
|
+
end
|
26
|
+
|
27
|
+
def concat_in_p(content_html)
|
28
|
+
concat_content "<p>#{content_html}</p>"
|
29
|
+
end
|
30
|
+
|
31
|
+
def ruby_not_template_block
|
32
|
+
determine_block_is_template('ruby') do
|
33
|
+
content_tag(:span, "This not a template block")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def determine_block_is_template(name, &block)
|
38
|
+
concat_content "<p class='is_template'>The #{name} block passed in is a template</p>" if block_is_template?(block)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class MarkupUser
|
44
|
+
def errors; Errors.new; end
|
45
|
+
def session_id; 45; end
|
46
|
+
def gender; 'male'; end
|
47
|
+
def remember_me; '1'; end
|
48
|
+
def permission; Permission.new; end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Permission
|
52
|
+
def can_edit; true; end
|
53
|
+
def can_delete; false; end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Errors < Array
|
57
|
+
def initialize; self << [:fake, :second, :third]; end
|
58
|
+
def full_messages
|
59
|
+
["This is a fake error", "This is a second fake error", "This is a third fake error"]
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<% @content = captured_content do %>
|
2
|
+
<span>Captured Line 1</span>
|
3
|
+
<span>Captured Line 2</span>
|
4
|
+
<% end %>
|
5
|
+
<%= @content %>
|
6
|
+
|
7
|
+
<% concat_in_p('Concat Line 3') %>
|
8
|
+
|
9
|
+
<% determine_block_is_template('erb') do %>
|
10
|
+
<span>This is erb</span>
|
11
|
+
<span>This is erb</span>
|
12
|
+
<% end %>
|
13
|
+
|
14
|
+
<% ruby_not_template_block %>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<% content_for :demo do %>
|
2
|
+
<h1>This is content yielded from a content_for</h1>
|
3
|
+
<% end %>
|
4
|
+
|
5
|
+
<div class='demo'><%= yield_content :demo %></p>
|
6
|
+
|
7
|
+
<% content_for :demo2 do |fname, lname| %>
|
8
|
+
<h1>This is content yielded with name <%= fname + " " + lname %></h1>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<div class='demo2'><%= yield_content :demo2, "Johnny", "Smith" %></div>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<%= content_tag :p, "Test 1", :class => 'test', :id => "test1" %>
|
2
|
+
|
3
|
+
<%= content_tag :p, "Test 2" %>
|
4
|
+
|
5
|
+
<% content_tag(:p, :class => 'test', :id => 'test3') do %>
|
6
|
+
<span>Test 3</span>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<% content_tag(:p) do %>
|
10
|
+
<span>Test 4</span>
|
11
|
+
<% end %>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<% @user = MarkupUser.new %>
|
2
|
+
<% form_for @user , '/demo1', :id => 'demo-fields-for' do |f| %>
|
3
|
+
<%= f.text_field :gender %>
|
4
|
+
<% fields_for @user.permission do |permission| %>
|
5
|
+
<%= permission.check_box :can_edit %>
|
6
|
+
<%= permission.check_box :can_delete %>
|
7
|
+
<% end %>
|
8
|
+
<% end %>
|