nitro 0.28.0 → 0.29.0

Sign up to get free protection for your applications and to get access to all the features.
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>