mirah 0.0.4-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. data/History.txt +15 -0
  2. data/README.txt +51 -0
  3. data/Rakefile +86 -0
  4. data/bin/duby +10 -0
  5. data/bin/dubyc +10 -0
  6. data/bin/dubyp +10 -0
  7. data/bin/jrubyp +36 -0
  8. data/bin/mirah +9 -0
  9. data/bin/mirah.cmd +1 -0
  10. data/bin/mirahc +9 -0
  11. data/bin/mirahc.cmd +1 -0
  12. data/bin/mirahp +9 -0
  13. data/bin/mirahp.cmd +1 -0
  14. data/examples/ant/example-build.xml +7 -0
  15. data/examples/appengine/Rakefile +19 -0
  16. data/examples/appengine/Readme +29 -0
  17. data/examples/appengine/src/org/mirah/MirahApp.mirah +57 -0
  18. data/examples/appengine/src/org/mirah/list.dhtml +15 -0
  19. data/examples/appengine/war/WEB-INF/lib/dubydatastore.jar +0 -0
  20. data/examples/bintrees.mirah +66 -0
  21. data/examples/construction.mirah +8 -0
  22. data/examples/dynamic.mirah +17 -0
  23. data/examples/edb.mirah +3 -0
  24. data/examples/fib.mirah +16 -0
  25. data/examples/fields.mirah +22 -0
  26. data/examples/fractal.mirah +55 -0
  27. data/examples/java_thing.mirah +13 -0
  28. data/examples/plugins/appengine/Rakefile +55 -0
  29. data/examples/plugins/appengine/lib/com/google/appengine/ext/duby/db/datastore.rb +375 -0
  30. data/examples/plugins/appengine/src/com/google/appengine/ext/duby/db/Model.duby +336 -0
  31. data/examples/plugins/appengine/test/com/google/appengine/ext/duby/db/ModelTest.duby +113 -0
  32. data/examples/simple_class.mirah +12 -0
  33. data/examples/sort_closure.mirah +7 -0
  34. data/examples/swing.mirah +20 -0
  35. data/examples/tak.mirah +15 -0
  36. data/examples/test.edb +9 -0
  37. data/examples/wiki/Rakefile +18 -0
  38. data/examples/wiki/src/org/mirah/wiki/MirahWiki.duby +324 -0
  39. data/examples/wiki/src/org/mirah/wiki/edit.eduby.html +42 -0
  40. data/examples/wiki/src/org/mirah/wiki/error.eduby.html +2 -0
  41. data/examples/wiki/src/org/mirah/wiki/layout.eduby.html +69 -0
  42. data/examples/wiki/src/org/mirah/wiki/parser.eduby.html +7 -0
  43. data/examples/wiki/src/org/mirah/wiki/view.eduby.html +15 -0
  44. data/examples/wiki/war/WEB-INF/classes/test/HeredocContext.class +0 -0
  45. data/examples/wiki/war/WEB-INF/classes/test/MirahParser.class +0 -0
  46. data/examples/wiki/war/WEB-INF/lib/appengine-api.jar +0 -0
  47. data/examples/wiki/war/WEB-INF/lib/dubydatastore.jar +0 -0
  48. data/examples/wiki/war/WEB-INF/lib/jmeta-runtime.jar +0 -0
  49. data/examples/wiki/war/WEB-INF/lib/pegdown-stubs.jar +0 -0
  50. data/examples/wiki/war/WEB-INF/pegdown.jar +0 -0
  51. data/examples/wiki/war/app.yaml +21 -0
  52. data/examples/wiki/war/public/favicon.ico +0 -0
  53. data/examples/wiki/war/public/images/appengine_duby.png +0 -0
  54. data/examples/wiki/war/public/images/back.gif +0 -0
  55. data/examples/wiki/war/public/images/dir.gif +0 -0
  56. data/examples/wiki/war/public/images/file.gif +0 -0
  57. data/examples/wiki/war/public/javascripts/prettify.js +61 -0
  58. data/examples/wiki/war/public/robots.txt +0 -0
  59. data/examples/wiki/war/public/stylesheets/main.css +156 -0
  60. data/examples/wiki/war/public/stylesheets/prettify.css +1 -0
  61. data/examples/wiki/war/public/stylesheets/sh_style.css +66 -0
  62. data/examples/wiki/war/public/stylesheets/source.css +21 -0
  63. data/examples/wiki/war/public/wmd/images/bg-fill.png +0 -0
  64. data/examples/wiki/war/public/wmd/images/bg.png +0 -0
  65. data/examples/wiki/war/public/wmd/images/blockquote.png +0 -0
  66. data/examples/wiki/war/public/wmd/images/bold.png +0 -0
  67. data/examples/wiki/war/public/wmd/images/code.png +0 -0
  68. data/examples/wiki/war/public/wmd/images/h1.png +0 -0
  69. data/examples/wiki/war/public/wmd/images/hr.png +0 -0
  70. data/examples/wiki/war/public/wmd/images/img.png +0 -0
  71. data/examples/wiki/war/public/wmd/images/italic.png +0 -0
  72. data/examples/wiki/war/public/wmd/images/link.png +0 -0
  73. data/examples/wiki/war/public/wmd/images/ol.png +0 -0
  74. data/examples/wiki/war/public/wmd/images/redo.png +0 -0
  75. data/examples/wiki/war/public/wmd/images/separator.png +0 -0
  76. data/examples/wiki/war/public/wmd/images/ul.png +0 -0
  77. data/examples/wiki/war/public/wmd/images/undo.png +0 -0
  78. data/examples/wiki/war/public/wmd/images/wmd-on.png +0 -0
  79. data/examples/wiki/war/public/wmd/images/wmd.png +0 -0
  80. data/examples/wiki/war/public/wmd/showdown.js +421 -0
  81. data/examples/wiki/war/public/wmd/wmd-base.js +1799 -0
  82. data/examples/wiki/war/public/wmd/wmd-plus.js +311 -0
  83. data/examples/wiki/war/public/wmd/wmd.js +73 -0
  84. data/javalib/JRubyParser.jar +0 -0
  85. data/javalib/dynalang-invoke-0.1.jar +0 -0
  86. data/javalib/mirah-bootstrap.jar +0 -0
  87. data/javalib/mirah-parser.jar +0 -0
  88. data/lib/duby.rb +2 -0
  89. data/lib/mirah.rb +338 -0
  90. data/lib/mirah/appengine_tasks.rb +146 -0
  91. data/lib/mirah/ast.rb +615 -0
  92. data/lib/mirah/ast/call.rb +307 -0
  93. data/lib/mirah/ast/class.rb +311 -0
  94. data/lib/mirah/ast/flow.rb +364 -0
  95. data/lib/mirah/ast/intrinsics.rb +470 -0
  96. data/lib/mirah/ast/literal.rb +154 -0
  97. data/lib/mirah/ast/local.rb +89 -0
  98. data/lib/mirah/ast/method.rb +360 -0
  99. data/lib/mirah/ast/scope.rb +208 -0
  100. data/lib/mirah/ast/structure.rb +226 -0
  101. data/lib/mirah/ast/type.rb +130 -0
  102. data/lib/mirah/compiler.rb +341 -0
  103. data/lib/mirah/env.rb +33 -0
  104. data/lib/mirah/jvm/base.rb +258 -0
  105. data/lib/mirah/jvm/compiler.rb +885 -0
  106. data/lib/mirah/jvm/method_lookup.rb +203 -0
  107. data/lib/mirah/jvm/source_compiler.rb +737 -0
  108. data/lib/mirah/jvm/source_generator/builder.rb +444 -0
  109. data/lib/mirah/jvm/source_generator/loops.rb +110 -0
  110. data/lib/mirah/jvm/source_generator/precompile.rb +188 -0
  111. data/lib/mirah/jvm/source_generator/typer.rb +11 -0
  112. data/lib/mirah/jvm/typer.rb +151 -0
  113. data/lib/mirah/jvm/types.rb +416 -0
  114. data/lib/mirah/jvm/types/basic_types.rb +33 -0
  115. data/lib/mirah/jvm/types/boolean.rb +17 -0
  116. data/lib/mirah/jvm/types/enumerable.rb +65 -0
  117. data/lib/mirah/jvm/types/extensions.rb +86 -0
  118. data/lib/mirah/jvm/types/factory.rb +186 -0
  119. data/lib/mirah/jvm/types/floats.rb +86 -0
  120. data/lib/mirah/jvm/types/integers.rb +171 -0
  121. data/lib/mirah/jvm/types/intrinsics.rb +376 -0
  122. data/lib/mirah/jvm/types/literals.rb +74 -0
  123. data/lib/mirah/jvm/types/methods.rb +614 -0
  124. data/lib/mirah/jvm/types/number.rb +143 -0
  125. data/lib/mirah/nbcompiler.rb +29 -0
  126. data/lib/mirah/plugin/edb.rb +29 -0
  127. data/lib/mirah/plugin/gwt.rb +173 -0
  128. data/lib/mirah/plugin/java.rb +55 -0
  129. data/lib/mirah/transform.rb +266 -0
  130. data/lib/mirah/transform2.rb +728 -0
  131. data/lib/mirah/typer.rb +407 -0
  132. data/lib/mirah_task.rb +107 -0
  133. data/test/test_ast.rb +359 -0
  134. data/test/test_compilation.rb +112 -0
  135. data/test/test_env.rb +42 -0
  136. data/test/test_gwt.rb +58 -0
  137. data/test/test_java_typer.rb +183 -0
  138. data/test/test_javac_compiler.rb +63 -0
  139. data/test/test_jvm_compiler.rb +2607 -0
  140. data/test/test_typer.rb +221 -0
  141. metadata +235 -0
