nitro 0.29.0 → 0.30.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/CHANGELOG +410 -0
  2. data/ProjectInfo +36 -44
  3. data/README +5 -5
  4. data/doc/AUTHORS +6 -0
  5. data/doc/RELEASES +159 -2
  6. data/lib/glue/sweeper.rb +2 -2
  7. data/lib/glue/webfile.rb +14 -1
  8. data/lib/nitro.rb +6 -9
  9. data/lib/nitro/adapter/mongrel.rb +36 -43
  10. data/lib/nitro/adapter/scgi.rb +1 -1
  11. data/lib/nitro/adapter/webrick.rb +96 -24
  12. data/lib/nitro/caching/actions.rb +2 -1
  13. data/lib/nitro/caching/fragments.rb +1 -8
  14. data/lib/nitro/caching/output.rb +14 -4
  15. data/lib/nitro/cgi.rb +19 -21
  16. data/lib/nitro/cgi/cookie.rb +5 -1
  17. data/lib/nitro/cgi/request.rb +20 -4
  18. data/lib/nitro/compiler.rb +74 -28
  19. data/lib/nitro/compiler/cleanup.rb +1 -1
  20. data/lib/nitro/compiler/elements.rb +1 -2
  21. data/lib/nitro/compiler/localization.rb +1 -1
  22. data/lib/nitro/compiler/markup.rb +1 -1
  23. data/lib/nitro/compiler/script.rb +52 -44
  24. data/lib/nitro/compiler/squeeze.rb +4 -3
  25. data/lib/nitro/compiler/xslt.rb +7 -6
  26. data/lib/nitro/context.rb +39 -20
  27. data/lib/nitro/controller.rb +24 -5
  28. data/lib/nitro/dispatcher.rb +13 -5
  29. data/lib/nitro/global.rb +63 -0
  30. data/lib/nitro/helper/feed.rb +432 -0
  31. data/lib/nitro/helper/form.rb +11 -3
  32. data/lib/nitro/helper/form/builder.rb +140 -0
  33. data/lib/nitro/helper/form/controls.rb +2 -1
  34. data/lib/nitro/helper/javascript.rb +6 -0
  35. data/lib/nitro/helper/javascript/morphing.rb +13 -6
  36. data/lib/nitro/helper/xhtml.rb +42 -6
  37. data/lib/nitro/helper/xml.rb +3 -0
  38. data/lib/nitro/part.rb +2 -2
  39. data/lib/nitro/render.rb +7 -2
  40. data/lib/nitro/router.rb +57 -16
  41. data/lib/nitro/scaffolding.rb +29 -20
  42. data/lib/nitro/server.rb +4 -10
  43. data/lib/nitro/server/drb.rb +1 -1
  44. data/lib/nitro/server/runner.rb +10 -0
  45. data/lib/nitro/session.rb +31 -12
  46. data/lib/nitro/session/drb.rb +13 -1
  47. data/lib/nitro/session/file.rb +1 -1
  48. data/lib/nitro/session/memcached.rb +1 -1
  49. data/lib/nitro/session/memory.rb +1 -1
  50. data/lib/nitro/session/og.rb +1 -1
  51. data/lib/nitro/test/testcase.rb +3 -0
  52. data/proto/public/error.xhtml +5 -5
  53. data/proto/public/js/controls.js +2 -2
  54. data/proto/public/js/dragdrop.js +320 -79
  55. data/proto/public/js/effects.js +200 -152
  56. data/proto/public/js/prototype.js +284 -63
  57. data/proto/public/js/scriptaculous.js +7 -5
  58. data/proto/public/js/unittest.js +11 -0
  59. data/proto/public/scaffold/advanced_search.xhtml +30 -0
  60. data/proto/public/scaffold/list.xhtml +8 -1
  61. data/proto/public/scaffold/search.xhtml +2 -1
  62. data/proto/script/scgi_service +1 -1
  63. data/src/part/admin/controller.rb +1 -1
  64. data/src/part/admin/skin.rb +1 -1
  65. data/test/nitro/CONFIG.rb +3 -0
  66. data/test/nitro/adapter/tc_webrick.rb +1 -1
  67. data/test/nitro/cgi/tc_cookie.rb +1 -1
  68. data/test/nitro/cgi/tc_request.rb +5 -5
  69. data/test/nitro/compiler/tc_client_morpher.rb +47 -0
  70. data/test/nitro/compiler/tc_compiler.rb +2 -0
  71. data/test/nitro/helper/tc_feed.rb +138 -0
  72. data/test/nitro/helper/tc_pager.rb +1 -1
  73. data/test/nitro/helper/tc_rss.rb +1 -1
  74. data/test/nitro/helper/tc_table.rb +1 -1
  75. data/test/nitro/helper/tc_xhtml.rb +1 -1
  76. data/test/nitro/tc_caching.rb +1 -1
  77. data/test/nitro/tc_cgi.rb +1 -1
  78. data/test/nitro/tc_context.rb +1 -1
  79. data/test/nitro/tc_controller.rb +31 -3
  80. data/test/nitro/tc_controller_aspect.rb +1 -1
  81. data/test/nitro/tc_dispatcher.rb +1 -1
  82. data/test/nitro/tc_element.rb +1 -1
  83. data/test/nitro/tc_flash.rb +1 -1
  84. data/test/nitro/tc_helper.rb +1 -1
  85. data/test/nitro/tc_render.rb +6 -6
  86. data/test/nitro/tc_router.rb +8 -4
  87. data/test/nitro/tc_server.rb +1 -3
  88. data/test/nitro/tc_session.rb +1 -3
  89. metadata +107 -104
  90. data/Rakefile +0 -232
  91. data/lib/nitro/adapter/acgi.rb +0 -237
  92. data/proto/public/Makefile.acgi +0 -40
  93. data/proto/public/acgi.c +0 -138
