tenjin 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.txt +54 -0
  3. data/benchmark/bench.rb +502 -0
  4. data/benchmark/bench_context.rb +17 -0
  5. data/benchmark/bench_context.yaml +141 -0
  6. data/benchmark/templates/_footer.html +4 -0
  7. data/benchmark/templates/_header.html +52 -0
  8. data/benchmark/templates/bench_eruby.rhtml +29 -0
  9. data/benchmark/templates/bench_tenjin.rbhtml +29 -0
  10. data/bin/rbtenjin +449 -0
  11. data/doc-api/classes/Tenjin.html +141 -0
  12. data/doc-api/classes/Tenjin/ArrayBufferTemplate.html +270 -0
  13. data/doc-api/classes/Tenjin/BaseContext.html +312 -0
  14. data/doc-api/classes/Tenjin/Context.html +126 -0
  15. data/doc-api/classes/Tenjin/ContextHelper.html +433 -0
  16. data/doc-api/classes/Tenjin/Engine.html +616 -0
  17. data/doc-api/classes/Tenjin/ErubisTemplate.html +166 -0
  18. data/doc-api/classes/Tenjin/HtmlHelper.html +359 -0
  19. data/doc-api/classes/Tenjin/Preprocessor.html +242 -0
  20. data/doc-api/classes/Tenjin/Template.html +916 -0
  21. data/doc-api/created.rid +1 -0
  22. data/doc-api/files/README_txt.html +185 -0
  23. data/doc-api/files/lib/tenjin_rb.html +136 -0
  24. data/doc-api/fr_class_index.html +36 -0
  25. data/doc-api/fr_file_index.html +28 -0
  26. data/doc-api/fr_method_index.html +89 -0
  27. data/doc-api/index.html +24 -0
  28. data/doc-api/rdoc-style.css +208 -0
  29. data/doc/docstyle.css +188 -0
  30. data/doc/examples.html +312 -0
  31. data/doc/faq.html +909 -0
  32. data/doc/users-guide.html +1691 -0
  33. data/lib/tenjin.rb +959 -0
  34. data/setup.rb +1331 -0
  35. data/tenjin.gemspec +58 -0
  36. data/test/assert-text-equal.rb +45 -0
  37. data/test/data/examples/form/create.rbhtml +4 -0
  38. data/test/data/examples/form/form.rbhtml +14 -0
  39. data/test/data/examples/form/layout.rbhtml +8 -0
  40. data/test/data/examples/form/main.rb +9 -0
  41. data/test/data/examples/form/main.result +21 -0
  42. data/test/data/examples/form/update.rbhtml +4 -0
  43. data/test/data/examples/preprocessing/helper.rb +16 -0
  44. data/test/data/examples/preprocessing/main.rb +11 -0
  45. data/test/data/examples/preprocessing/main.result +17 -0
  46. data/test/data/examples/preprocessing/select.rbhtml +15 -0
  47. data/test/data/examples/preprocessing/select_P.result +18 -0
  48. data/test/data/examples/table/table.rb +9 -0
  49. data/test/data/examples/table/table.rbhtml +16 -0
  50. data/test/data/examples/table/table.result +20 -0
  51. data/test/data/examples/table/table_s.result +18 -0
  52. data/test/data/faq/ex1.rbhtml +5 -0
  53. data/test/data/faq/ex10-baselayout.rbhtml +27 -0
  54. data/test/data/faq/ex10-content.rbhtml +12 -0
  55. data/test/data/faq/ex10-customlayout.rbhtml +11 -0
  56. data/test/data/faq/ex10_inherit.result +25 -0
  57. data/test/data/faq/ex11-bench.rb +28 -0
  58. data/test/data/faq/ex11-content.rbhtml +9 -0
  59. data/test/data/faq/ex11-layout1.rbhtml +5 -0
  60. data/test/data/faq/ex11-layout2.rbhtml +6 -0
  61. data/test/data/faq/ex11.rb +5 -0
  62. data/test/data/faq/ex11.rbhtml +8 -0
  63. data/test/data/faq/ex11.source +11 -0
  64. data/test/data/faq/ex11_arraybuffer.result +15 -0
  65. data/test/data/faq/ex1_chksyntax.result +3 -0
  66. data/test/data/faq/ex2-content.rbhtml +3 -0
  67. data/test/data/faq/ex2-layout.rbhtml +11 -0
  68. data/test/data/faq/ex2_removenl.result +19 -0
  69. data/test/data/faq/ex3.rb +4 -0
  70. data/test/data/faq/ex3.rbhtml +1 -0
  71. data/test/data/faq/ex3_escapefunc1.result +2 -0
  72. data/test/data/faq/ex3_escapefunc2.result +2 -0
  73. data/test/data/faq/ex5.rbhtml +7 -0
  74. data/test/data/faq/ex5_template_args.source +9 -0
  75. data/test/data/faq/ex6-content.rhtml +6 -0
  76. data/test/data/faq/ex6-layout.rhtml +6 -0
  77. data/test/data/faq/ex6.rb +10 -0
  78. data/test/data/faq/ex6_eruby.result +12 -0
  79. data/test/data/faq/ex7-expr-pattern.rb +34 -0
  80. data/test/data/faq/ex7-expr-pattern.rbhtml +3 -0
  81. data/test/data/faq/ex7_expr_pattern.result +4 -0
  82. data/test/data/faq/ex8-m18n.rb +77 -0
  83. data/test/data/faq/ex8-m18n.rbhtml +4 -0
  84. data/test/data/faq/ex8_m18n.result +10 -0
  85. data/test/data/faq/ex9-baselayout.rbhtml +8 -0
  86. data/test/data/faq/ex9-content.rbhtml +6 -0
  87. data/test/data/faq/ex9-mylayout.rbhtml +5 -0
  88. data/test/data/faq/ex9_changelayout.result +11 -0
  89. data/test/data/users_guide/content6.rbhtml +3 -0
  90. data/test/data/users_guide/content7.rbhtml +5 -0
  91. data/test/data/users_guide/content8.rbhtml +2 -0
  92. data/test/data/users_guide/contextdata.rb +7 -0
  93. data/test/data/users_guide/datafile.rb +5 -0
  94. data/test/data/users_guide/datafile.yaml +10 -0
  95. data/test/data/users_guide/ex.rbhtml +6 -0
  96. data/test/data/users_guide/ex.result +7 -0
  97. data/test/data/users_guide/ex.script +5 -0
  98. data/test/data/users_guide/ex_script.result +7 -0
  99. data/test/data/users_guide/ex_source.result +8 -0
  100. data/test/data/users_guide/example1.rbhtml +12 -0
  101. data/test/data/users_guide/example1.result +17 -0
  102. data/test/data/users_guide/example10.rbhtml +4 -0
  103. data/test/data/users_guide/example10_template_args.result +6 -0
  104. data/test/data/users_guide/example11.rbhtml +5 -0
  105. data/test/data/users_guide/example11_template_args_result +2 -0
  106. data/test/data/users_guide/example12.rbhtml +12 -0
  107. data/test/data/users_guide/example12_preprocessed.result +10 -0
  108. data/test/data/users_guide/example12_preprocessed_source.result +10 -0
  109. data/test/data/users_guide/example13.rbhtml +6 -0
  110. data/test/data/users_guide/example13_preprocessed.result +2 -0
  111. data/test/data/users_guide/example13_preprocessed_source.result +2 -0
  112. data/test/data/users_guide/example14.rb +32 -0
  113. data/test/data/users_guide/example14.rbhtml +6 -0
  114. data/test/data/users_guide/example14_tmplclass.result +15 -0
  115. data/test/data/users_guide/example15.rb +10 -0
  116. data/test/data/users_guide/example15_escapefunc.result +14 -0
  117. data/test/data/users_guide/example16.rbhtml +4 -0
  118. data/test/data/users_guide/example16a.rb +10 -0
  119. data/test/data/users_guide/example16a.result +4 -0
  120. data/test/data/users_guide/example16b.rb +13 -0
  121. data/test/data/users_guide/example16b.result +4 -0
  122. data/test/data/users_guide/example16c.rb +12 -0
  123. data/test/data/users_guide/example16c.result +4 -0
  124. data/test/data/users_guide/example1_S.result +14 -0
  125. data/test/data/users_guide/example1_SXNC.result +10 -0
  126. data/test/data/users_guide/example1_source.result +14 -0
  127. data/test/data/users_guide/example2.rbhtml +3 -0
  128. data/test/data/users_guide/example2_sb.result2 +9 -0
  129. data/test/data/users_guide/example3.rbhtml +5 -0
  130. data/test/data/users_guide/example3_syntaxcheck.result +2 -0
  131. data/test/data/users_guide/example4.rbhtml +13 -0
  132. data/test/data/users_guide/example4_datafile_rb.result +13 -0
  133. data/test/data/users_guide/example4_yaml.result +13 -0
  134. data/test/data/users_guide/example5.rbhtml +9 -0
  135. data/test/data/users_guide/example5_datastr_rb.result +9 -0
  136. data/test/data/users_guide/example5_datastr_yaml.result +9 -0
  137. data/test/data/users_guide/example6.rbhtml +19 -0
  138. data/test/data/users_guide/example6_layout.result +29 -0
  139. data/test/data/users_guide/example6_nested.result +28 -0
  140. data/test/data/users_guide/example7_layout2.result +13 -0
  141. data/test/data/users_guide/example8_layout3.result +8 -0
  142. data/test/data/users_guide/example9.rbhtml +18 -0
  143. data/test/data/users_guide/example9_capture.result +26 -0
  144. data/test/data/users_guide/footer.html +5 -0
  145. data/test/data/users_guide/footer.rbhtml +4 -0
  146. data/test/data/users_guide/layout6.rbhtml +17 -0
  147. data/test/data/users_guide/layout7.rbhtml +9 -0
  148. data/test/data/users_guide/layout8_html.rbhtml +5 -0
  149. data/test/data/users_guide/layout8_xhtml.rbhtml +6 -0
  150. data/test/data/users_guide/layout9.rbhtml +25 -0
  151. data/test/data/users_guide/sidemenu.rbhtml +5 -0
  152. data/test/data/users_guide/user_app.cgi +39 -0
  153. data/test/data/users_guide/user_app.result +30 -0
  154. data/test/data/users_guide/user_create.rbhtml +6 -0
  155. data/test/data/users_guide/user_edit.rbhtml +7 -0
  156. data/test/data/users_guide/user_form.rbhtml +10 -0
  157. data/test/data/users_guide/user_layout.rbhtml +16 -0
  158. data/test/test_all.rb +23 -0
  159. data/test/test_engine.rb +526 -0
  160. data/test/test_engine.yaml +2039 -0
  161. data/test/test_examples.rb +81 -0
  162. data/test/test_faq.rb +60 -0
  163. data/test/test_htmlhelper.rb +78 -0
  164. data/test/test_main.rb +564 -0
  165. data/test/test_main.yaml +174 -0
  166. data/test/test_template.rb +113 -0
  167. data/test/test_template.yaml +1244 -0
  168. data/test/test_users_guide.rb +75 -0
  169. data/test/testcase-helper.rb +166 -0
  170. metadata +226 -0
