raw 0.49.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 (148) hide show
  1. data/doc/CONTRIBUTORS +106 -0
  2. data/doc/LICENSE +32 -0
  3. data/doc/coding_conventions.txt +11 -0
  4. data/lib/raw.rb +42 -0
  5. data/lib/raw/adapter.rb +113 -0
  6. data/lib/raw/adapter/cgi.rb +41 -0
  7. data/lib/raw/adapter/fastcgi.rb +48 -0
  8. data/lib/raw/adapter/mongrel.rb +146 -0
  9. data/lib/raw/adapter/script.rb +94 -0
  10. data/lib/raw/adapter/webrick.rb +144 -0
  11. data/lib/raw/adapter/webrick/vcr.rb +91 -0
  12. data/lib/raw/cgi.rb +323 -0
  13. data/lib/raw/cgi/cookie.rb +47 -0
  14. data/lib/raw/cgi/http.rb +62 -0
  15. data/lib/raw/compiler.rb +138 -0
  16. data/lib/raw/compiler/filter/cleanup.rb +21 -0
  17. data/lib/raw/compiler/filter/elements.rb +166 -0
  18. data/lib/raw/compiler/filter/elements/element.rb +210 -0
  19. data/lib/raw/compiler/filter/localization.rb +23 -0
  20. data/lib/raw/compiler/filter/markup.rb +32 -0
  21. data/lib/raw/compiler/filter/morph.rb +123 -0
  22. data/lib/raw/compiler/filter/morph/each.rb +34 -0
  23. data/lib/raw/compiler/filter/morph/for.rb +11 -0
  24. data/lib/raw/compiler/filter/morph/if.rb +26 -0
  25. data/lib/raw/compiler/filter/morph/selected_if.rb +43 -0
  26. data/lib/raw/compiler/filter/morph/standard.rb +55 -0
  27. data/lib/raw/compiler/filter/morph/times.rb +27 -0
  28. data/lib/raw/compiler/filter/script.rb +116 -0
  29. data/lib/raw/compiler/filter/squeeze.rb +16 -0
  30. data/lib/raw/compiler/filter/static_include.rb +74 -0
  31. data/lib/raw/compiler/filter/template.rb +121 -0
  32. data/lib/raw/compiler/reloader.rb +96 -0
  33. data/lib/raw/context.rb +154 -0
  34. data/lib/raw/context/flash.rb +157 -0
  35. data/lib/raw/context/global.rb +88 -0
  36. data/lib/raw/context/request.rb +338 -0
  37. data/lib/raw/context/response.rb +57 -0
  38. data/lib/raw/context/session.rb +198 -0
  39. data/lib/raw/context/session/drb.rb +11 -0
  40. data/lib/raw/context/session/file.rb +15 -0
  41. data/lib/raw/context/session/memcached.rb +13 -0
  42. data/lib/raw/context/session/memory.rb +12 -0
  43. data/lib/raw/context/session/og.rb +15 -0
  44. data/lib/raw/context/session/pstore.rb +13 -0
  45. data/lib/raw/control.rb +18 -0
  46. data/lib/raw/control/attribute.rb +91 -0
  47. data/lib/raw/control/attribute/checkbox.rb +25 -0
  48. data/lib/raw/control/attribute/datetime.rb +21 -0
  49. data/lib/raw/control/attribute/file.rb +20 -0
  50. data/lib/raw/control/attribute/fixnum.rb +26 -0
  51. data/lib/raw/control/attribute/float.rb +26 -0
  52. data/lib/raw/control/attribute/options.rb +38 -0
  53. data/lib/raw/control/attribute/password.rb +16 -0
  54. data/lib/raw/control/attribute/text.rb +16 -0
  55. data/lib/raw/control/attribute/textarea.rb +16 -0
  56. data/lib/raw/control/none.rb +16 -0
  57. data/lib/raw/control/relation.rb +59 -0
  58. data/lib/raw/control/relation/belongs_to.rb +0 -0
  59. data/lib/raw/control/relation/has_many.rb +97 -0
  60. data/lib/raw/control/relation/joins_many.rb +0 -0
  61. data/lib/raw/control/relation/many_to_many.rb +0 -0
  62. data/lib/raw/control/relation/refers_to.rb +29 -0
  63. data/lib/raw/controller.rb +37 -0
  64. data/lib/raw/controller/publishable.rb +160 -0
  65. data/lib/raw/dispatcher.rb +209 -0
  66. data/lib/raw/dispatcher/format.rb +108 -0
  67. data/lib/raw/dispatcher/format/atom.rb +31 -0
  68. data/lib/raw/dispatcher/format/css.rb +0 -0
  69. data/lib/raw/dispatcher/format/html.rb +42 -0
  70. data/lib/raw/dispatcher/format/json.rb +31 -0
  71. data/lib/raw/dispatcher/format/rss.rb +33 -0
  72. data/lib/raw/dispatcher/format/xoxo.rb +31 -0
  73. data/lib/raw/dispatcher/mounter.rb +60 -0
  74. data/lib/raw/dispatcher/router.rb +111 -0
  75. data/lib/raw/errors.rb +19 -0
  76. data/lib/raw/helper.rb +86 -0
  77. data/lib/raw/helper/benchmark.rb +23 -0
  78. data/lib/raw/helper/buffer.rb +60 -0
  79. data/lib/raw/helper/cookie.rb +32 -0
  80. data/lib/raw/helper/debug.rb +28 -0
  81. data/lib/raw/helper/default.rb +16 -0
  82. data/lib/raw/helper/feed.rb +451 -0
  83. data/lib/raw/helper/form.rb +284 -0
  84. data/lib/raw/helper/javascript.rb +59 -0
  85. data/lib/raw/helper/layout.rb +40 -0
  86. data/lib/raw/helper/navigation.rb +87 -0
  87. data/lib/raw/helper/pager.rb +305 -0
  88. data/lib/raw/helper/table.rb +247 -0
  89. data/lib/raw/helper/xhtml.rb +218 -0
  90. data/lib/raw/helper/xml.rb +125 -0
  91. data/lib/raw/mixin/magick.rb +35 -0
  92. data/lib/raw/mixin/sweeper.rb +71 -0
  93. data/lib/raw/mixin/thumbnails.rb +1 -0
  94. data/lib/raw/mixin/webfile.rb +165 -0
  95. data/lib/raw/render.rb +271 -0
  96. data/lib/raw/render/builder.rb +26 -0
  97. data/lib/raw/render/caching.rb +81 -0
  98. data/lib/raw/render/call.rb +43 -0
  99. data/lib/raw/render/send_file.rb +46 -0
  100. data/lib/raw/render/stream.rb +39 -0
  101. data/lib/raw/scaffold.rb +13 -0
  102. data/lib/raw/scaffold/controller.rb +25 -0
  103. data/lib/raw/scaffold/model.rb +157 -0
  104. data/lib/raw/test.rb +5 -0
  105. data/lib/raw/test/assertions.rb +169 -0
  106. data/lib/raw/test/context.rb +55 -0
  107. data/lib/raw/test/testcase.rb +79 -0
  108. data/lib/raw/util/attr.rb +128 -0
  109. data/lib/raw/util/encode_uri.rb +149 -0
  110. data/lib/raw/util/html_filter.rb +538 -0
  111. data/lib/raw/util/markup.rb +130 -0
  112. data/test/glue/tc_webfile.rb +1 -0
  113. data/test/nitro/CONFIG.rb +3 -0
  114. data/test/nitro/adapter/raw_post1.bin +9 -0
  115. data/test/nitro/adapter/tc_webrick.rb +16 -0
  116. data/test/nitro/cgi/tc_cookie.rb +14 -0
  117. data/test/nitro/cgi/tc_request.rb +61 -0
  118. data/test/nitro/compiler/tc_client_morpher.rb +47 -0
  119. data/test/nitro/compiler/tc_compiler.rb +25 -0
  120. data/test/nitro/dispatcher/tc_mounter.rb +47 -0
  121. data/test/nitro/helper/tc_feed.rb +135 -0
  122. data/test/nitro/helper/tc_navbar.rb +74 -0
  123. data/test/nitro/helper/tc_pager.rb +35 -0
  124. data/test/nitro/helper/tc_table.rb +68 -0
  125. data/test/nitro/helper/tc_xhtml.rb +19 -0
  126. data/test/nitro/tc_caching.rb +19 -0
  127. data/test/nitro/tc_cgi.rb +222 -0
  128. data/test/nitro/tc_context.rb +17 -0
  129. data/test/nitro/tc_controller.rb +103 -0
  130. data/test/nitro/tc_controller_aspect.rb +32 -0
  131. data/test/nitro/tc_controller_params.rb +885 -0
  132. data/test/nitro/tc_dispatcher.rb +109 -0
  133. data/test/nitro/tc_element.rb +85 -0
  134. data/test/nitro/tc_flash.rb +59 -0
  135. data/test/nitro/tc_helper.rb +47 -0
  136. data/test/nitro/tc_render.rb +119 -0
  137. data/test/nitro/tc_router.rb +61 -0
  138. data/test/nitro/tc_server.rb +35 -0
  139. data/test/nitro/tc_session.rb +66 -0
  140. data/test/nitro/tc_template.rb +71 -0
  141. data/test/nitro/util/tc_encode_url.rb +87 -0
  142. data/test/nitro/util/tc_markup.rb +31 -0
  143. data/test/public/blog/another/very_litle/index.xhtml +1 -0
  144. data/test/public/blog/inc1.xhtml +2 -0
  145. data/test/public/blog/inc2.xhtml +1 -0
  146. data/test/public/blog/list.xhtml +9 -0
  147. data/test/public/dummy_mailer/registration.xhtml +5 -0
  148. metadata +244 -0