@@ -8,7 +8,7 @@ module Nitro
8
8
 
9
9
  module ScriptCompiler
10
10
  extend JavascriptHelper
11
-
11
+
12
12
  def self.transform(text, compiler)
13
13
  begin
14
14
  client = constant("#{compiler.controller}::Client")
@@ -21,9 +21,11 @@ module ScriptCompiler
21
21
  client = client.new
22
22
 
23
23
  functions = {}
24
+ params = {}
24
25
 
25
- text.scan(/__nc_(.*)\(\)/) do |match|
26
+ text.scan(/__nc_(.*)\((.*)\)/) do |match|
26
27
  functions[match.first] = true
28
+ params[match.last] = true
27
29
  end
28
30
 
29
31
  script = ''
@@ -38,13 +40,13 @@ module ScriptCompiler
38
40
 
39
41
  function_code = client.buffer
40
42
 
41
- # if the client action returs a String append it to
43
+ # if the client action returns a String append it to
42
44
  # the function code.
43
45
 
44
46
  function_code << ret if ret.is_a?(String)
45
47
 
46
48
  script << %{
47
- function __nc_#{fun}() {
49
+ function __nc_#{fun}(params) {
48
50
  #{function_code}
49
51
  }
50
52
  }
@@ -53,51 +55,57 @@ module ScriptCompiler
53
55
  end
54
56
  end
55
57
 
56
- # Inject css in the head tag.
57
-
58
- if css_buffer = compiler.shared[:css_buffer]
59
- text.sub!(/<\/head>/) do |match|
60
- %{
61
- <style>
62
- #{css_buffer}
63
- </style>
64
- </head>
65
- }
66
- end
58
+ end
59
+
60
+ # Inject css in the head tag.
61
+
62
+ if css_buffer = compiler.shared[:css_buffer]
63
+ text.sub!(/<\/head>/) do |match|
64
+ %{
65
+ <style>
66
+ #{css_buffer}
67
+ </style>
68
+ </head>
69
+ }
67
70
  end
71
+ end
68
72
 
69
- # Inject required javascript files in the head tag.
70
-
71
- if required_files = compiler.shared[:js_required_files]
72
- text.sub!(/<\/head>/) do |match|
73
- %{
74
- #{include_script *required_files.keys}
75
- </head>
76
- }
77
- end
73
+ # Inject required javascript files in the head tag.
74
+
75
+ required_files = Javascript.required_files
76
+ if crf = compiler.shared[:js_required_files]
77
+ required_files.concat(crf.keys)
78
+ end
79
+
80
+ unless required_files.empty?
81
+ text.sub!(/<\/head>/) do |match|
82
+ %{
83
+ #{include_script *required_files}
84
+ </head>
85
+ }
78
86
  end
79
-
80
- # Inject javascript just before the end of the
81
- # template.
82
- #--
83
- # gmosx: injection happens at the end, so that the DOM
84
- # tree is created. Dunno if this is valid xhtml though.
85
- #++
87
+ end
88
+
89
+ # Inject javascript just before the end of the
90
+ # template.
91
+ #--
92
+ # gmosx: injection happens at the end, so that the DOM
93
+ # tree is created. Dunno if this is valid xhtml though.
94
+ #++
86
95
 