@@ -0,0 +1,17 @@
1
+ require 'yaml'
2
+
3
+ class StockInfo
4
+ def initialize(values={})
5
+ @name = values['name']
6
+ @name2 = values['name2']
7
+ @url = values['url']
8
+ @symbol = values['symbol']
9
+ @price = values['price']
10
+ @change = values['change']
11
+ @ratio = values['ratio']
12
+ end
13
+ attr_accessor :name, :name2, :url, :symbol, :price, :change, :ratio
14
+ end
15
+
16
+ ydoc = YAML.load_file('bench_context.yaml')
17
+ @list = ydoc['list'].collect { |values| StockInfo.new(values) }
@@ -0,0 +1,141 @@
1
+ list:
2
+ - name: Adobe Systems
3
+ name2: Adobe Systems Inc.
4
+ url: http://www.adobe.com
5
+ symbol: ADBE
6
+ price: 39.26
7
+ change: 0.13
8
+ ratio: 0.33
9
+ - name: Advanced Micro Devices
10
+ name2: Advanced Micro Devices Inc.
11
+ url: http://www.amd.com
12
+ symbol: AMD
13
+ price: 16.22
14
+ change: 0.17
15
+ ratio: 1.06
16
+ - name: Amazon.com
17
+ name2: Amazon.com Inc
18
+ url: http://www.amazon.com
19
+ symbol: AMZN
20
+ price: 36.85
21
+ change: -0.23
22
+ ratio: -0.62
23
+ - name: Apple
24
+ name2: Apple Inc.
25
+ url: http://www.apple.com
26
+ symbol: AAPL
27
+ price: 85.38
28
+ change: -0.87
29
+ ratio: -1.01
30
+ - name: BEA Systems
31
+ name2: BEA Systems Inc.
32
+ url: http://www.bea.com
33
+ symbol: BEAS
34
+ price: 12.46
35
+ change: 0.09
36
+ ratio: 0.73
37
+ - name: CA
38
+ name2: CA, Inc.
39
+ url: http://www.ca.com
40
+ symbol: CA
41
+ price: 24.66
42
+ change: 0.38
43
+ ratio: 1.57
44
+ - name: Cisco Systems
45
+ name2: Cisco Systems Inc.
46
+ url: http://www.cisco.com
47
+ symbol: CSCO
48
+ price: 26.35
49
+ change: 0.13
50
+ ratio: 0.5
51
+ - name: Dell
52
+ name2: Dell Corp.
53
+ url: http://www.dell.com/
54
+ symbol: DELL
55
+ price: 23.73
56
+ change: -0.42
57
+ ratio: -1.74
58
+ - name: eBay
59
+ name2: eBay Inc.
60
+ url: http://www.ebay.com
61
+ symbol: EBAY
62
+ price: 31.65
63
+ change: -0.8
64
+ ratio: -2.47
65
+ - name: Google
66
+ name2: Google Inc.
67
+ url: http://www.google.com
68
+ symbol: GOOG
69
+ price: 495.84
70
+ change: 7.75
71
+ ratio: 1.59
72
+ - name: Hewlett-Packard
73
+ name2: Hewlett-Packard Co.
74
+ url: http://www.hp.com
75
+ symbol: HPQ
76
+ price: 41.69
77
+ change: -0.02
78
+ ratio: -0.05
79
+ - name: IBM
80
+ name2: International Business Machines Corp.
81
+ url: http://www.ibm.com
82
+ symbol: IBM
83
+ price: 97.45
84
+ change: -0.06
85
+ ratio: -0.06
86
+ - name: Intel
87
+ name2: Intel Corp.
88
+ url: http://www.intel.com
89
+ symbol: INTC
90
+ price: 20.53
91
+ change: -0.07
92
+ ratio: -0.34
93
+ - name: Juniper Networks
94
+ name2: Juniper Networks, Inc
95
+ url: http://www.juniper.net/
96
+ symbol: JNPR
97
+ price: 18.96
98
+ change: 0.5
99
+ ratio: 2.71
100
+ - name: Microsoft
101
+ name2: Microsoft Corp
102
+ url: http://www.microsoft.com
103
+ symbol: MSFT
104
+ price: 30.6
105
+ change: 0.15
106
+ ratio: 0.49
107
+ - name: Oracle
108
+ name2: Oracle Corp.
109
+ url: http://www.oracle.com
110
+ symbol: ORCL
111
+ price: 17.15
112
+ change: 0.17
113
+ ratio: 1.0
114
+ - name: SAP
115
+ name2: SAP AG
116
+ url: http://www.sap.com
117
+ symbol: SAP
118
+ price: 46.2
119
+ change: -0.16
120
+ ratio: -0.35
121
+ - name: Seagate Technology
122
+ name2: Seagate Technology
123
+ url: http://www.seagate.com/
124
+ symbol: STX
125
+ price: 27.35
126
+ change: -0.36
127
+ ratio: -1.3
128
+ - name: Sun Microsystems
129
+ name2: Sun Microsystems Inc.
130
+ url: http://www.sun.com
131
+ symbol: SUNW
132
+ price: 6.33
133
+ change: -0.01
134
+ ratio: -0.16
135
+ - name: Yahoo
136
+ name2: Yahoo! Inc.
137
+ url: http://www.yahoo.com
138
+ symbol: YHOO
139
+ price: 28.04
140
+ change: -0.17
141
+ ratio: -0.6
@@ -0,0 +1,4 @@
1
+ </table>
2
+
3
+ </body>
4
+ </html>
@@ -0,0 +1,52 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
3
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
+ <head>
6
+ <title>Stock Prices</title>
7
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
8
+ <meta http-equiv="Content-Style-Type" content="text/css" />
9
+ <meta http-equiv="Content-Script-Type" content="text/javascript" />
10
+ <link rel="shortcut icon" href="/images/favicon.ico" />
11
+ <link rel="stylesheet" type="text/css" href="/css/style.css" media="all" />
12
+ <script type="text/javascript" src="/js/util.js"></script>
13
+ <style type="text/css">
14
+ /*<![CDATA[*/
15
+
16
+ body {
17
+ color: #333333;
18
+ line-height: 150%;
19
+ }
20
+
21
+ thead {
22
+ font-weight: bold;
23
+ background-color: #CCCCCC;
24
+ }
25
+
26
+ .odd {
27
+ background-color: #FFCCCC;
28
+ }
29
+
30
+ .even {
31
+ background-color: #CCCCFF;
32
+ }
33
+
34
+ .minus {
35
+ color: #FF0000;
36
+ }
37
+
38
+ /*]]>*/
39
+ </style>
40
+
41
+ </head>
42
+
43
+ <body>
44
+
45
+ <h1>Stock Prices</h1>
46
+
47
+ <table>
48
+ <thead>
49
+ <tr>
50
+ <th>#</th><th>symbol</th><th>name</th><th>price</th><th>change</th><th>ratio</th>
51
+ </tr>
52
+ </thead>
@@ -0,0 +1,29 @@
1
+ <tbody>
2
+ <%
3
+ n = 0
4
+ for item in list
5
+ n += 1
6
+ %>
7
+ <tr class="<%= n % 2 == 0 ? 'even' : 'odd' %>">
8
+ <td style="text-align: center"><%= n %></td>
9
+ <td>
10
+ <a href="/stocks/<%= item['symbol'] %>"><%= item['symbol'] %></a>
11
+ </td>
12
+ <td>
13
+ <a href="<%= item['url'] %>"><%= item['name'] %></a>
14
+ </td>
15
+ <td>
16
+ <strong><%= item['price'] %></strong>
17
+ </td>
18
+ <% if item['change'] < 0.0 %>
19
+ <td class="minus"><%= item['change'] %></td>
20
+ <td class="minus"><%= item['ratio'] %></td>
21
+ <% else %>
22
+ <td><%= item['change'] %></td>
23
+ <td><%= item['ratio'] %></td>
24
+ <% end %>
25
+ </tr>
26
+ <%
27
+ end
28
+ %>
29
+ </tbody>
@@ -0,0 +1,29 @@
1
+ <tbody>
2
+ <?rb
3
+ n = 0
4
+ for item in @list
5
+ n += 1
6
+ ?>
7
+ <tr class="#{n % 2 == 0 ? 'even' : 'odd'}">
8
+ <td style="text-align: center">#{n}</td>
9
+ <td>
10
+ <a href="/stocks/#{item['symbol']}">#{item['symbol']}</a>
11
+ </td>
12
+ <td>
13
+ <a href="#{item['url']}">#{item['name']}</a>
14
+ </td>
15
+ <td>
16
+ <strong>#{item['price']}</strong>
17
+ </td>
18
+ <?rb if item['change'] < 0.0 ?>
19
+ <td class="minus">#{item['change']}</td>
20
+ <td class="minus">#{item['ratio']}</td>
21
+ <?rb else ?>
22
+ <td>#{item['change']}</td>
23
+ <td>#{item['ratio']}</td>
24
+ <?rb end ?>
25
+ </tr>
26
+ <?rb
27
+ end
28
+ ?>
29
+ </tbody>
data/bin/rbtenjin ADDED
@@ -0,0 +1,449 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ##
4
+ ## copyright(c) 2007 kuwata-lab all rights reserved.
5
+ ##
6
+ ## Permission is hereby granted, free of charge, to any person obtaining
7
+ ## a copy of this software and associated documentation files (the
8
+ ## "Software"), to deal in the Software without restriction, including
9
+ ## without limitation the rights to use, copy, modify, merge, publish,
10
+ ## distribute, sublicense, and/or sell copies of the Software, and to
11
+ ## permit persons to whom the Software is furnished to do so, subject to
12
+ ## the following conditions:
13
+ ##
14
+ ## The above copyright notice and this permission notice shall be
15
+ ## included in all copies or substantial portions of the Software.
16
+ ##
17
+ ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ ## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ ## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ ## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ ## LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ ## OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ ## WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ ##
25
+
26
+ ## $Rev: 59 $
27
+ ## $Release: 0.6.0 $
28
+
29
+ require 'tenjin'
30
+
31
+ module Tenjin
32
+
33
+
34
+ module NoTextModule
35
+
36
+ def initialize(*args)
37
+ @noexpr = args[-1].is_a?(Hash) ? args[-1].delete(:noexpr) : nil
38
+ super
39
+ end
40
+
41
+ def start_text_part()
42
+ nil
43
+ end
44
+
45
+ def stop_text_part()
46
+ nil
47
+ end
48
+
49
+ def add_text(text, encode_newline=false)
50
+ return unless text && !text.empty?
51
+ n = text.count("\n")
52
+ n -= 1 if encode_newline && text[-1] == ?\n
53
+ @script << (@newline * n)
54
+ end
55
+
56
+ ## add expression code
57
+ def add_expr(code, flag_escape=nil)
58
+ return if !code || code.empty?
59
+ if @noexpr
60
+ @script << (@newline * code.count("\n"))
61
+ elsif flag_escape
62
+ #@script << "_buf << %Q`\#{#{@escapefunc}((#{code}).to_s)}`"
63
+ @script << "#{@escapefunc}((#{code}).to_s); "
64
+ else
65
+ #@script << "_buf << %Q`\#{#{code}}`"
66
+ @script << "#{code}; "
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+
73
+ class NoTextTemplate < Template
74
+ include NoTextModule
75
+ end
76
+
77
+
78
+ class CommandOptionError < StandardError
79
+ end
80
+
81
+
82
+ class Main
83
+
84
+
85
+ def self.main()
86
+ begin
87
+ output = self.new().execute()
88
+ print output if output
89
+ exit(0)
90
+ rescue CommandOptionError => ex
91
+ $stderr.puts ex.message
92
+ exit(1)
93
+ end
94
+ end
95
+
96
+
97
+ def initialize(argv=nil)
98
+ @command = File.basename($0)
99
+ @argv = argv || ARGV
100
+ end
101
+
102
+
103
+ def execute()
104
+ command = @command
105
+
106
+ ## parse options
107
+ noargopts = 'hvsSNXCUbxzqwTPdD'
108
+ argopts = 'fckra'
109
+ argopts2 = ''
110
+ command = File.basename($0)
111
+ options, properties, filenames = parse_argv(@argv, noargopts, argopts, argopts2)
112
+ debug = options[:D]
113
+ debug and $stderr.puts "*** debug: options=#{options.inspect}"
114
+ debug and $stderr.puts "*** debug: properties=#{properties.inspect}"
115
+ debug and $stderr.puts "*** debug: filenames=#{filenames.inspect}"
116
+
117
+ ## help, version
118
+ return usage() if options[:h] || properties[:help]
119
+ return version()+"\n" if options[:v] || properties[:version]
120
+
121
+ ## check options
122
+ datafile = options[:f]
123
+ if datafile && !test(?f, datafile)
124
+ raise _error("-f %s: file not found." % datafile)
125
+ end
126
+
127
+ ## action
128
+ action = options[:a]
129
+ actions = %w[render convert retrieve statements syntax dump preprocess]
130
+ if action
131
+ unless actions.include?(action)
132
+ raise _error("-a #{action}: unknown action.")
133
+ end
134
+ action = action.intern
135
+ else
136
+ action = options[:s] && :convert ||
137
+ options[:X] && :statements ||
138
+ options[:S] && :retrieve ||
139
+ options[:z] && :syntax ||
140
+ options[:P] && :preprocess ||
141
+ true && :render
142
+ end
143
+
144
+ ## ruby global options
145
+ $KCODE = options[:k] if options[:k]
146
+ $VERBOSE = 2 if options[:w]
147
+
148
+ ## lib
149
+ if options[:r]
150
+ begin
151
+ for lib in options[:r].split(/,/)
152
+ require lib
153
+ end
154
+ rescue LoadError => ex
155
+ raise _error("-r %s: library not found." % lib)
156
+ end
157
+ end
158
+
159
+ ## context data file
160
+ context = Tenjin::Context.new
161
+ if options[:f]
162
+ datafile = options[:f]
163
+ content = File.read(datafile)
164
+ if datafile =~ /\.ya?ml\z/
165
+ content = _expandtabs(content) unless options[:T]
166
+ hash = load_yaml(content, datafile)
167
+ context.update(hash)
168
+ elsif datafile =~ /\.rb\z/
169
+ load_rubycode_as_contextdata(content, context, datafile)
170
+ else
171
+ raise _error("-f %s: unknown file type ('*.yaml' or '*.rb' expected)." % datafile)
172
+ end
173
+ end
174
+
175
+ ## context data
176
+ if options[:c]
177
+ if options[:c][0] == ?{
178
+ yamlstr = options[:c]
179
+ hash = load_yaml(yamlstr, '-c')
180
+ context.update(hash)
181
+ else
182
+ ruby_code = options[:c]
183
+ load_rubycode_as_contextdata(ruby_code, context, '-c')
184
+ end
185
+ end
186
+
187
+ ## set initalizer option for Tenjin::Template.new()
188
+ if options[:b]
189
+ properties[:preamble] = properties[:postamble] = nil
190
+ elsif action == :convert || action == :retrieve || action == :statements
191
+ properties[:preamble] = true unless properties.key?(:preamble)
192
+ properties[:postamble] = true unless properties.key?(:postamble)
193
+ end
194
+
195
+ ## set initalizer option for Tenjin::Engine.new()
196
+ properties[:cache] = false unless properties.key?(:cache)
197
+ properties[:path] = properties[:path].split(/,/) if properties[:path]
198
+ if properties.key?(:templateclass)
199
+ classname = properties[:templateclass]
200
+ klass = _to_class(classname)
201
+ properties[:templateclass] = klass
202
+ elsif action == :preprocess || options[:P]
203
+ properties[:templateclass] = Tenjin::Preprocessor
204
+ properties[:preprocess] = false
205
+ elsif action == :retrieve
206
+ properties[:templateclass] = Tenjin::NoTextTemplate
207
+ elsif action == :statements
208
+ properties[:templateclass] = Tenjin::NoTextTemplate
209
+ properties[:noexpr] = true
210
+ end
211
+
212
+ ## create Engine instance
213
+ engine = Tenjin::Engine.new(properties)
214
+
215
+ ## execute
216
+ filenames = [nil] if filenames.empty?
217
+ output_buf = ''
218
+ for template_name in filenames
219
+ if template_name.nil?
220
+ input = $stdin.read()
221
+ template_class = properties.fetch(:templateclass, Tenjin::Template)
222
+ template = template_class.new(properties)
223
+ engine.register_template(template_name, template)
224
+ end
225
+ case action
226
+ when :convert, :retrieve, :statements
227
+ template = engine.get_template(template_name)
228
+ output = template.script
229
+ when :syntax
230
+ template = engine.get_template(template_name)
231
+ output = check_syntax(template.script, template.filename, options[:q], options[:w])
232
+ when :render, :preprocess
233
+ output = engine.render(template_name, context)
234
+ else
235
+ assert unreachable
236
+ end
237
+ output_buf << manipulate_output(output, options)
238
+ end
239
+ return output_buf
240
+ end
241
+
242
+
243
+ def check_syntax(script, filename, quiet, warning)
244
+ command = warning ? "ruby -wc" : "ruby -c"
245
+ require 'open3'
246
+ stdin, stdout, stderr = *Open3.popen3(command)
247
+ stdin.write(script) ; stdin.close()
248
+ result = stdout.read() ; stdout.close()
249
+ errmsg = stderr.read() ; stderr.close()
250
+ if errmsg && !errmsg.empty?
251
+ errmsg.gsub!(/^-(:\d+: )/, "#{filename}\\1")
252
+ return errmsg
253
+ else
254
+ return quiet ? '' : "#{filename}: #{result}"
255
+ end
256
+ end
257
+
258
+
259
+ def manipulate_output(output, options)
260
+ flag_linenum = options[:N] # add line numbers
261
+ flag_compact = options[:C] # remove empty lines
262
+ flag_unique = options[:U] # compress empty lines to a line
263
+ if flag_linenum
264
+ n = 0
265
+ output.gsub!(/^/) { n += 1; "%5d: " % n }
266
+ output.gsub!(/^ *\d+:\s+?\n/, '') if flag_compact
267
+ output.gsub!(/(^ *\d+:\s+?\n)+/, "\n") if flag_unique
268
+ else
269
+ output.gsub!(/^\s*?\n/, '') if flag_compact
270
+ output.gsub!(/(^\s*?\n)+/, "\n") if flag_unique
271
+ end
272
+ return output
273
+ end
274
+
275
+
276
+ def load_yaml(yamlstr, filename)
277
+ require 'yaml'
278
+ hash = YAML.load(yamlstr)
279
+ unless hash.is_a?(Hash)
280
+ raise _error("%s: not a mapping (hash)." % filename)
281
+ end
282
+ return hash
283
+ end
284
+
285
+
286
+ def load_rubycode_as_contextdata(_rubycode, _context, _filename)
287
+ _context.instance_eval(_rubycode, _filename)
288
+ end
289
+
290
+
291
+ def usage(command=nil)
292
+ command ||= @command
293
+ s = <<"END"
294
+ Usage: #{command} [..options..] [file1 [file2...]]
295
+ -h, --help : help
296
+ -v, --version : version
297
+ -a action : action
298
+ -a render : render template (default)
299
+ -a convert : convert template into script
300
+ -a retrieve : retrieve statements and expressons
301
+ -a statements : retrieve only statements
302
+ -a syntax : syntax check
303
+ -a preprocess : show preprocessed template
304
+ -s : alias of '-a convert'
305
+ -S : alias of '-a retrieve'
306
+ -z : alias of '-a syntax'
307
+ -X : alias of '-a statements'
308
+ # -d : alias of '-a dump'
309
+ -P : alias of '-a preprocess'
310
+ -N : add line number
311
+ -C : compact: remove empty lines
312
+ -U : uniq: compress empty lines into a line
313
+ -b : remove "_buf='';" and "_buf.to_s"
314
+ -q : quet mode (for '-a syntax')
315
+ # -w : use strict package
316
+ -c string : context data string (yaml or ruby)
317
+ -f file : context data file (*.yaml or *.rb)
318
+ -T : unexpand tab chars in datafile
319
+ -r lib1,lib2,.. : require libraries
320
+ -k code : kanji code for $KCODE
321
+ # --encoding=encoding : encoding name, with converting into unicode
322
+ --escapefunc=name : 'escape' function name
323
+ # --tostrfunc=name : 'to_str' function name
324
+ --preamble=text : preamble which is insreted into ruby script
325
+ --postamble=text : postamble which is insreted into ruby script
326
+ # --smarttrim : trim "\\n{expr}\\n" into "\\n{expr}".
327
+ --prefix=str : prefix string for template shortname
328
+ --postfix=str : postfix string for template shortname
329
+ --layout=filename : layout template name
330
+ --path=dir1,dir2,.. : template lookup path
331
+ --preprocess : activate preprocessing
332
+ --templateclass=name: template class (default: Tenjin::Template)
333
+ Examples:
334
+ ex1. render template
335
+ $ #{command} file.rbhtml
336
+ ex2. convert template into ruby script
337
+ $ #{command} -a convert file.rbhtml
338
+ $ #{command} -a retrieve -UN file.rbhtml # for debug
339
+ ex3. render with context data file (*.yaml or *.rb)
340
+ $ #{command} -f datafile.yaml file.rbhtml
341
+ ex4. render with context data string
342
+ $ #{command} -c '{title: tenjin example, items: [1, 2, 3]}' file.rbhtml # yaml
343
+ $ #{command} -c '@title="tenjin example"; @items=[1,2,3]' file.rbhtml # ruby
344
+ ex5. syntax check
345
+ $ #{command} -wa syntax *.rbhtml # or '-wz'
346
+ END
347
+ return s.gsub(/^\#.*\n/, '')
348
+ end
349
+
350
+
351
+ def version()
352
+ return RELEASE
353
+ end
354
+
355
+
356
+ def parse_argv(argv, noargopts, argopts, argopts2='')
357
+ options = {}
358
+ properties = {}
359
+ while argv[0] && argv[0][0] == ?-
360
+ optstr = argv.shift
361
+ if optstr == '-'
362
+ break
363
+ elsif optstr =~ /\A--([-\w]+)(=.*)?/
364
+ name = $1
365
+ value = $2 && !$2.empty? ? _to_value($2[1..-1]) : true
366
+ properties[name.intern] = value
367
+ else
368
+ optstr = optstr[1..-1]
369
+ while !optstr.empty?
370
+ ch = optstr[0].chr
371
+ optstr = optstr[1..-1]
372
+ if noargopts.include?(ch)
373
+ options[ch.intern] = true
374
+ elsif argopts.include?(ch)
375
+ if !optstr.empty?
376
+ options[ch.intern] = optstr
377
+ optstr = ''
378
+ elsif !argv.empty?
379
+ options[ch.intern] = argv.shift
380
+ else
381
+ raise _error("-#{ch}: argument required.")
382
+ end
383
+ elsif argopts2.include?(ch)
384
+ options[ch.intern] = !optstr.empty? ? optstr : true
385
+ optstr = ''
386
+ else
387
+ raise _error("-#{ch}: unknown option.")
388
+ end
389
+ end
390
+ end
391
+ end
392
+ filenames = argv
393
+ return options, properties, filenames
394
+ end
395
+
396
+
397
+ def _to_value(s)
398
+ case s
399
+ when 'true', 'yes' ; return true
400
+ when 'false', 'no' ; return false
401
+ when 'null', 'nil' ; return nil
402
+ when /\A\d+\z/ ; return s.to_i
403
+ when /\A\d+\.\d+\z/ ; return s.to_f
404
+ when /\A:[-\w]+\z/ ; return s[1..-1].intern
405
+ else ; return s
406
+ end
407
+ end
408
+
409
+
410
+ def _to_class(classname)
411
+ klass = Object
412
+ begin
413
+ classname.split(/::/).each { |m| klass = klass.const_get(m) }
414
+ rescue NameError => ex
415
+ raise _error("%s: class not found." % classname)
416
+ end
417
+ return klass
418
+ end
419
+
420
+
421
+ def _error(message)
422
+ return CommandOptionError.new(message)
423
+ end
424
+
425
+
426
+ def _expandtabs(input, width=8)
427
+ arr = input.split(/\t/)
428
+ last = arr.pop
429
+ buf = ''
430
+ arr.each do |s|
431
+ column = (n = s.rindex(?\n)) ? s.length - n - 1 : s.length
432
+ n = width - (column % width)
433
+ buf << s << (' ' * n)
434
+ end
435
+ buf << last
436
+ return buf
437
+ end
438
+
439
+
440
+ end
441
+
442
+
443
+ end
444
+
445
+
446
+ #if __FILE__ == $0
447
+ unless defined?(DONT_INVOKE)
448
+ Tenjin::Main.main()
449
+ end