bike 0.2.1

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 (151) hide show
  1. data/LICENSE +19 -0
  2. data/README.rdoc +124 -0
  3. data/bin/bike +35 -0
  4. data/lib/_error.rb +14 -0
  5. data/lib/_field.rb +260 -0
  6. data/lib/_i18n.rb +144 -0
  7. data/lib/_parser.rb +256 -0
  8. data/lib/_path.rb +86 -0
  9. data/lib/_storage/_storage.rb +215 -0
  10. data/lib/_storage/file.rb +201 -0
  11. data/lib/_storage/sequel.rb +174 -0
  12. data/lib/_storage/temp.rb +73 -0
  13. data/lib/_widget/action_create.rb +23 -0
  14. data/lib/_widget/action_login.rb +22 -0
  15. data/lib/_widget/action_signup.rb +16 -0
  16. data/lib/_widget/action_update.rb +16 -0
  17. data/lib/_widget/crumb.rb +24 -0
  18. data/lib/_widget/done.rb +16 -0
  19. data/lib/_widget/login.rb +25 -0
  20. data/lib/_widget/me.rb +31 -0
  21. data/lib/_widget/message.rb +51 -0
  22. data/lib/_widget/navi.rb +88 -0
  23. data/lib/_widget/submit.rb +49 -0
  24. data/lib/_widget/view_ym.rb +77 -0
  25. data/lib/_workflow/_workflow.rb +89 -0
  26. data/lib/_workflow/attachment.rb +50 -0
  27. data/lib/_workflow/blog.rb +28 -0
  28. data/lib/_workflow/contact.rb +23 -0
  29. data/lib/_workflow/forum.rb +26 -0
  30. data/lib/_workflow/register.rb +39 -0
  31. data/lib/bike.rb +396 -0
  32. data/lib/meta/_meta.rb +20 -0
  33. data/lib/meta/group.rb +19 -0
  34. data/lib/meta/id.rb +59 -0
  35. data/lib/meta/owner.rb +21 -0
  36. data/lib/meta/timestamp.rb +118 -0
  37. data/lib/scalar/checkbox.rb +68 -0
  38. data/lib/scalar/file.rb +144 -0
  39. data/lib/scalar/img.rb +112 -0
  40. data/lib/scalar/password.rb +58 -0
  41. data/lib/scalar/radio.rb +47 -0
  42. data/lib/scalar/select.rb +47 -0
  43. data/lib/scalar/text.rb +38 -0
  44. data/lib/scalar/textarea.rb +35 -0
  45. data/lib/scalar/textarea_pre.rb +14 -0
  46. data/lib/scalar/textarea_wiki.rb +173 -0
  47. data/lib/set/_set.rb +196 -0
  48. data/lib/set/dynamic.rb +177 -0
  49. data/lib/set/static.rb +102 -0
  50. data/lib/set/static_folder.rb +96 -0
  51. data/locale/en/index.po +242 -0
  52. data/locale/index.pot +243 -0
  53. data/locale/ja/index.po +242 -0
  54. data/locale/lazy_parser.rb +54 -0
  55. data/skel/config.ru +27 -0
  56. data/skel/skin/_users/00000000_frank-avatar.jpg +0 -0
  57. data/skel/skin/_users/00000000_frank-avatar_small.jpg +0 -0
  58. data/skel/skin/_users/00000000_frank.yaml +12 -0
  59. data/skel/skin/_users/00000000_root-avatar.jpg +0 -0
  60. data/skel/skin/_users/00000000_root-avatar_small.jpg +0 -0
  61. data/skel/skin/_users/00000000_root.yaml +11 -0
  62. data/skel/skin/_users/css/users.css +21 -0
  63. data/skel/skin/_users/css/users.less +25 -0
  64. data/skel/skin/_users/done.html +42 -0
  65. data/skel/skin/_users/index.html +46 -0
  66. data/skel/skin/_users/index.yaml +3 -0
  67. data/skel/skin/_users/summary.html +40 -0
  68. data/skel/skin/css/base.css +93 -0
  69. data/skel/skin/css/base.less +139 -0
  70. data/skel/skin/css/coax.css +199 -0
  71. data/skel/skin/css/coax.less +244 -0
  72. data/skel/skin/examples/blog/20091214_0001.yaml +8 -0
  73. data/skel/skin/examples/blog/20100630_0001.yaml +8 -0
  74. data/skel/skin/examples/blog/20100630_0002.yaml +14 -0
  75. data/skel/skin/examples/blog/20100701_0001.yaml +8 -0
  76. data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f.jpg +0 -0
  77. data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f_small.jpg +0 -0
  78. data/skel/skin/examples/blog/20100701_0002.yaml +19 -0
  79. data/skel/skin/examples/blog/frank/20100701_0001.yaml +10 -0
  80. data/skel/skin/examples/blog/frank/index.yaml +4 -0
  81. data/skel/skin/examples/blog/index.html +51 -0
  82. data/skel/skin/examples/blog/rss.xml +18 -0
  83. data/skel/skin/examples/contact/20100701_0001-file.txt +1 -0
  84. data/skel/skin/examples/contact/20100701_0001.yaml +15 -0
  85. data/skel/skin/examples/contact/20100701_0002.yaml +8 -0
  86. data/skel/skin/examples/contact/20100701_0003.yaml +9 -0
  87. data/skel/skin/examples/contact/index.html +47 -0
  88. data/skel/skin/examples/contact/js/contact.js +13 -0
  89. data/skel/skin/examples/contact/summary.html +54 -0
  90. data/skel/skin/examples/forum/20100701_0001.yaml +41 -0
  91. data/skel/skin/examples/forum/20100701_0002.yaml +25 -0
  92. data/skel/skin/examples/forum/index.html +68 -0
  93. data/skel/skin/examples/forum/summary.html +47 -0
  94. data/skel/skin/examples/index.html +73 -0
  95. data/skel/skin/index.html +39 -0
  96. data/skel/skin/js/base.js +50 -0
  97. data/t/locale/de/index.po +19 -0
  98. data/t/locale/en-GB/index.po +25 -0
  99. data/t/locale/ja/index.po +30 -0
  100. data/t/skin/_users/00000000_test.yaml +3 -0
  101. data/t/skin/_users/index.html +13 -0
  102. data/t/skin/foo/20091120_0001.yaml +7 -0
  103. data/t/skin/foo/bar/20091120_0001.yaml +5 -0
  104. data/t/skin/foo/bar/index.yaml +5 -0
  105. data/t/skin/foo/baz/css/baz.css +1 -0
  106. data/t/skin/foo/css/foo.css +1 -0
  107. data/t/skin/foo/index.html +14 -0
  108. data/t/skin/foo/index.yaml +7 -0
  109. data/t/skin/foo/not_css/foo.css +1 -0
  110. data/t/skin/foo/qux/index.html +8 -0
  111. data/t/skin/foo/qux/moo/index.html +6 -0
  112. data/t/skin/foo/sub-20100306_0001.yaml +3 -0
  113. data/t/skin/index.yaml +3 -0
  114. data/t/skin/t_attachment/index.html +13 -0
  115. data/t/skin/t_contact/done.html +6 -0
  116. data/t/skin/t_contact/index.html +9 -0
  117. data/t/skin/t_file/index.html +16 -0
  118. data/t/skin/t_img/index.html +14 -0
  119. data/t/skin/t_img/test.jpg +0 -0
  120. data/t/skin/t_select/index.html +9 -0
  121. data/t/skin/t_store/index.html +9 -0
  122. data/t/skin/t_summary/20100326_0001.yaml +3 -0
  123. data/t/skin/t_summary/create.html +9 -0
  124. data/t/skin/t_summary/index.html +9 -0
  125. data/t/skin/t_summary/summary.html +9 -0
  126. data/t/t.rb +27 -0
  127. data/t/test_bike.rb +768 -0
  128. data/t/test_call.rb +1281 -0
  129. data/t/test_checkbox.rb +273 -0
  130. data/t/test_field.rb +330 -0
  131. data/t/test_file.rb +900 -0
  132. data/t/test_i18n.rb +325 -0
  133. data/t/test_id.rb +215 -0
  134. data/t/test_img.rb +328 -0
  135. data/t/test_meta.rb +57 -0
  136. data/t/test_parser.rb +1516 -0
  137. data/t/test_password.rb +188 -0
  138. data/t/test_radio.rb +226 -0
  139. data/t/test_role.rb +249 -0
  140. data/t/test_select.rb +182 -0
  141. data/t/test_set_complex.rb +527 -0
  142. data/t/test_set_dynamic.rb +1504 -0
  143. data/t/test_set_folder.rb +515 -0
  144. data/t/test_set_permit.rb +246 -0
  145. data/t/test_set_static.rb +468 -0
  146. data/t/test_storage.rb +915 -0
  147. data/t/test_text.rb +125 -0
  148. data/t/test_textarea.rb +138 -0
  149. data/t/test_timestamp.rb +473 -0
  150. data/t/test_workflow.rb +367 -0
  151. metadata +347 -0