87
- js_buffer = compiler.shared[:js_buffer]
88
-
89
- if script or js_buffer
90
- text.sub!(/<\/body>/) do |match|
91
- %{
92
- <script type="text/javascript">
93
- #{script}
94
- #{js_buffer}
95
- </script>
96
- </body>
97
- }
98
- end
96
+ js_buffer = compiler.shared[:js_buffer]
97
+
98
+ if script or js_buffer
99
+ text.sub!(/<\/body>/) do |match|
100
+ %{
101
+ <script type="text/javascript">
102
+ #{script}
103
+ #{js_buffer}
104
+ </script>
105
+ </body>
106
+ }
99
107
  end
100
- end
108
+ end
101
109
 
102
110
  return text
103
111
  end
@@ -1,12 +1,13 @@
1
1
  module Nitro
2
-
3
- # Compress the inline xhtml. Does not touch the ruby code.
2
+
3
+ # A compiler transformation pipeline stage that compresses
4
+ # the generated xhtml code.
4
5
 
5
6
  class Squeeze
6
7
 
7
8
  # Compresses the inline xhtml. Does not touch the ruby code.
8
9
 
9
- def self.transform(text)
10
+ def self.transform(text, compiler = nil)
10
11
  return text.gsub(/\@out \<\< \%\^(.*?)\^/m) do |match|
11
12
  c = $1.gsub(/^(\s*)/m, '').squeeze(" \t").tr("\n", '').tr("\t", ' ')
12
13
  "@out << %{#{c}}"
@@ -4,10 +4,12 @@ require 'xml/xslt'
4
4
 
5
5
  module Nitro
6
6
 
7
- # Apply XSL transformation.
7
+ # Pre-applies an XSL transformation to the template file to
8
+ # generate the final template file. Due to preproccessing, the
9
+ # transformation comes for free.
8
10
 
9
11
  class XSLTransform
10
- @@sync = Sync.new
12
+ @@sync = Sync.new
11
13
 
12
14
  class << self
13
15
 
@@ -34,8 +36,7 @@ class XSLTransform
34
36
 
35
37
  # Transform the given xml.
36
38
 
37
- def transform(text)
38
-
39
+ def transform(text, compiler = nil)
39
40
  parse_xsl()
40
41
  @xslt.xml = text
41
42
  hash += @name
@@ -43,8 +44,8 @@ class XSLTransform
43
44
  process_next(hash, xslt.serve)
44
45
  end
45
46
 
46
- private
47
-
47
+ private
48
+
48
49
  # Parse the xsl.
49
50
 
50
51
  def parse_xsl
@@ -2,10 +2,13 @@ require 'forwardable'
2
2
 
3
3
  require 'facet/kernel/assign_with'
4
4
 
5
+ require 'glue/cache/memory'
6
+
5
7
  require 'nitro/cgi'
6
8
  require 'nitro/cgi/request'
7
9
  require 'nitro/cgi/response'
8
10
  require 'nitro/render'
11
+ require 'nitro/global'
9
12
  require 'nitro/session'
10
13
 
11
14
  module Nitro
@@ -16,17 +19,14 @@ module Nitro
16
19
  #
17
20
  # The Context object can be accessed by the context, request or
18
21
  # response aliases. You can use the alias that makes sense
19
- # every time.
22
+ # every time. This means inside an action request, response and
23
+ # context all point to the same object.
20
24
 
21
25
  class Context
22
26
  include Request
23
27
  include Response
24
28
  include Render
25
29
 
26
- # The cache/store used to back global variables.
27
-
28
- setting :global_cache_class, :default => ($NITRO_GLOBAL_CACHE_CLASS || MemoryCache), :doc => 'The store used to back global variables'
29
-
30
30
  # The configuration parameters.
31
31
 
32
32
  attr_accessor :conf
@@ -42,7 +42,15 @@ class Context
42
42
 
43
43
  attr_accessor :dispatcher
44
44
 
45
+ # The rendering level. An action may include sub-actions,
46
+ # each time the action is called, the level is increased,
47
+ # when the action returns the level decreases. The first
48
+ # action, called top level action has a level of 1.
49
+
50
+ attr_accessor :level
51
+
45
52
  def initialize(conf)
