nitro 0.28.0 → 0.29.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 (73) hide show
  1. data/CHANGELOG +382 -0
  2. data/ProjectInfo +4 -4
  3. data/README +1 -1
  4. data/doc/AUTHORS +15 -15
  5. data/doc/MIGRATION +13 -0
  6. data/doc/RELEASES +102 -0
  7. data/lib/glue/sweeper.rb +1 -1
  8. data/lib/nitro.rb +38 -9
  9. data/lib/nitro/adapter/acgi.rb +1 -3
  10. data/lib/nitro/adapter/cgi.rb +1 -1
  11. data/lib/nitro/adapter/fastcgi.rb +1 -3
  12. data/lib/nitro/adapter/mongrel.rb +8 -6
  13. data/lib/nitro/adapter/webrick.rb +1 -2
  14. data/lib/nitro/cgi.rb +1 -1
  15. data/lib/nitro/compiler.rb +21 -40
  16. data/lib/nitro/compiler/elements.rb +72 -32
  17. data/lib/nitro/compiler/errors.rb +92 -42
  18. data/lib/nitro/compiler/include.rb +47 -17
  19. data/lib/nitro/compiler/morphing.rb +1 -3
  20. data/lib/nitro/compiler/script.rb +2 -2
  21. data/lib/nitro/context.rb +36 -0
  22. data/lib/nitro/controller.rb +140 -31
  23. data/lib/nitro/dispatcher.rb +27 -28
  24. data/lib/nitro/element.rb +52 -15
  25. data/lib/nitro/flash.rb +44 -0
  26. data/lib/nitro/helper/buffer.rb +0 -2
  27. data/lib/nitro/helper/form.rb +2 -2
  28. data/lib/nitro/helper/form/controls.rb +14 -3
  29. data/lib/nitro/helper/pager.rb +1 -1
  30. data/lib/nitro/helper/table.rb +4 -3
  31. data/lib/nitro/helper/xml.rb +1 -1
  32. data/lib/nitro/part.rb +20 -0
  33. data/lib/nitro/render.rb +44 -5
  34. data/lib/nitro/router.rb +81 -0
  35. data/lib/nitro/scaffolding.rb +24 -23
  36. data/lib/nitro/server.rb +12 -1
  37. data/lib/nitro/server/runner.rb +12 -0
  38. data/lib/nitro/session.rb +3 -12
  39. data/lib/nitro/session/drb.rb +2 -5
  40. data/lib/nitro/session/file.rb +2 -2
  41. data/lib/nitro/session/memcached.rb +14 -0
  42. data/lib/nitro/session/memory.rb +3 -26
  43. data/lib/nitro/session/og.rb +1 -1
  44. data/lib/nitro/test/assertions.rb +1 -1
  45. data/lib/nitro/test/context.rb +8 -2
  46. data/lib/nitro/test/testcase.rb +16 -7
  47. data/proto/public/error.xhtml +58 -21
  48. data/proto/public/js/controls.js +60 -15
  49. data/proto/public/js/dragdrop.js +105 -16
  50. data/proto/public/js/effects.js +19 -12
  51. data/proto/public/js/scriptaculous.js +1 -1
  52. data/proto/public/js/slider.js +2 -2
  53. data/proto/public/js/unittest.js +29 -20
  54. data/proto/public/scaffold/edit.xhtml +1 -1
  55. data/proto/public/scaffold/index.xhtml +2 -2
  56. data/proto/public/scaffold/list.xhtml +2 -2
  57. data/proto/public/scaffold/new.xhtml +1 -1
  58. data/proto/public/scaffold/search.xhtml +1 -1
  59. data/src/part/admin/controller.rb +5 -5
  60. data/src/part/admin/template/index.xhtml +2 -2
  61. data/test/nitro/compiler/tc_compiler.rb +23 -0
  62. data/test/nitro/helper/tc_table.rb +35 -0
  63. data/test/nitro/tc_cgi.rb +1 -1
  64. data/test/nitro/tc_controller.rb +3 -3
  65. data/test/nitro/tc_controller_aspect.rb +2 -0
  66. data/test/nitro/tc_dispatcher.rb +10 -1
  67. data/test/nitro/tc_flash.rb +14 -0
  68. data/test/nitro/tc_router.rb +58 -0
  69. data/test/nitro/tc_session.rb +26 -9
  70. metadata +13 -12
  71. data/lib/nitro/routing.rb +0 -41
  72. data/test/nitro/caching/tc_stores.rb +0 -17
  73. data/test/nitro/tc_table.rb +0 -66