@@ -0,0 +1,112 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ begin
7
+ require 'quick_magick'
8
+ rescue LoadError
9
+ end
10
+
11
+ class Bike::Img < Bike::File
12
+
13
+ DEFAULT_META = {
14
+ :width => 120,
15
+ :height => 120,
16
+ :options => ['png', 'jpg', 'jpeg', 'gif', 'tiff', 'bmp'],
17
+ }
18
+
19
+ def self.quick_magick?
20
+ Object.const_defined? :QuickMagick
21
+ end
22
+
23
+ def initialize(meta = {})
24
+ meta[:crop] = true if meta[:tokens] && meta[:tokens].include?('crop')
25
+ super
26
+ end
27
+
28
+ def thumbnail
29
+ raise Bike::Error::Forbidden unless permit? :read
30
+
31
+ if ps = my[:persistent_sd]
32
+ @thumbnail ||= ps.storage.val "#{my[:persistent_name]}_small"
33
+ end
34
+ @thumbnail || body
35
+ end
36
+
37
+ def errors
38
+ if @error_thumbnail
39
+ [_('wrong file type: should be %{types}') % {:types => my[:options].join('/')}]
40
+ else
41
+ super
42
+ end
43
+ end
44
+
45
+ def commit(type = :temp)
46
+ super
47
+ if type == :temp && @action == :delete
48
+ @thumbnail = nil
49
+ elsif type == :persistent && ps = my[:persistent_sd]
50
+ case @action
51
+ when :create, :update, nil
52
+ ps.storage.store(
53
+ "#{my[:persistent_name]}_small",
54
+ @thumbnail,
55
+ val['basename'][/\.([\w\.]+)$/, 1] || 'bin'
56
+ ) if @thumbnail && valid?
57
+ end
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def _g_default(arg = {})
64
+ path = _path arg[:action]
65
+ basename = Bike::Field.h val['basename']
66
+ s_basename = basename.sub(/\..+$/, '_small\\&')
67
+ if val.empty?
68
+ <<_html.chomp
69
+ <span class="dummy_img" style="width: #{my[:width]}px; height: #{my[:height]}px;"></span>
70
+ _html
71
+ elsif errors.include?(_('wrong file type: should be %{types}') % {:types => my[:options].join('/')})
72
+ super
73
+ elsif arg[:sub_action] == :without_link
74
+ <<_html.chomp
75
+ <img src="#{path}/#{s_basename}" alt="#{basename}" />
76
+ _html
77
+ else
78
+ <<_html.chomp
79
+ <a href="#{path}/#{basename}"><img src="#{path}/#{s_basename}" alt="#{basename}" /></a>
80
+ _html
81
+ end
82
+ end
83
+
84
+ def _g_thumbnail(arg = {})
85
+ _g_default arg.merge(:sub_action => :without_link)
86
+ end
87
+
88
+ def _thumbnail(tempfile)
89
+ @error_thumbnail = nil
90
+ begin
91
+ tempfile.rewind
92
+ img = QuickMagick::Image.read(tempfile.path).first
93
+ if my[:crop]
94
+ img.gravity = 'center'
95
+ img.resize "#{my[:width]}x#{my[:height]}^"
96
+ img.extent "#{my[:width]}x#{my[:height]}"
97
+ else
98
+ img.resize "#{my[:width]}x#{my[:height]}"
99
+ end
100
+ img.to_blob
101
+ rescue QuickMagick::QuickMagickError
102
+ @error_thumbnail = $!.inspect
103
+ nil
104
+ end if self.class.quick_magick?
105
+ end
106
+
107
+ def val_cast(v)
108
+ @thumbnail = _thumbnail(v[:tempfile]) if v.is_a?(::Hash) && v[:tempfile]
109
+ super
110
+ end
111
+
112
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ class Bike::Password < Bike::Field
7
+
8
+ def initialize(meta = {})
9
+ meta[:size] = $&.to_i if meta[:tokens] && meta[:tokens].find {|t| t =~ /^\d+$/ }
10
+ super
11
+ end
12
+
13
+ def errors
14
+ if @size.nil?
15
+ []
16
+ elsif (my[:max].to_i > 0) && (@size > my[:max])
17
+ [_('too long: %{max} characters maximum') % {:max => my[:max]}]
18
+ elsif (my[:min].to_i == 1) && (@size == 0)
19
+ [_('mandatory')]
20
+ elsif (my[:min].to_i > 0) && (@size < my[:min])
21
+ [_('too short: %{min} characters minimum') % {:min => my[:min]}]
22
+ else
23
+ []
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def _g_default(arg)
30
+ '*' * (@size || 5)
31
+ end
32
+
33
+ def _g_update(arg)
34
+ <<_html.chomp
35
+ <span class="#{_g_class arg}"><input type="password" name="#{my[:short_name]}" value="" size="#{my[:size]}" />#{_g_errors arg}</span>
36
+ _html
37
+ end
38
+ alias :_g_create :_g_update
39
+
40
+ def _post(action, v)
41
+ case action
42
+ when :load
43
+ @size = nil
44
+ @val = v
45
+ when :create, :update
46
+ if v.is_a?(::String) && !v.empty?
47
+ salt = Array('a'..'z')[rand(26)] + Array('a'..'z')[rand(26)]
48
+ @size = v.size
49
+ @val = v.crypt salt
50
+ elsif @val.nil?
51
+ @size = 0
52
+ else
53
+ # no action: keep current @val
54
+ end
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ class Bike::Radio < Bike::Field
7
+
8
+ def initialize(meta = {})
9
+ meta[:mandatory] = meta[:tokens] && meta[:tokens].include?('mandatory')
10
+ meta[:options] ||= meta[:max] && (meta[:min].to_i..meta[:max]).collect {|i| i.to_s }
11
+ super
12
+ end
13
+
14
+ def errors
15
+ if val.empty?
16
+ my[:mandatory] ? [_('mandatory')] : []
17
+ else
18
+ my[:options].include?(val) ? [] : [_('no such option')]
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def _g_update(arg)
25
+ options = my[:options].collect {|opt|
26
+ checked = (opt == val) ? ' checked' : ''
27
+ h_opt = Bike::Field.h opt
28
+ <<_html
29
+ <span class="item">
30
+ <input type="radio" id="radio_#{my[:short_name]}-#{h_opt}" name="#{my[:short_name]}" value="#{h_opt}"#{checked} />
31
+ <label for="radio_#{my[:short_name]}-#{h_opt}">#{h_opt}</label>
32
+ </span>
33
+ _html
34
+ }.join
35
+ <<_html
36
+ <span class="#{_g_class arg}">
37
+ <input type="hidden" name="#{my[:short_name]}" value="" />
38
+ #{options}#{_g_errors arg}</span>
39
+ _html
40
+ end
41
+ alias :_g_create :_g_update
42
+
43
+ def val_cast(v)
44
+ v.to_s
45
+ end
46
+
47
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ class Bike::Select < Bike::Field
7
+
8
+ def initialize(meta = {})
9
+ meta[:mandatory] = meta[:tokens] && meta[:tokens].include?('mandatory')
10
+ meta[:options] ||= meta[:max] && (meta[:min].to_i..meta[:max]).collect {|i| i.to_s }
11
+ super
12
+ end
13
+
14
+ def errors
15
+ if my[:mandatory] && val.empty?
16
+ [_('mandatory')]
17
+ elsif my[:options].include?(val) || val.empty?
18
+ []
19
+ else
20
+ [_('no such option')]
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def _g_update(arg)
27
+ options = my[:options].collect {|opt|
28
+ selected = (opt == val) ? ' selected' : ''
29
+ " <option#{selected}>#{Bike::Field.h opt}</option>\n"
30
+ }.join
31
+ unless my[:mandatory] && my[:options].include?(val)
32
+ options = " <option value=\"\">#{_ 'please select'}</option>\n#{options}"
33
+ end
34
+ <<_html
35
+ <span class="#{_g_class arg}">
36
+ <select name="#{my[:short_name]}">
37
+ #{options} </select>
38
+ #{_g_errors arg}</span>
39
+ _html
40
+ end
41
+ alias :_g_create :_g_update
42
+
43
+ def val_cast(v)
44
+ v.to_s
45
+ end
46
+
47
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ class Bike::Text < Bike::Field
7
+
8
+ def initialize(meta = {})
9
+ meta[:size] = $&.to_i if meta[:tokens] && meta[:tokens].first =~ /^\d+$/
10
+ super
11
+ end
12
+
13
+ def errors
14
+ if (my[:max].to_i > 0) && (val.size > my[:max])
15
+ [_('too long: %{max} characters maximum') % {:max => my[:max]}]
16
+ elsif (my[:min].to_i == 1) && val.empty?
17
+ [_('mandatory')]
18
+ elsif (my[:min].to_i > 0) && (val.size < my[:min])
19
+ [_('too short: %{min} characters minimum') % {:min => my[:min]}]
20
+ else
21
+ []
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def _g_update(arg)
28
+ <<_html.chomp
29
+ <span class="#{_g_class arg}"><input type="text" name="#{my[:short_name]}" value="#{Bike::Field.h val}" size="#{my[:size]}" />#{_g_errors arg}</span>
30
+ _html
31
+ end
32
+ alias :_g_create :_g_update
33
+
34
+ def val_cast(v)
35
+ v.to_s
36
+ end
37
+
38
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ class Bike::Textarea < Bike::Field
7
+
8
+ def errors
9
+ if (my[:max].to_i > 0) && (val.size > my[:max])
10
+ [_('too long: %{max} characters maximum') % {:max => my[:max]}]
11
+ elsif (my[:min].to_i == 1) && val.empty?
12
+ [_('mandatory')]
13
+ elsif (my[:min].to_i > 0) && (val.size < my[:min])
14
+ [_('too short: %{min} characters minimum') % {:min => my[:min]}]
15
+ else
16
+ []
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def _g_update(arg)
23
+ <<_html
24
+ <span class="#{_g_class arg}">
25
+ <textarea name="#{my[:short_name]}" cols="#{my[:width]}" rows="#{my[:height]}">#{Bike::Field.h val}</textarea>
26
+ #{_g_errors arg}</span>
27
+ _html
28
+ end
29
+ alias :_g_create :_g_update
30
+
31
+ def val_cast(v)
32
+ v.to_s.gsub(/\r\n?/, "\n")
33
+ end
34
+
35
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ require 'rack/utils'
7
+
8
+ class Bike::Textarea::Pre < Bike::Textarea
9
+
10
+ def _g_default(arg)
11
+ '<pre>' + Bike::Field.h(val) + '</pre>'
12
+ end
13
+
14
+ end
@@ -0,0 +1,173 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ require 'csv'
7
+
8
+ class Bike::Textarea::Wiki < Bike::Textarea
9
+
10
+ private
11
+
12
+ def _g_default(arg)
13
+ wiki @val.to_s
14
+ end
15
+
16
+ def wiki(str)
17
+ str.chomp!
18
+
19
+ html = ''
20
+ body = ''
21
+ type = :p
22
+
23
+ Bike::Field.h(str).each_line {|line|
24
+ if line =~ /^---$/
25
+ new_type = :hr
26
+ else
27
+ line =~ /^(.?)/
28
+ case $1
29
+ when '!'
30
+ new_type = :heading
31
+ when ','
32
+ new_type = :table
33
+ when '*'
34
+ new_type = :ul
35
+ when '+'
36
+ new_type = :ol
37
+ when ' ', "\t"
38
+ new_type = :pre
39
+ when ''
40
+ new_type = :blank
41
+ else
42
+ new_type = :p
43
+ end
44
+ end
45
+ if new_type != type
46
+ html += __send__("element_#{type}", body.to_s)
47
+ body = ''
48
+ end
49
+ body += line
50
+ type = new_type
51
+ }
52
+ html += __send__("element_#{type}", body) if body != ''
53
+
54
+ html
55
+ end
56
+
57
+ def element_hr(body)
58
+ body.gsub!(/\n*\Z/, '')
59
+ body.gsub!(/^---$/, "<hr />\n")
60
+
61
+ <<_html
62
+ #{body}
63
+ _html
64
+ end
65
+
66
+ def element_heading(body)
67
+ <<_html
68
+ <h3>#{body}</h3>
69
+ _html
70
+ end
71
+
72
+ def element_table(body)
73
+ table = CSV.parse(body.gsub!(/^,/, ''))
74
+
75
+ table.each_with_index {|row, y|
76
+ row.each_index {|x|
77
+ if (row[x] == :same_as_above)
78
+ row[x] = nil
79
+ next
80
+ end
81
+
82
+ span = 1
83
+ ((y + 1) .. (table.size - 1)).each {|y_below|
84
+ if (table[y_below][x] == row[x])
85
+ table[y_below][x] = :same_as_above
86
+ span += 1
87
+ else
88
+ break
89
+ end
90
+ }
91
+ rowspan = (span > 1) ? " rowspan=\"#{span}\"" : ''
92
+
93
+ row[x] = "<td valign=\"top\"#{rowspan}>#{inline(row[x])}</td>"
94
+ }
95
+ }
96
+
97
+ rows = table.collect {|row|
98
+ " <tr>" + row.join('') + '</tr>'
99
+ }.join("\n")
100
+
101
+ <<_html
102
+ <table class="wiki">
103
+ #{rows}
104
+ </table>
105
+ _html
106
+ end
107
+
108
+ def element_list(body, ul_ol)
109
+ items = []
110
+ lines = []
111
+ type = nil
112
+ (body + "\n\n").each_line {|line|
113
+ line.sub!(/^(\*|\+)/, '')
114
+
115
+ unless line =~ /^(\*|\+)/ || lines.empty?
116
+ item = inline(lines.shift.to_s.chomp)
117
+ unless lines.empty?
118
+ lines = lines.join
119
+ item += (lines =~ /\A\*/) ? element_ul(lines) : element_ol(lines)
120
+ end
121
+ items << "<li>#{item}</li>\n" if item != ''
122
+ lines = []
123
+ end
124
+
125
+ lines << line
126
+ }
127
+
128
+ <<_html
129
+ <#{ul_ol} class="wiki">
130
+ #{items.join}</#{ul_ol}>
131
+ _html
132
+ end
133
+
134
+ def element_ul(body)
135
+ element_list(body, 'ul')
136
+ end
137
+
138
+ def element_ol(body)
139
+ element_list(body, 'ol')
140
+ end
141
+
142
+ def element_pre(body)
143
+ body.gsub!(/\n*\Z/, '')
144
+
145
+ <<_html
146
+ <pre class="wiki">#{body}
147
+ </pre>
148
+ _html
149
+ end
150
+
151
+ def element_p(body)
152
+ body.gsub!(/\n*\Z/, '')
153
+ body.gsub!(/\n/, "<br />\n")
154
+ body.gsub!(/^/, ' ')
155
+
156
+ <<_html
157
+ <p class="wiki">
158
+ #{inline(body)}
159
+ </p>
160
+ _html
161
+ end
162
+
163
+ def element_blank(body)
164
+ body.gsub!(/\n/, "<br />\n")
165
+ end
166
+
167
+ def inline(body)
168
+ body.gsub!(/\*(.+?)\*/, '<strong class="wiki">\1</strong>')
169
+ body.gsub!(/\=\=(.+?)\=\=/, '<s class="wiki">\1</s>')
170
+ body
171
+ end
172
+
173
+ end