padrino-helpers 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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