@@ -1,9 +1,11 @@
1
1
  require 'rexml/document'
2
2
  require 'rexml/streamlistener'
3
+ require 'facet/class/by_name'
3
4
 
4
5
  require 'nitro/element'
5
6
  require "glue/html"
6
7
 
8
+
7
9
  module Nitro
8
10
 
9
11
  # A compiler that handles the processing of Elements
@@ -16,8 +18,9 @@ class Elements # :nodoc: all
16
18
  attr_accessor :buffer
17
19
  attr_accessor :stack
18
20
 
19
- def initialize
20
- super
21
+ def initialize(compiler)
22
+ super()
23
+ @compiler = compiler
21
24
  @buffer = ''
22
25
  @stack = []
23
26
  end
@@ -29,35 +32,20 @@ class Elements # :nodoc: all
29
32
  # check if the name starts with the element prefix, or
30
33
  # is capitalized.
31
34
 
32
- if name =~ PREFIX_RE or name =~ CAPITALIZED_RE
33
- name = name.split(':')[1].camelize if name =~ PREFIX_RE
34
-
35
- # First try to use Nitro::Element::xxx then ::xxx
36
-
37
- if klass = Nitro::Element.const_get(name) || Object.const_get(name)
38
- unless klass.ancestors.include? Nitro::Element
39
- if Element.auto_extend
40
- klass.send(:include, Nitro::ElementMixin)
41
- else
42
- Logger.error "Invalid element class '#{name}', does not extend Nitro::Element"
43
- end
44
- end
45
- else
46
- Logger.error "The class of this element tag '#{name}' does not exist"
47
- end
48
-
49
- if klass and obj = klass.new
50
- attributes.each do | k, v |
51
- obj.instance_variable_set("@#{k}", v)
52
- end
35
+ if klass = is_element?(name)
36
+
37
+ obj = klass.new
38
+ attributes.each do | k, v |
39
+ obj.instance_variable_set("@#{k}", v)
40
+ end
53
41
 
54
- @stack.push [obj, @buffer, @parent]
42
+ @stack.push [obj, @buffer, @parent]
55
43
 
56
- @buffer = obj._text
57
- @parent.add_child(obj) if @parent
44
+ @buffer = obj._text
45
+ @parent.add_child(obj) if @parent
58
46
 
59
- @parent = obj
60
- end
47
+ @parent = obj
48
+
61
49
  else # This is a static element.
62
50
  attrs = []
63
51
 
@@ -74,14 +62,66 @@ class Elements # :nodoc: all
74
62
  def tag_end(name)
75
63
  # check if the name starts with the element prefix, or
76
64
  # is capitalized.
77
- if name =~ PREFIX_RE or name =~ CAPITALIZED_RE
78
- name = name.split(':')[1].camelize if name =~ PREFIX_RE
65
+ if is_element? name
79
66
  obj, @buffer, @parent = @stack.pop
80
67
  @buffer << obj.render
81
68
  else
82
69
  @buffer << "</#{name}>"
83
70
  end
84
71
  end