@@ -0,0 +1,284 @@
1
+ require "glue/builder/xml"
2
+
3
+ require 'raw/control'
4
+ require 'raw/control/none'
5
+
6
+ require 'raw/control/attribute/fixnum'
7
+ require 'raw/control/attribute/float'
8
+ require 'raw/control/attribute/text'
9
+ require 'raw/control/attribute/password'
10
+ require 'raw/control/attribute/textarea'
11
+ require 'raw/control/attribute/checkbox'
12
+ require 'raw/control/attribute/options'
13
+ require 'raw/control/attribute/file'
14
+
15
+ require 'raw/control/relation/refers_to'
16
+ require 'raw/control/relation/has_many'
17
+
18
+ module Raw
19
+
20
+ module FormHelper
21
+
22
+ # A specialized Builder for dynamically building of forms.
23
+ # Provides extra support for forms backed by managed objects
24
+ # (entities).
25
+ #--
26
+ # TODO: allow multiple objects per form.
27
+ # TODO: use more generalized controls.
28
+ #++
29
+
30
+ class FormXmlBuilder < ::Glue::XmlBuilder
31
+
32
+ # Mappings of control names to controls.
33
+
34
+ setting :control_map, :doc => 'Mappings of control names to controls', :default => {
35
+ :fixnum => FixnumControl,
36
+ :integer => FixnumControl,
37
+ :float => FloatControl,
38
+ :true_class => CheckboxControl,
39
+ :boolean => CheckboxControl,
40
+ :checkbox => CheckboxControl,
41
+ :string => TextControl,
42
+ :password => PasswordControl,
43
+ :textarea => TextareaControl,
44
+ :file => FileControl,
45
+ :webfile => FileControl,
46
+ =begin
47
+ :array => ArrayControl,
48
+ =end
49
+ :options => OptionsControl,
50
+ :refers_to => RefersToControl,
51
+ :has_one => RefersToControl,
52
+ :belongs_to => RefersToControl,
53
+ :has_many => HasManyControl,
54
+ :many_to_many => HasManyControl,
55
+ :joins_many => HasManyControl
56
+ }
57
+
58
+ # Returns a control for the given objects attribute.
59
+
60
+ def self.control_for(obj, a, anno, options)
61
+ raise "Invalid attribute '#{a}' for object '#{obj}'" if anno.nil?
62
+ name = anno[:control] || anno[:class].to_s.demodulize.underscore.to_sym
63
+ control_class = self.control_map.fetch(name, NoneControl)
64
+ return control_class.new(obj, a, options)
65
+ end
66
+
67
+ # Returns a control for the given objects relation.
68
+
69
+ def self.control_for_relation(obj, rel, options)
70
+ name = rel[:control] || rel.class.to_s.demodulize.underscore.to_sym
71
+ control_class = self.control_map.fetch(name, NoneControl)
72
+ return control_class.new(obj, rel, options)
73
+ end
74
+
75
+ def initialize(buffer = '', options = {})
76
+ super
77
+ @obj = options[:object]
78
+ @errors = options[:errors]
79
+ end
80
+
81
+ # Render a control+label for the given property of the form
82
+ # object.
83
+
84
+ def attribute(a, options = {})
85
+ if anno = @obj.class.ann(a)
86
+ control = self.class.control_for(@obj, a, anno, options)
87
+ print element(a, anno, control.render)
88
+ else
89
+ raise "Undefined attribute '#{a}' for class '#{@obj.class}'."
90
+ end
91
+ end
92
+ alias_method :attr, :attribute
93
+
94
+ # Render controls for all attributes of the form object.
95
+ # It only considers serializable attributes.
96
+
97
+ def all_attributes(options = {})
98
+ for a in @obj.class.serializable_attributes
99
+ prop = @obj.class.ann(a)
100
+ unless options[:all]
101
+ next if a == @obj.class.primary_key or prop[:control] == :none or prop[:relation] or [options[:exclude]].flatten.include?(a)
102
+ end
103
+ attribute a, options
104
+ end
105
+ end
106
+ alias_method :attributes, :all_attributes
107
+ alias_method :serializable_attributes, :all_attributes
108
+
109
+ # === Input
110
+ #
111
+ # * rel = The relation name as symbol, or the actual
112
+ # relation object.
113
+ #--
114
+ # FIXME: Fix the mismatch with the attributes.
115
+ #++
116
+
117
+ def relation(rel, options = {})
118
+ # If the relation name is passed, lookup the actual
119
+ # relation.
120
+
121
+ if rel.is_a? Symbol
122
+ rel = @obj.class.relation(rel)
123
+ end
124
+
125
+ control = self.class.control_for_relation(@obj, rel, options)
126
+ print element(rel[:symbol], rel, control.render)
127
+ end
128
+ alias_method :rel, :relation
129
+
130
+ # Render controls for all relations of the form object.
131
+
132
+ def all_relations(options = {})
133
+ for rel in @obj.class.relations
134
+ unless options[:all]
135
+ # Ignore polymorphic_marker relations.
136
+ #--
137
+ # gmosx: should revisit the handling of polymorphic
138
+ # relations, feels hacky.
139
+ #++
140
+ next if (rel[:control] == :none) or rel.polymorphic_marker?
141
+ end
142
+ relation rel, options
143
+ end
144
+ end
145
+ alias_method :relations, :all_relations
146
+
147
+ # Renders a control to select a file for upload.
148
+
149
+ def select_file(name, options = {})
150
+ print %|<input type="file" name="#{name}" />|
151
+ end
152
+
153
+ # If flash[:ERRORS] is filled with errors structured as
154
+ # name/message pairs the method creates a div containing them,
155
+ # otherwise it returns an empty string.
156
+ #
157
+ # So you can write code like
158
+ # #{form_errors}
159
+ # <form>... </form>
160
+ #
161
+ # and redirect the user to the form in case of errors, thus
162
+ # allowing him to see what was wrong.
163
+
164
+ def form_errors
165
+ res = ''
166
+
167
+ unless @errors.empty?
168
+ res << %{<div class="error">\n<ul>\n}
169
+ for err in @errors
170
+ if err.is_a? Array
171
+ res << "<li><strong>#{err[0].to_s.humanize}</strong>: #{err[1]}</li>\n"
172
+ else
173
+ res << "<li>#{err}</li>\n"
174
+ end
175
+ end
176
+ res << %{</ul>\n</div>\n}
177
+ end
178
+
179
+ print(res)
180
+ end
181
+
182
+ private
183
+
184
+ # Emit a form element. Override this method to customize the
185
+ # rendering for your application needs.
186
+
187
+ def element(a, anno, html)
188
+ # TODO: give better form id!
189
+ %{
190
+ <p id="form_#{a}">
191
+ #{html}
192
+ </p>
193
+ }
194
+ end
195
+
196
+ end
197
+
198
+ private
199
+
200
+ # A sophisticated form generation helper method.
201
+ # If no block is provided, render all attributes.
202
+ #
203
+ # === Options
204
+ #
205
+ # * :object, :entity, :class = The object that acts as model
206
+ # for this form. If you pass a class an empty object is
207
+ # instantiated.
208
+ #
209
+ # * :action = The action of this form. The parameter is
210
+ # passed through the R operator (encode_uri) to support
211
+ # advanced uri transformation.
212
+ #
213
+ # * :errors = An optional collection of errors.
214
+ #
215
+ # === Example
216
+ #
217
+ # #{form(:object => @owner, :action => :save_profile) do |f|
218
+ # f.property :name, :editable => false
219
+ # f.property :password
220
+ # f.br
221
+ # f.submit 'Update'
222
+ # end}
223
+
224
+ def form(options = {}, &block)
225
+ obj = (options[:object] ||= options[:entity] || options[:class])
226
+
227
+ # If the passed obj is a Class instantiate an empty object
228
+ # of this class.
229
+
230
+ if obj.is_a? Class
231
+ obj = options[:object] = obj.allocate
232
+ end
233
+
234
+ # Convert virtual :multipart method to method="post",
235
+ # enctype="multipart/form-data"
236
+
237
+ if options[:method] == :multipart
238
+ options[:method] = 'POST'
239
+ options[:enctype] = 'multipart/form-data'
240
+ end
241
+
242
+ options[:errors] ||= []
243
+
244
+ if errors = flash[:ERRORS]
245
+ if errors.is_a? Array
246
+ options[:errors].concat(errors)
247
+ elsif errors.is_a? Glue::Validation::Errors
248
+ options[:errors].concat(errors.to_a)
249
+ else
250
+ options[:errors] << errors
251
+ end
252
+ end
253
+
254
+ if obj and errors = obj.errors
255
+ options[:errors].concat(errors.to_a)
256
+ end
257
+
258
+ b = FormXmlBuilder.new('', options)
259
+
260
+ b << '<form'
261
+ b << %| action="#{R options[:action]}"| if options[:action]
262
+ b << %| method="#{options[:method]}"| if options[:method]
263
+ b << %| accept-charset="#{options[:charset]}"| if options[:charset]
264
+ b << %| enctype="#{options[:enctype]}"| if options[:enctype]
265
+ b << '>'
266
+
267
+ b.hidden(:oid, obj.oid) if obj and obj.saved?
268
+
269
+ # If no block is provided, render all attributes.
270
+
271
+ if block_given?
272
+ yield b
273
+ else
274
+ b.all_attributes
275
+ end
276
+
277
+ b << '</form>'
278
+
279
+ return b
280
+ end
281
+
282
+ end
283
+
284
+ end
@@ -0,0 +1,59 @@
1
+ require "facets/core/inflect"
2
+
3
+ module Raw
4
+
5
+ # Javascript code manager.
6
+ #--
7
+ # TODO: Add support for synthesizing compound Javascript files from
8
+ # multiple smaller files.
9
+ #++
10
+
11
+ module Javascript
12
+
13
+ # The javascript files to auto include.
14
+
15
+ setting :required_files, :default => [], :doc => "The javascript files to auto include"
16
+
17
+ # The root directory where javascript files reside.
18
+
19
+ setting :root_dir, :default => "public/js", :doc => "The root directory where javascript files reside"
20
+
21
+ def self.require(path)
22
+ end
23
+
24
+ end
25
+
26
+ # Javascript utilities.
27
+
28
+ module JavascriptUtils
29
+
30
+ private
31
+ # Escape carrier returns and single and double quotes for JavaScript segments.
32
+
33
+ def escape_javascript(js)
34
+ (js || '').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }
35
+ end
36
+ alias_method :escape, :escape_javascript
37
+
38
+ # Converts a Ruby hash to a Javascript hash.
39
+
40
+ def hash_to_js(options)
41
+ '{' + options.map {|k, v| "#{k}:#{v}"}.join(', ') + '}'
42
+ end
43
+
44
+ # Converts the name of a javascript file to the actual
45
+ # filename. Override if you don't like the defaults.
46
+
47
+ def name_to_jsfile(name)
48
+ "/js/#{name}.js"
49
+ end
50
+
51
+ # Generate javascript confirm code for links.
52
+
53
+ def confirm(text = 'Are you sure?')
54
+ %|onclick="return confirm('#{text}')"|
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,40 @@
1
+ require "facets/core/inflect"
2
+
3
+ require "raw/element"
4
+
5
+ module Raw
6
+
7
+ # This helper uses Nitro's powerfull Elements mechanism to
8
+ # implement a simple Rails style Layout helper. Perhaps this
9
+ # may be useful for people coming over from Rails.
10
+ #
11
+ # WARNING: This is not enabled by default. You have to insert
12
+ # the LayoutCompiler before the ElementsCompiler for layout to
13
+ # work.
14
+
15
+ module LayoutHelper
16
+
17
+ def self.included(base)
18
+ base.module_eval do
19
+ # Enclose all templates of this controller with the
20
+ # given element.
21
+
22
+ def self.layout(name = nil)
23
+ klass = name.to_s.camelize
24
+
25
+ unless klass
26
+ if defined? 'Nitro::Element::Layout'
27
+ klass = Nitro::Element::Layout
28
+ end
29
+ end
30
+
31
+ if klass
32
+ ann :self, :layout => klass
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,87 @@
1
+ module Raw
2
+
3
+ # A helper mixin for programmatically building Navigation Menus
4
+ # through easy to style XHTML.
5
+ # The code resulting from these methods is in line with that found in most examples at
6
+ # listamatic, thus you can use prebuilt CSS and Javascript to create horizontal or vertical menus.
7
+ # Basically it generates something like
8
+ # <div id="navcontainer">
9
+ # <ul id="navlist">
10
+ # <li id="active"> <a href="/foo" id="current"> Current Page </a></li>
11
+ # <li> <a href="/bar"> Other page </a> </li>
12
+ # </ul>
13
+ # </div>
14
+ #
15
+ # This helper takes care of setting of putting the special CSS
16
+ # identifiers for the current controller automatically.
17
+ # You could override menuitem_active_on(path)
18
+ # to change the behaviour that choose the active item,
19
+ # for example to keep the item "Wiki" active both for
20
+ # /wiki/pageone and /wiki/pagetwo
21
+ #
22
+ # Example of horizontal bar at listamatic:
23
+ # http://css.maxdesign.com.au/listamatic/horizontal26.htm
24
+ # Vertical example
25
+ # http://css.maxdesign.com.au/listamatic/vertical09.htm
26
+ #
27
+ # NOTE: No tests were made with Publishable objects which are not
28
+ # subclass of Nitro::Controller, but it _should_ work.
29
+
30
+ module NavigationHelper
31
+
32
+ TEMPLATE=<<Eof
33
+ <div id="navcontainer">
34
+ <ul id="navlist">
35
+ LIST
36
+ </ul>
37
+ </div>
38
+ Eof
39
+
40
+ # Takes a list of controllers and builds a menu
41
+ # using #mount_path as the uri and the controller name as text.
42
+ # An eventual "Controller" suffix will be stripped, so i.e. for controllers
43
+ # named +HomeController+, +Pages+, +FeedCtl+ it will use
44
+ # +Home+, +Page+, +FeedCtl+.
45
+ #
46
+ # For more finegrained control you can pass a block to this function, each
47
+ # controller will be passed to it and the result will be used as the text
48
+ # for the menu item.
49
+ #
50
+ #
51
+ # Otherwise you can specify pairs of path/text using #navigation_for_hash
52
+
53
+ def menu_for(*controllers) #:yields:
54
+ hash= {}
55
+ controllers.each do |c|
56
+ hash[c.mount_path] = block_given? ? yield(c) : c.name.gsub(/Controller/,'')
57
+ end
58
+ menu_from_hash(hash)
59
+ end
60
+
61
+ # The argument must be an hash of pairs {'path'=>'text for menu item'},
62
+ # no control will be applied on these values, they will be used directly.
63
+ # You can use the method like
64
+ # navigation_for_hash '/foo/bar'=>'Page One', '/foo/baz'=>'Page Two'
65
+ #
66
+ # The method takes care of setting the CSS values as expected.
67
+ #
68
+ # To avoid specifying everything the method #navigation_menu can be used.
69
+
70
+ def menu_from_hash(hash)
71
+ list=hash.map do |path,name|
72
+ if menuitem_active_on?(path)
73
+ %{<li id="active"><a href="#{path}" id="current"> #{name} </a></li>}
74
+ else
75
+ %{<li><a href="#{path}"> #{name} </a></li>}
76
+ end
77
+ end.join("\n")
78
+ TEMPLATE.gsub("LIST",list)
79
+ end
80
+
81
+ def menuitem_active_on?(path)
82
+ path == request.path
83
+ end
84
+
85
+ end
86
+
87
+ end