nitro 0.29.0 → 0.30.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 (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>