53
+ @level = 0
46
54
  @conf = conf
47
55
  @dispatcher = @conf.dispatcher
48
56
  @context = self
@@ -57,6 +65,17 @@ class Context
57
65
  @out ||= OutputBuffer.new
58
66
  end
59
67
 
68
+
69
+ # Don't sync session. This method may be needed in low level
70
+ # hacks with the session code. For example if you updade an
71
+ # entity (managed) object that is cached in the session, you
72
+ # may dissable the syncing to avoid resetting the old value
73
+ # after the sync.
74
+
75
+ def no_sync!
76
+ @no_sync = true
77
+ end
78
+
60
79
  # Close the context, should be called at the
61
80
  # end of the HTTP request handling code.
62
81
 
@@ -67,12 +86,14 @@ class Context
67
86
  @session[:FLASH].clean
68
87
  end
69
88
 
70
- # INVESTIGATE: is this needed?
71
- @session.sync
89
+ # INVESTIGATE: is this needed?
90
+ @session.sync unless @no_sync
72
91
  end
73
92
  end
74
93
  alias_method :finish, :close
75
94
 
95
+ # The accomulated output for the current context (ie the
96
+ # current request).
76
97
  #--
77
98
  # FIXME: still something more elegant/efficient.
78
99
  #++
@@ -97,7 +118,7 @@ class Context
97
118
  # these variables can reside outside of the process.
98
119
 
99
120
  def global
100
- return $global
121
+ return Global
101
122
  end
102
123
  alias_method :application, :global
103
124
 
@@ -132,18 +153,16 @@ class Context
132
153
  alias_method :populate, :fill
133
154
  alias_method :assign, :fill
134
155
 
135
- end
136
-
137
- # Forwards to the context
138
- #
139
- # use in Nitro::Action
140
-
141
- module ContextHelper
142
- extend Forwardable
143
-
144
- attr_accessor :context
145
-
146
- def_delegators :@context, :controller, :request, :response, :session, :out
156
+ # Is the current action the top level action? The level
157
+ # of the top action is 1.
158
+
159
+ def is_top_level?
160
+ @level == 1
161
+ end
162
+ alias_method :is_top?, :is_top_level?
163
+ alias_method :in_top_level?, :is_top_level?
164
+ alias_method :top_level?, :is_top_level?
165
+
147
166
  end
148
167
 
149
168
  end
@@ -1,7 +1,7 @@
1
1
  require 'facet/annotation'
2
2
  require 'facet/inheritor'
3
+ require 'facets/more/aspects'
3
4
 
4
- require 'glue/aspects'
5
5
  require 'glue/markup'
6
6
 
7
7
  require 'nitro'
@@ -37,12 +37,13 @@ module Nitro
37
37
 
38
38
 
39
39
  module Publishable
40
+
40
41
  def self.included(base)
41
42
  super
42
43
 
43
44
  base.module_eval do
44
45
  include Render
45
- include Glue::Aspects
46
+ include ::Aspects
46
47
  include Flashing
47
48
  include Helpers
48
49
  end
@@ -154,6 +155,13 @@ module Publishable
154
155
  @template_root.reverse!
155
156
  end
156
157
  alias_method :mount, :mount_at
158
+
159
+ # Returns the path where this controller is mounted.
160
+
161
+ def mount_path
162
+ @mount_path
163
+ end
164
+ alias_method :mount_point, :mount_path
157
165
  end
158
166
  end
159
167
 
@@ -170,10 +178,21 @@ private
170
178
  @context.cookies
171
179
  end
172
180
 
181
+ # Send the cookie to the response stream.
182
+
173
183
  def send_cookie(name, value = nil)
174
184
  @context.add_cookie(name, value)
175
185
  end
176
186
 
187
+ # Delete the cookie by setting the expire time to now and
188
+ # clearing the value.
189
+
190
+ def delete_cookie(name)
191
+ cookie = Cookie.new(name, '')
192
+ cookie.expires = Time.now
193
+ @context.add_cookie(cookie)
194
+ end
195
+
177
196
  # Encode controller, action, params into a valid url.
178
197
  # Automatically respects nice urls and routing.
179
198
  #
@@ -210,10 +229,10 @@ private
210
229
  controller = args.shift
211
230
  action = args.shift.to_sym
212
231
 
213
- url = "/#{controller.ann.self.mount_point}/#{action}"
232
+ url = "#{controller.mount_path}/#{action}"
214
233
 
