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.
Files changed (48) hide show
  1. data/.document +5 -0
  2. data/.gitignore +21 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +7 -0
  5. data/Rakefile +58 -0
  6. data/VERSION +1 -0
  7. data/lib/padrino-helpers.rb +16 -0
  8. data/lib/padrino-helpers/asset_tag_helpers.rb +97 -0
  9. data/lib/padrino-helpers/form_builder/abstract_form_builder.rb +139 -0
  10. data/lib/padrino-helpers/form_builder/standard_form_builder.rb +37 -0
  11. data/lib/padrino-helpers/form_helpers.rb +194 -0
  12. data/lib/padrino-helpers/format_helpers.rb +74 -0
  13. data/lib/padrino-helpers/output_helpers.rb +98 -0
  14. data/lib/padrino-helpers/render_helpers.rb +63 -0
  15. data/lib/padrino-helpers/tag_helpers.rb +42 -0
  16. data/test/active_support_helpers.rb +7 -0
  17. data/test/fixtures/markup_app/app.rb +61 -0
  18. data/test/fixtures/markup_app/views/capture_concat.erb +14 -0
  19. data/test/fixtures/markup_app/views/capture_concat.haml +13 -0
  20. data/test/fixtures/markup_app/views/content_for.erb +11 -0
  21. data/test/fixtures/markup_app/views/content_for.haml +9 -0
  22. data/test/fixtures/markup_app/views/content_tag.erb +11 -0
  23. data/test/fixtures/markup_app/views/content_tag.haml +9 -0
  24. data/test/fixtures/markup_app/views/fields_for.erb +8 -0
  25. data/test/fixtures/markup_app/views/fields_for.haml +6 -0
  26. data/test/fixtures/markup_app/views/form_for.erb +56 -0
  27. data/test/fixtures/markup_app/views/form_for.haml +47 -0
  28. data/test/fixtures/markup_app/views/form_tag.erb +57 -0
  29. data/test/fixtures/markup_app/views/form_tag.haml +45 -0
  30. data/test/fixtures/markup_app/views/link_to.erb +5 -0
  31. data/test/fixtures/markup_app/views/link_to.haml +4 -0
  32. data/test/fixtures/markup_app/views/mail_to.erb +3 -0
  33. data/test/fixtures/markup_app/views/mail_to.haml +3 -0
  34. data/test/fixtures/render_app/app.rb +53 -0
  35. data/test/fixtures/render_app/views/erb/test.erb +1 -0
  36. data/test/fixtures/render_app/views/haml/test.haml +1 -0
  37. data/test/fixtures/render_app/views/template/_user.haml +7 -0
  38. data/test/fixtures/render_app/views/template/haml_template.haml +1 -0
  39. data/test/fixtures/render_app/views/template/some_template.haml +2 -0
  40. data/test/helper.rb +73 -0
  41. data/test/test_asset_tag_helpers.rb +127 -0
  42. data/test/test_form_builder.rb +611 -0
  43. data/test/test_form_helpers.rb +406 -0
  44. data/test/test_format_helpers.rb +96 -0
  45. data/test/test_output_helpers.rb +63 -0
  46. data/test/test_render_helpers.rb +78 -0
  47. data/test/test_tag_helpers.rb +73 -0
  48. 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,7 @@
1
+ unless Fixnum.method_defined?(:days)
2
+ require 'active_support/core_ext/object/misc'
3
+ require 'active_support/core_ext/date'
4
+ require 'active_support/core_ext/time'
5
+ require 'active_support/core_ext/numeric'
6
+ require 'active_support/duration'
7
+ 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,13 @@
1
+ - @content = captured_content do
2
+ %span Captured Line 1
3
+ %span Captured Line 2
4
+ = @content
5
+
6
+ - concat_in_p('Concat Line 3')
7
+
8
+ - determine_block_is_template('haml') do
9
+ %span This is haml
10
+ %span This is haml
11
+
12
+ - ruby_not_template_block
13
+
@@ -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,9 @@
1
+ - content_for :demo do
2
+ %h1 This is content yielded from a content_for
3
+
4
+ .demo= yield_content :demo
5
+
6
+ - content_for :demo2 do |fname, lname|
7
+ %h1 This is content yielded with name #{fname + " " + lname}
8
+
9
+ .demo2= yield_content :demo2, "Johnny", "Smith"
@@ -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,9 @@
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
7
+
8
+ - content_tag(:p) do
9
+ %span Test 4
@@ -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 %>
@@ -0,0 +1,6 @@
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