72
+
73
+
74
+ # Check if a tag is a Nitro::Element. If found, it also tries to
75
+ # auto-extend the klass.
76
+ #
77
+ # returns the Element class if found
78
+ def is_element?(name)
79
+ # Doesn't support modulized classes
80
+ # name = name.demodulize
81
+ return false unless name =~ PREFIX_RE or name =~ CAPITALIZED_RE
82
+
83
+ name = name.gsub(/#{PREFIX_RE}:/,'').camelize if name =~ PREFIX_RE
84
+
85
+ # First try to use Nitro::Element::xxx then ::xxx
86
+
87
+ begin
88
+ klass = Class.by_name("Nitro::Element::#{name}")
89
+ rescue
90
+ end
91
+
92
+ # Look into the module the controller's module if any
93
+
94
+ begin
95
+ namespace = @compiler.controller.name
96
+ if /::/ =~ namespace
97
+ namespace = namespace.gsub( /::[a-zA-Z]+$/, "::#{name}" )
98
+ pp namespace
99
+ klass = Class.by_name(namespace)
100
+ end
101
+ rescue
102
+ end unless klass
103
+
104
+ # Look in the root module
105
+
106
+ begin
107
+ klass = Class.by_name(name)
108
+ rescue
109
+ end unless klass
110
+
111
+ return false unless klass.kind_of?( Class )
112
+
113
+ # Try to auto-extend
114
+
115
+ unless klass.ancestors.include? Nitro::Element
116
+ if Element.auto_extend
117
+ klass.send(:include, Nitro::ElementMixin)
118
+ else
119
+ return false
120
+ end
121
+ end
122
+
123
+ return klass
124
+ end
85
125
 
86
126
  def text(str)
87
127
  @buffer << str
@@ -92,7 +132,7 @@ class Elements # :nodoc: all
92
132
  end
93
133
 
94
134
  def comment(c)
95
- unless Template.strip_xml_comments
135
+ unless Glue::Template.strip_xml_comments
96
136
  @buffer << "<!--#{c}-->"
97
137
  end
98
138
  end
@@ -116,7 +156,7 @@ class Elements # :nodoc: all
116
156
  #++
117
157
 
118
158
  def transform(source, compiler)
119
- listener = Listener.new
159
+ listener = Listener.new(compiler)
120
160
 
121
161
  REXML::Document.parse_stream(source, listener)
122
162
 
@@ -1,65 +1,115 @@
1
1
  require 'facet/ormsupport'
2
2
 
3
- module Nitro
3
+ # This supports source code extraction from a full
4
+ # backtrace from any exception and also exposes class
5
+ # methods incase you wish to harness them from elsewhere.
6
+ #--
7
+ # TODO: Cache source code? Memory vs Speed - Is it worth
8
+ # it?
9
+ # FIXME: Don't change the Exception class, use NitroException
10
+ # or something.
11
+ #++
4
12
 
5
- class ActionCompileError < SyntaxError
6
- SOURCE_CODE_RADIUS = 5
13
+ class Exception
7
14
 
8
- attr_accessor :original_excpetion
9
-
10
- def initialize(source_code, filename, original_exception)
11
- @source_code = source_code.split("\n")
12
- @filename = filename
13
- @original_exception = original_exception
14
- end
15
+ # Radius of the source code to display arround the error line.
15
16
 
16
- def line_number
17
- trace = @original_exception.backtrace.join
17
+ SOURCE_RADIUS = 5
18
+
19
+ class << self
18
20
 
19
- if trace.include?(":in `class_eval'")
20
- trace.scan(/:([0-9]*):in `class_eval'/).first.first.to_i
21
- else
22
- 1
21
+ # Extracts the filename and line from the given line (step)
22
+ # of the backtrace.
23
+
24
+ def file_and_line_no(step)
25
+ file = no = nil
26
+ if res = step.match(/^(.+):(\d+)$/)
27
+ file = res[1]
28
+ no = res[2]
29
+ elsif res = step.match(/^(.+):(\d+):in `.+'$/)
30
+ file = res[1]
31
+ no = res[2]
32
+ end
33
+ if file and no
34
+ [file,no.to_i]
35
+ else
36
+ nil
37
+ end
23
38
  end
24
- end
39
+
40
+ # Extract the source arround the error line.
41
+
42
+ def source_extract(step, indent = 0)
43
+ begin
44
+ file, no = file_and_line_no(step)
45
+ source = File.read(file)
46
+
47
+ if file =~ /\.xhtml$/
48
+ source = Compiler.new.transform_template(source)
49
+ no -= 2
50
+ end
51
+
52
+ source = source.split("\n")
53
+ start = (no-1) - SOURCE_RADIUS
54
+ finish = (no-1) + SOURCE_RADIUS
55
+ start = 0 if start < 0
56
+ finish = source.size if finish > source.size
25
57
 
26
- def source_extract(indent = 0)
27
- ln = line_number
28
- start_line = [ln - SOURCE_CODE_RADIUS, 0].max
29
- end_line = [ln + SOURCE_CODE_RADIUS - 1, @source_code.length].min
58
+ number = start
59
+ extract = source[start..finish].collect do |line|
60
+ number += 1
61
+ line = line.gsub(/; @out << %\^/, ' ?>').gsub(/\^;/, '<?r ')
62
+ "#{' ' * indent}#{number}: #{line}"
63
+ end
30
64
 
31
- number = start_line
32
- extract = @source_code[start_line..end_line].collect do |line|
33
- number += 1
34
- line = line.gsub(/; @out << %\^/, ' ?>').gsub(/\^;/, '<?r ')
35
- if number == line_number
36
- "#{' ' * indent}#{number}: #{line}"
37
- else
38
- "#{' ' * indent}#{number}: #{line}"
65
+ return extract.join("\n")
66
+ rescue => ex
67
+ ''
39
68
  end
40
69
  end
70
+
71
+ end
41
72
 
42
- return extract.join("\n")
73
+ # Identifies the 'hot' index in the backtrace, where the error
74
+ # actually happened.
75
+
76
+ def hot_trace_index
77
+ index = 0
78
+ while backtrace[index] =~ /controller\.rb(.*)method_missing/
79
+ index += 1
80
+ end
81
+ return index
43
82
  end
44
83
 
45
- def backtrace
46
- @original_exception.backtrace.collect do |line|
47
- line.gsub("#{Nitro::LibPath}/", '')
48
- end.compact
84
+ # Returns the 'hot' backtrace line.
85
+
86
+ def hot_trace_step
87
+ backtrace[hot_trace_index]
49
88
  end
50
89
 
51
- def to_s
52
- "#{self.class.to_s.demodulize} (#{@original_exception.class}): '#@filename' at line ##{line_number}"
90
+ # Returns the 'hot' backtrace file.
91
+
92
+ def hot_file
93
+ file_and_line_no(hot_trace_step).first
53
94
  end
54
95
 
55
- def message
56
- "#{to_s}\n#{source_extract}"
96
+ def file_and_line_no(step)
97
+ self.class.file_and_line_no(step)
98
+ end
99
+
100
+ def source_extract(step = hot_trace_step, indent = 0)
101
+ self.class.source_extract(step,indent)
102
+ end
103
+
104
+ # Extract source for backtrace.
105
+
106
+ def source_for_backtrace(indent = 0)
107
+ backtrace.inject([]) do |sources, step|
108
+ sources << source_extract(indent,step)
109
+ end
57
110
  end
58
- end
59
111
 
60
- class TemplateCompileError < ActionCompileError
61
- end
62
-
63
112
  end
64
113
 
65
114
  # * George Moschovitis <gm@navel.gr>
115
+ # * Rob Pitt <rob@motionpath.co.uk>
@@ -7,27 +7,57 @@ module Nitro
7
7
  # to use static includes in many many cases.
8
8
 
9
9
  class StaticInclude
10
- #--
11
- # FIXME: use template root of the controller.
12
- #++
10
+ # Statically include sub-template files.
11
+ # The target file is included at compile time.
12
+ # If the given path is relative, the template_root stack of
13
+ # the controller is traversed. If an absolute path is provided,
14
+ # templates are searched only in Template.root
15
+ #
16
+ # gmosx: must be xformed before the <?r pi.
17
+ #
18
+ # === Example
19
+ # <?include href="root/myfile.sx" ?>
13
20
 
14
- def self.transform(text)
15
- # Statically include sub-template files.
16
- # The target file is included at compile time.
17
- #
18
- # gmosx: must be xformed before the <?r pi.
19
- #
20
- # Example:
21
- # <?include href="root/myfile.sx" ?>
22
-
21
+ def self.transform(text, compiler)
22
+ controller = compiler.controller
23
+
23
24
  return text.gsub(/<\?include href=["|'](.*?)["|'](.*)\?>/) do |match|
24
- unless File.exist?(filename = "#{Template.root}/#$1")
25
- unless File.exist?(filename = "#{Template.root}/#$1.xinc")
26
- unless File.exist?(filename = "#{Template.root}/#$1.xhtml")
27
- raise "Cannot include '#{Template.root}/#$1'"
28
- end
25
+ found = false
26
+
27
+ if $1[0] == ?/
28
+ # Absolute path, search only in the application template
29
+ # root.
30
+
31
+ href = $1[1, 999999] # hack!!
32
+ template_stack = [ Glue::Template.root ]
33
+ else
34
+ # Relative path, traverse the template_root stack.
35
+
36
+ href = $1
37
+ template_stack = controller.instance_variable_get(:@template_root)
38
+ end
39
+
40
+ for template_root in template_stack
41
+ if File.exist?(filename = "#{template_root}/#{href}")
42
+ found = true
43
+ break
44
+ end
45
+
46
+ if File.exist?(filename = "#{template_root}/#{href}.xinc")
47
+ found = true
48
+ break
49
+ end
50
+
51
+ if File.exist?(filename = "#{template_root}/#{href}.xhtml")
52
+ found = true
53
+ break
29
54
  end
30
55
  end
56
+
57
+ unless found
58
+ raise "Cannot statically include '#{href}'"
59
+ end
60
+
31
61
  itext = File.read(filename)
32
62
  itext.gsub!(/<\?xml.*\?>/, '')
33
63
  itext.gsub!(/<\/?root(.*?)>/m, ' ');
@@ -4,8 +4,6 @@ require 'rexml/streamlistener'
4
4
  require 'facet/dictionary'
5
5
  require 'facet/string/blank'
6
6
 
7
- require 'glue/html'
8
-
9
7
  module Nitro
10
8
 
11
9
  # :section: A collection of standard morphers.
@@ -205,7 +203,7 @@ class Morphing
205
203
  end
206
204
 
207
205
  def comment(c)
208
- unless Template.strip_xml_comments
206
+ unless Glue::Template.strip_xml_comments
209
207
  @buffer << "<!--#{c}-->"
210
208
  end
211
209
  end
@@ -87,13 +87,13 @@ module ScriptCompiler
87
87
  js_buffer = compiler.shared[:js_buffer]
88
88
 
89
89
  if script or js_buffer
90
- text.sub!(/<\/html>/) do |match|
90
+ text.sub!(/<\/body>/) do |match|
91
91
  %{
92
92
  <script type="text/javascript">
93
93
  #{script}
94
94
  #{js_buffer}
95
95
  </script>
96
- </html>
96
+ </body>
97
97
  }
98
98
  end
99
99
  end
@@ -1,3 +1,5 @@
1
+ require 'forwardable'
2
+
1
3
  require 'facet/kernel/assign_with'
2
4
 
3
5
  require 'nitro/cgi'
@@ -21,6 +23,10 @@ class Context
21
23
  include Response
22
24
  include Render
23
25
 
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
+
24
30
  # The configuration parameters.
25
31
 
26
32
  attr_accessor :conf
@@ -60,6 +66,8 @@ class Context
60
66
  if @session.has_key?(:FLASH)
61
67
  @session[:FLASH].clean
62
68
  end
69
+
70
+ # INVESTIGATE: is this needed?
63
71
  @session.sync
64
72
  end
65
73
  end
@@ -85,6 +93,14 @@ class Context
85
93
  @session || @session = Session.lookup(self)
86
94
  end
87
95
 
96
+ # Access global variables. In a distributed server scenario,
97
+ # these variables can reside outside of the process.
98
+
99
+ def global
100
+ return $global
101
+ end
102
+ alias_method :application, :global
103
+
88
104
  # Lookup the controller for this request.
89
105
  #--
90
106
  # FIXME: improve this! BUGGY
@@ -101,6 +117,14 @@ class Context
101
117
  #
102
118
  # * name
103
119
  # * force_boolean
120
+ #
121
+ # === Example
122
+ #
123
+ # request.fill(User.new)
124
+ #
125
+ # Prefer to use the following form:
126
+ #
127
+ # User.new.assign_with(request)
104
128
 
105
129
  def fill(obj, options = {})
106
130
  Property.populate_object(obj, @params, options)
@@ -110,6 +134,18 @@ class Context
110
134
 
111
135
  end
112
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
147
+ end
148
+
113
149
  end
114
150
 
115
151
  # * George Moschovitis <gm@navel.gr>