215
234
  unless args.empty?
216
- if controller.respond_to? action
235
+ if controller.respond_to_action_or_template? action
217
236
  param_count = controller.instance_method(action).arity
218
237
  if param_count != 0
219
238
  param_count.times do
@@ -262,7 +281,7 @@ class Controller
262
281
  def self.mounted(path)
263
282
  # Resolve aspects.
264
283
 
265
- Glue::Aspects.include_advice_modules(self)
284
+ ::Aspects.include_advice_modules(self)
266
285
 
267
286
  # The scaffolding code is compiled after the mount, so
268
287
  # that template roots are finalized.
@@ -74,6 +74,7 @@ class Dispatcher
74
74
 
75
75
  # Customize the class for mounting at the given path.
76
76
  #--
77
+ # gmosx, TODO: path should include trailing '/'
77
78
  # gmosx, TODO: should actually create an instance, thus
78
79
  # allowing mounting the same controller to multiple
79
80
  # paths, plus simplifying the code. This instance will
@@ -81,7 +82,6 @@ class Dispatcher
81
82
  #++
82
83
 
83
84
  klass.mount_at(path)
84
-
85
85
  klass.mounted(path) if klass.respond_to?(:mounted)
86
86
  end
87
87
 
@@ -112,16 +112,23 @@ class Dispatcher
112
112
 
113
113
  # Update the routes. Typically called after a new
114
114
  # Controller is mounted.
115
+ #
116
+ # === Example of routing through annotations
117
+ #
118
+ # def view_user
119
+ # "params: #{request[:id]} and #{request[:mode]}"
120
+ # end
121
+ # ann :view_user, :route => [ /user_(\d*)_(*?)\.html/, :id, :mode ]
115
122
 
116
123
  def update_routes
117
- @routes = []
124
+ init_routes()
118
125
 
119
126
  @controllers.each do |base, c|
120
127
  base = '' if base == '/'
121
128
  for m in c.action_methods
122
129
  m = m.to_sym
123
130
  if route = c.ann(m).route and (!route.nil?)
124
- add_route(route.first, c, m, route.last)
131
+ add_route(route.first, :controller => c, :action => m, :params => route.last)
125
132
  end
126
133
  end
127
134
  end
@@ -156,8 +163,9 @@ class Dispatcher
156
163
 
157
164
  klass, action, params = decode_route(path)
158
165
  if klass
159
- context.params.update(params)
160
- return klass, "#{action}_action", controller.ann.self.mount_point
166
+ context.params.update(params) if params
167
+ # gmosx, FIXME/OPTIMIZE: no annotation for mount point!!
168
+ return klass, "#{action}_action", klass.mount_path
161
169
  end
162
170
 
163
171
  parts = path.split('/')
@@ -0,0 +1,63 @@
1
+ require 'glue/cache/memory'
2
+
3
+ # Global scoped variables. This is backed by a Cache store.
4
+
5
+ class Global
6
+
7
+ # The address of the store.
8
+
9
+ setting :cache_address, :default => '127.0.0.1', :doc => 'The address of the store'
10
+
11
+ # The port of the store.
12
+
13
+ setting :cache_port, :default => 9079, :doc => 'The port of the store'
14
+
15
+ # The cache store that backs global variables.
16
+
17
+ setting :cache, :default => ::Glue::MemoryCache.new, :doc => 'The cache store that backs global variables'
18
+
19
+ class << self
20
+
21
+ # Initialize a global value once.
22
+
23
+ def init(key, value)
24
+ unless Global[key]
25
+ Global[key] = value
26
+ end
27
+ end
28
+
29
+ def set(key, value)
30
+ Global.cache[key] = value
31
+ end
32
+ alias_method :[]=, :set
33
+
34
+ def get(key)
35
+ return Global.cache[key]
36
+ end
37
+ alias_method :[], :get
38
+
39
+ # If block is given it acts as an update methods,
40
+ # that transparently handles distributed stores.
41
+ #
42
+ # Global.update(:USERS) do |users|
43
+ # users << 'gmosx'
44
+ # end
45
+
46
+ def update(key)
47
+ if block_given?
48
+ # update, also handles distributed stores.
49
+ val = Global.cache[key]
50
+ yield val
51
+ Global.cache[key] = val
52
+ end
53
+ end
54
+
55
+ def delete(key)
56
+ Global.cache.delete(key)
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+
63
+ # * George Moschovitis <gm@navel.gr>