@@ -0,0 +1,7 @@
1
+ import java.util.Collections
2
+ import java.util.ArrayList
3
+
4
+ list = ArrayList.new [9,5,2,6,8,5,0,3,6,1,8,3,6,4,7,5,0,8,5,6,7,2,3]
5
+ puts "unsorted: #{list}"
6
+ Collections.sort(list) {|a,b| Integer(a).compareTo(b)}
7
+ puts "sorted: #{list}"
@@ -0,0 +1,20 @@
1
+ import javax.swing.JFrame
2
+ import javax.swing.JButton
3
+
4
+ # FIXME blocks need to be inside a MethodDefinition, but main doesn't
5
+ # have one.
6
+ def self.run
7
+ frame = JFrame.new "Welcome to Duby"
8
+ frame.setSize 300, 300
9
+ frame.setVisible true
10
+
11
+ button = JButton.new "Press me"
12
+ frame.add button
13
+ frame.show
14
+
15
+ button.addActionListener do |event|
16
+ JButton(event.getSource).setText "Duby Rocks!"
17
+ end
18
+ end
19
+
20
+ run
@@ -0,0 +1,15 @@
1
+ def tak(x:fixnum, y:fixnum, z:fixnum)
2
+ unless y < x
3
+ z
4
+ else
5
+ tak( tak(x-1, y, z),
6
+ tak(y-1, z, x),
7
+ tak(z-1, x, y))
8
+ end
9
+ end
10
+
11
+ i = 0
12
+ while i<1000
13
+ tak(24, 16, 8)
14
+ i+=1
15
+ end
@@ -0,0 +1,9 @@
1
+ <html>
2
+ <head>
3
+ <title><%= @message %></title>
4
+ </head>
5
+ <body>
6
+ <h1><%= @message %></h1>
7
+ </body>
8
+ </html>
9
+
@@ -0,0 +1,18 @@
1
+ # Hack to use the latest version instead of the gems when developing Mirah
2
+ if File.exist?('../../lib/mirah.rb')
3
+ $: << File.expand_path('../../lib')
4
+ end
5
+ if File.exist?('../../../bitescript/lib/bitescript.rb')
6
+ $: << File.expand_path('../../../bitescript/lib/')
7
+ end
8
+ require 'mirah/appengine_tasks'
9
+
10
+ appengine_app :app
11
+
12
+ DUBY_APP = "#{Duby.dest_path}/org/mirah/wiki/MirahWiki.class"
13
+ Templates = Dir.glob("#{Duby.source_path}/org/mirah/wiki/*.eduby.html")
14
+
15
+ Rake::Task[DUBY_APP].enhance(Templates)
16
+
17
+ task :app => DUBY_APP
18
+ task :default => :server
@@ -0,0 +1,324 @@
1
+ import javax.servlet.http.HttpServlet
2
+ import javax.servlet.http.HttpServletRequest
3
+ import javax.servlet.Filter
4
+ import com.google.appengine.ext.duby.db.Model
5
+ import com.google.appengine.api.datastore.Text
6
+ import java.util.Date
7
+ import org.pegdown.PegDownProcessorStub
8
+ import com.google.appengine.api.users.UserServiceFactory
9
+ import com.google.appengine.api.users.User
10
+ import java.util.ArrayList
11
+ import java.util.List
12
+
13
+ class Page < Model
14
+ property 'title', String
15
+ property 'body', Text
16
+ property 'userid', String
17
+ property 'user', User
18
+ property 'nickname', String
19
+ property 'comment', String
20
+ property 'created', Date
21
+ property 'version', Long
22
+ property 'locked', Boolean
23
+ end
24
+
25
+ class Helper < HttpServlet
26
+ def markdown(text:String)
27
+ return "" unless text
28
+ flags = 0x5ff
29
+ if @allow_html
30
+ flags |= 0x200 unless @allow_html
31
+ @html_markdown ||= PegDownProcessorStub.new(flags)
32
+ @html_markdown.markdownToHtml(text)
33
+ else
34
+ @nohtml_markdown ||= PegDownProcessorStub.new(flags)
35
+ @nohtml_markdown.markdownToHtml(text)
36
+ end
37
+ end
38
+
39
+ def html=(enabled:boolean)
40
+ @allow_html = enabled
41
+ end
42
+
43
+ def page_name(name:String)
44
+ if name.nil?
45
+ return "Main"
46
+ else
47
+ name = name.replaceAll("\\W", "")
48
+ end
49
+ if name.equals("")
50
+ "Main"
51
+ else
52
+ name
53
+ end
54
+ end
55
+
56
+ def h(text:String)
57
+ return "" unless text
58
+ text = text.replace("&", "&amp;")
59
+ text = text.replace("<", "&lt;")
60
+ text = text.replace(">", "&gt;")
61
+ text = text.replace("\"", "&quot;")
62
+ text.replace("'", "&#39;")
63
+ end
64
+
65
+ def h(o:Object)
66
+ h(o.toString)
67
+ end
68
+
69
+ def_edb(layout, 'org/mirah/wiki/layout.eduby.html')
70
+
71
+ def with_layout(content:String)
72
+ @content = content
73
+ layout
74
+ end
75
+
76
+ def title
77
+ @title
78
+ end
79
+
80
+ def title=(title:String)
81
+ @title = title
82
+ end
83
+
84
+ def users
85
+ @users ||= UserServiceFactory.getUserService()
86
+ end
87
+
88
+ macro def admin?
89
+ quote { users.isUserAdmin }
90
+ end
91
+
92
+ def user
93
+ users.getCurrentUser
94
+ end
95
+
96
+ def nickname
97
+ user.getNickname.replaceAll('@.*', '')
98
+ end
99
+
100
+ def extra_links
101
+ @links ||= ArrayList.new
102
+ end
103
+
104
+ def url
105
+ '/'
106
+ end
107
+ end
108
+
109
+ class ViewPage < Helper
110
+ def_edb(view, 'org/mirah/wiki/view.eduby.html')
111
+
112
+ def doGet(request, response)
113
+ @url = request.getRequestURI
114
+ self.title = @name = page_name(request.getPathInfo)
115
+ canonical = "/" + @name
116
+ unless canonical.equals(request.getPathInfo)
117
+ response.sendRedirect(canonical)
118
+ return
119
+ end
120
+
121
+ @page = Page.get(@name)
122
+ self.html = @page.locked if @page
123
+ response.getWriter.write(with_layout(view))
124
+ end
125
+
126
+ def url
127
+ @url
128
+ end
129
+
130
+ def extra_links
131
+ links = ArrayList.new
132
+ if @page
133
+ can_edit = admin?
134
+ can_edit = true unless @page.locked
135
+ if can_edit
136
+ links.add(["Edit", "/edit/#{@page.title}"])
137
+ end
138
+ end
139
+ links.add(["New Page", "javascript:void(newPage())"])
140
+ links
141
+ end
142
+ end
143
+
144
+ import java.util.logging.Logger
145
+
146
+ class EditPage < Helper
147
+ def_edb(edit, 'org/mirah/wiki/edit.eduby.html')
148
+ def_edb(error, 'org/mirah/wiki/error.eduby.html')
149
+
150
+ def logger
151
+ @logger ||= Logger.getLogger("EditPage")
152
+ end
153
+
154
+ def render(content:String)
155
+ @response.setContentType("text/html; charset=utf-8")
156
+ @response.getWriter.write(with_layout(content))
157
+ end
158
+
159
+ def url
160
+ @url
161
+ end
162
+
163
+ def doGet(request, response)
164
+ @url = request.getRequestURI
165
+ @response = response
166
+ @error = String(nil)
167
+ @name = page_name(request.getPathInfo)
168
+ @page = Page.get(@name)
169
+ if @page && @page.locked
170
+ unless admin?
171
+ @error = "You are not authorized to edit this page"
172
+ response.setStatus(403)
173
+ render(error)
174
+ return
175
+ end
176
+ end
177
+ render(edit)
178
+ end
179
+
180
+ def doPost(_request, _response)
181
+ @error = nil
182
+ @response = _response
183
+ @url = _request.getRequestURI
184
+
185
+ # TODO scope inside blocks is not quite right
186
+ this = self
187
+ request = _request
188
+ response = _response
189
+ is_admin = admin?
190
+
191
+ name = page_name(request.getPathInfo)
192
+
193
+ begin
194
+ edit_version = Long.parseLong(String(request.getParameter("version")))
195
+ rescue NumberFormatException
196
+ response.sendError(
197
+ 400, "Invalid version '#{request.getParameter("version")}'")
198
+ return
199
+ end
200
+
201
+ Model.transaction do
202
+ orig_page = Page.get(name)
203
+ current_version = orig_page ? orig_page.version : long(0)
204
+ if current_version != edit_version
205
+ @error = <<EOS
206
+ Version conflict. You are trying to edit version #{edit_version}, but
207
+ the current version is #{current_version}.
208
+ EOS
209
+ @page = orig_page
210
+ @name = name
211
+ response.setStatus(409)
212
+ this.render(this.edit)
213
+ return
214
+ end
215
+ if orig_page && orig_page.locked
216
+ unless is_admin
217
+ @error = "You are not authorized to edit this page"
218
+ response.setStatus(403)
219
+ this.render(this.error)
220
+ return
221
+ end
222
+ end
223
+
224
+ this.save_old_version(orig_page) if orig_page
225
+
226
+ if orig_page
227
+ page = orig_page
228
+ page.version = page.version + 1
229
+ else
230
+ page = Page.new(name)
231
+ page.title = name
232
+ page.version = 1
233
+ end
234
+
235
+ locked = is_admin && "locked".equals(request.getParameter("locked"))
236
+ page.body = String(request.getParameter("body"))
237
+ page.nickname = String(request.getParameter("nickname"))
238
+ page.comment = String(request.getParameter("comment"))
239
+ page.user = this.user
240
+ page.userid = this.user.getUserId
241
+ page.created = Date.new
242
+ page.locked = locked
243
+ page.save
244
+ response.sendRedirect("/wiki/" + name)
245
+ end
246
+ end
247
+
248
+ def save_old_version(orig_page:Page)
249
+ old_version = Page.new(orig_page)
250
+ old_version.title = orig_page.title
251
+ old_version.body = orig_page.body
252
+ old_version.userid = orig_page.userid
253
+ old_version.user = orig_page.user
254
+ old_version.nickname = orig_page.nickname
255
+ old_version.comment = orig_page.comment
256
+ old_version.created = orig_page.created
257
+ old_version.version = orig_page.version
258
+ old_version.locked = orig_page.locked
259
+ old_version.save
260
+ end
261
+ end
262
+
263
+ import test.MirahParser
264
+ import jmeta.BaseParser
265
+ class MirahParserPage < Helper
266
+ def_edb(render, 'org/mirah/wiki/parser.eduby.html')
267
+
268
+ def initialize
269
+ self.title = "Mirah Parser Test"
270
+ end
271
+
272
+ def doGet(request, response)
273
+ doPost(request, response)
274
+ end
275
+
276
+ def doPost(request, response)
277
+ @code = String(request.getParameter("code")) || "puts 'Hello, world!'"
278
+ parser = MirahParser.new
279
+ begin
280
+ @parsed = BaseParser.print_r(parser.parse(@code))
281
+ rescue => ex
282
+ @parsed = ex.getMessage
283
+ end
284
+ response.getWriter.write(with_layout(render))
285
+ end
286
+ end
287
+
288
+ class FederatedLogin < Helper
289
+ def doGet(request, response)
290
+ provider = request.getPathInfo
291
+ if provider && provider.length > 1
292
+ provider = provider.substring(1)
293
+ else
294
+ provider = 'www.google.com/accounts/o8/id'
295
+ end
296
+ continue = String(request.getParameter('continue')) || '/'
297
+ url = users.createLoginURL(continue, nil, provider, nil)
298
+ response.sendRedirect(url)
299
+ end
300
+ end
301
+
302
+ class LoginRequired < Helper
303
+ def doGet(request, response)
304
+ # This should be a page allowing people to select an OpenID provider.
305
+ # But I'm lazy so I'll just force them to use gmail...
306
+ continue = String(request.getParameter('continue'))
307
+ url = users.createLoginURL(continue, nil, 'www.google.com/accounts/o8/id', nil)
308
+ response.sendRedirect(url)
309
+ end
310
+ end
311
+
312
+ class MainFilter; implements Filter
313
+ def init(arg); end
314
+ def destroy; end
315
+ def doFilter(request, response, filters)
316
+ if "/".equals(HttpServletRequest(request).getRequestURI)
317
+ request.getRequestDispatcher('/wiki/Main').forward(request, response)
318
+ nil
319
+ else
320
+ filters.doFilter(request, response)
321
+ nil
322
+ end
323
+ end
324
+ end
@@ -0,0 +1,42 @@
1
+ <div>
2
+ <% if @error %>
3
+ <div id=error><%=h @error %></div>
4
+ <% end %>
5
+ <div>
6
+ <form method=post>
7
+ <textarea id=body name=body style="width:95%;font-family:monospace" rows=5
8
+ ><% if @page && @page.body %><%=h @page.body %><% end
9
+ %></textarea><br>
10
+ Your Name: <input name=nickname value="<%=h nickname %>"><br>
11
+ Comment: <input name=comment value=<% if @page %>
12
+ "Edited Page"
13
+ <% else %>
14
+ "Created Page"
15
+ <% end %>><br>
16
+ <input type=hidden name=version value="<%= @page ? @page.version : long(0) %>">
17
+ <% if admin? %>
18
+ Locked: <input name=locked value=locked type=checkbox <%= "checked" if @page && @page.locked%>><br>
19
+ <% end %>
20
+ <input type=submit name=Save>
21
+ </form>
22
+ </div>
23
+ <hr style="clear:all">
24
+ <div id=preview class="wmd-preview"></div>
25
+ <script>
26
+ (function(){
27
+ var ta = document.getElementById("body");
28
+ var sh = 0;
29
+ function resize() {
30
+ var nsh = ta.scrollHeight;
31
+ if (nsh != sh) {
32
+ ta.style.height = 0;
33
+ ta.style.height = (nsh + 100) + "px";
34
+ sh = ta.scrollHeight;
35
+ }
36
+ setTimeout(resize, 250);
37
+ }
38
+ resize();
39
+ })();
40
+ wmd_options={output:"markdown"};
41
+ </script>
42
+ <script type="text/javascript" src="/wmd/wmd.js""></script>
@@ -0,0 +1,2 @@
1
+ <h1>error:</h1>
2
+ <p><%=h @error %></p>
@@ -0,0 +1,69 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
5
+ <title><%=h @title %></title>
6
+ <link type="text/css" rel="stylesheet" href="/stylesheets/main.css">
7
+ <link href="/stylesheets/prettify.css" type="text/css" rel="stylesheet" />
8
+ <script type="text/javascript" src="/javascripts/prettify.js"></script>
9
+ </head>
10
+ <body onload="prettyPrint()">
11
+ <div id="page">
12
+ <div id="sidebar">
13
+
14
+ <h1>Mirah</h1>
15
+
16
+ <ul id="sidebar-items">
17
+
18
+ <li>
19
+ <h3>Join the community</h3>
20
+ <ul class="links">
21
+ <li><a href="/">Home Page</a></li>
22
+ <li><a href="http://github.com/mirah/mirah">github</a></li>
23
+ <li><a href="/wiki/Resources">Resources</a></li>
24
+ <li><a href="/wiki/MirahHowto">Howto</a></li>
25
+ <li><a
26
+ href="http://groups.google.com/group/mirah/"
27
+ target="_new">Send Feedback</a></li>
28
+ </ul>
29
+ </li>
30
+
31
+ <li>
32
+ <h3>Sample Apps</h3>
33
+ <ul class="links">
34
+ <li><a href="http://github.com/mirah/mirah/tree/master/examples/appengine/">Guestbook</a> |
35
+ <a href="http://github.com/jacortinas/appengine-rails-jquery-demo">Mustache</a></li>
36
+ </ul>
37
+ <a href="/wiki/MirahSamples">Sample Code</a>
38
+ </li>
39
+
40
+ <li>
41
+ <h3>Mirah Videos</h3>
42
+ <ul class="links">
43
+ <li><a href="http://www.youtube.com/watch?v=08rNKxW0PPo">ribrdb</a> |
44
+ <a href="http://confreaks.net/videos/196-rubyconf2009-ruby-mutants">headius</a></li>
45
+ </ul>
46
+ </li>
47
+
48
+ </div>
49
+
50
+ <div id="content">
51
+ <div id=hlinks>
52
+ <% if user %>
53
+ <%=h user.getEmail %>&nbsp;|&nbsp;
54
+ <% extra_links.each do |_pair|;pair = List(_pair) %>
55
+ <a href="<%=h pair.get(1)%>"><%=h pair.get(0)%></a>&nbsp;|&nbsp;
56
+ <% end %>
57
+ <a href="<%=h users.createLogoutURL('/')%>">Sign Out</a>
58
+ <% else %>
59
+ <a href="<%=users.createLoginURL(url)%>">Sign In</a>
60
+ <% end %>
61
+ </div>
62
+ <div id=wikicontent>
63
+ <%= @content %>
64
+ </div>
65
+ </div>
66
+ </div>
67
+ </body>
68
+ </html>
69
+