masterview 0.2.5 → 0.3.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 (155) hide show
  1. data/CHANGELOG +31 -1
  2. data/README +70 -69
  3. data/RELEASE_NOTES +70 -64
  4. data/Rakefile +26 -27
  5. data/TODO +13 -29
  6. data/doc/about.html +246 -0
  7. data/doc/configuration.html +49 -36
  8. data/doc/developer.html +423 -41
  9. data/doc/directives.html +139 -51
  10. data/doc/guide.html +19 -9
  11. data/doc/index.html +90 -224
  12. data/doc/installation.html +36 -28
  13. data/doc/media_list.html +30 -20
  14. data/doc/simple_diagram.html +3 -5
  15. data/doc/stylesheets/masterview.css +16 -1
  16. data/examples/rails_app_config/masterview/settings.rb +2 -1
  17. data/init.rb +1 -1
  18. data/lib/#ChangeLog# +6 -0
  19. data/lib/masterview/analyzer.rb +48 -34
  20. data/lib/masterview/attr_string_parser.rb +5 -1
  21. data/lib/masterview/case_insensitive_hash.rb +69 -0
  22. data/lib/masterview/{pathname_extensions.rb → core_ext/pathname.rb} +0 -0
  23. data/lib/masterview/{string_extensions.rb → core_ext/string.rb} +0 -0
  24. data/lib/masterview/deprecated/directive_base.rb +362 -0
  25. data/lib/masterview/directive_base.rb +201 -179
  26. data/lib/masterview/directive_dsl.rb +457 -0
  27. data/lib/masterview/directive_helpers.rb +28 -141
  28. data/lib/masterview/directive_load_path.rb +388 -0
  29. data/lib/masterview/directive_metadata.rb +377 -0
  30. data/lib/masterview/directive_registry.rb +259 -69
  31. data/lib/masterview/directives/.metadata +16 -0
  32. data/lib/masterview/directives/attr.rb +9 -8
  33. data/lib/masterview/directives/block.rb +11 -14
  34. data/lib/masterview/directives/check_box.rb +13 -18
  35. data/lib/masterview/directives/collection_select.rb +15 -29
  36. data/lib/masterview/directives/content.rb +9 -3
  37. data/lib/masterview/directives/else.rb +15 -13
  38. data/lib/masterview/directives/elsif.rb +14 -13
  39. data/lib/masterview/directives/eval.rb +20 -0
  40. data/lib/masterview/directives/form.rb +56 -9
  41. data/lib/masterview/directives/form_remote.rb +26 -0
  42. data/lib/masterview/directives/global_inline_erb.rb +10 -14
  43. data/lib/masterview/directives/hidden_field.rb +11 -20
  44. data/lib/masterview/directives/if.rb +13 -12
  45. data/lib/masterview/directives/image_tag.rb +20 -28
  46. data/lib/masterview/directives/import.rb +5 -12
  47. data/lib/masterview/directives/import_render.rb +7 -19
  48. data/lib/masterview/directives/insert_generated_comment.rb +8 -11
  49. data/lib/masterview/directives/javascript_include.rb +21 -12
  50. data/lib/masterview/directives/link_to.rb +14 -8
  51. data/lib/masterview/directives/link_to_function.rb +22 -0
  52. data/lib/masterview/directives/link_to_if.rb +15 -13
  53. data/lib/masterview/directives/link_to_remote.rb +13 -8
  54. data/lib/masterview/directives/omit_tag.rb +32 -16
  55. data/lib/masterview/directives/password_field.rb +10 -22
  56. data/lib/masterview/directives/radio_button.rb +11 -22
  57. data/lib/masterview/directives/replace.rb +7 -8
  58. data/lib/masterview/directives/select.rb +11 -24
  59. data/lib/masterview/directives/stylesheet_link.rb +20 -12
  60. data/lib/masterview/directives/submit.rb +11 -5
  61. data/lib/masterview/directives/text_area.rb +10 -23
  62. data/lib/masterview/directives/text_field.rb +10 -22
  63. data/lib/masterview/exceptions.rb +21 -0
  64. data/lib/masterview/extras/app/controllers/masterview_controller.rb +102 -75
  65. data/lib/masterview/extras/app/views/layouts/masterview_admin.rhtml +24 -23
  66. data/lib/masterview/extras/app/views/layouts/masterview_admin_config.rhtml +81 -0
  67. data/lib/masterview/extras/app/views/masterview/admin/configuration.rhtml +5 -1
  68. data/lib/masterview/extras/app/views/masterview/admin/create.rhtml +2 -2
  69. data/lib/masterview/extras/app/views/masterview/admin/directives.rhtml +5 -0
  70. data/lib/masterview/extras/app/views/masterview/admin/features.rhtml +5 -79
  71. data/lib/masterview/extras/app/views/masterview/admin/interact.rhtml +5 -0
  72. data/lib/masterview/extras/app/views/masterview/admin/list.rhtml +3 -71
  73. data/lib/masterview/extras/init_mv_admin_pages.rb +42 -23
  74. data/lib/masterview/filter_helpers.rb +26 -0
  75. data/lib/masterview/initializer.rb +99 -53
  76. data/lib/masterview/io.rb +19 -15
  77. data/lib/masterview/keyword_expander.rb +7 -2
  78. data/lib/masterview/masterview_info.rb +229 -23
  79. data/lib/masterview/masterview_version.rb +2 -2
  80. data/lib/masterview/parser.rb +275 -105
  81. data/lib/masterview/parser_helpers.rb +54 -0
  82. data/lib/masterview/rails_ext/action_controller_erb_direct.rb +29 -0
  83. data/lib/masterview/rails_ext/action_controller_reparse_checking.rb +27 -0
  84. data/lib/masterview/{extras/init_rails_erb_mv_direct.rb → rails_ext/action_view_erb_direct.rb} +12 -59
  85. data/lib/masterview/template_spec.rb +3 -2
  86. data/lib/masterview.rb +21 -12
  87. data/lib/rexml/parsers/baseparser_with_doctype_fix.rb +473 -0
  88. data/lib/rexml/parsers/sax2parser_with_doctype_fix.rb +243 -0
  89. data/test/directive_test_helper.rb +135 -0
  90. data/test/fixtures/directives/id_check.rb +18 -0
  91. data/test/fixtures/directives/test_directive_events.rb +70 -0
  92. data/test/test_helper.rb +18 -5
  93. data/test/tmp/views/layouts/product.rhtml +10 -10
  94. data/test/tmp/views/product/_form.rhtml +4 -4
  95. data/test/tmp/views/product/_product.rhtml +3 -3
  96. data/test/tmp/views/product/destroy.rhtml +5 -5
  97. data/test/tmp/views/product/edit.rhtml +4 -4
  98. data/test/tmp/views/product/list.rhtml +3 -3
  99. data/test/tmp/views/product/new.rhtml +4 -4
  100. data/test/tmp/views/product/show.rhtml +2 -2
  101. data/test/unit/attr_string_parser_test.rb +105 -0
  102. data/test/unit/case_insensitive_hash_mod_test.rb +104 -0
  103. data/test/unit/config_settings_test.rb +13 -1
  104. data/test/unit/default_generate_mio_filter_test.rb +3 -3
  105. data/test/unit/deprecated_directive_base_test.rb +30 -0
  106. data/test/unit/directive_attr_test.rb +111 -35
  107. data/test/unit/directive_base_test.rb +520 -1
  108. data/test/unit/directive_block_test.rb +30 -22
  109. data/test/unit/directive_content_test.rb +24 -11
  110. data/test/unit/directive_else_test.rb +18 -15
  111. data/test/unit/directive_elsif_test.rb +17 -15
  112. data/test/unit/directive_form_remote_test.rb +59 -0
  113. data/test/unit/directive_form_test.rb +31 -39
  114. data/test/unit/directive_global_inline_erb_test.rb +28 -17
  115. data/test/unit/directive_grid_test_notready.rb +38 -0
  116. data/test/unit/directive_helpers_test.rb +39 -0
  117. data/test/unit/directive_hidden_field_test.rb +44 -29
  118. data/test/unit/directive_if_test.rb +10 -7
  119. data/test/unit/directive_image_tag_test.rb +69 -61
  120. data/test/unit/directive_import_render_test.rb +28 -38
  121. data/test/unit/directive_import_test.rb +16 -14
  122. data/test/unit/directive_insert_generated_comment_test.rb +32 -0
  123. data/test/unit/directive_javascript_include_test.rb +40 -43
  124. data/test/unit/directive_link_to_function_test.rb +40 -0
  125. data/test/unit/directive_link_to_if_test.rb +52 -12
  126. data/test/unit/directive_link_to_remote_test.rb +58 -0
  127. data/test/unit/directive_link_to_test.rb +46 -31
  128. data/test/unit/directive_load_path_test.rb +257 -0
  129. data/test/unit/directive_metadata_test.rb +313 -0
  130. data/test/unit/directive_omit_tag_test.rb +73 -21
  131. data/test/unit/directive_password_field_test.rb +44 -38
  132. data/test/unit/directive_registry_test.rb +44 -0
  133. data/test/unit/directive_replace_test.rb +28 -12
  134. data/test/unit/directive_stylesheet_link_test.rb +43 -36
  135. data/test/unit/directive_submit_test.rb +29 -30
  136. data/test/unit/directive_text_area_test.rb +40 -36
  137. data/test/unit/directive_text_field_test.rb +44 -38
  138. data/test/unit/example_directive_child_events_test.rb +41 -0
  139. data/test/unit/example_test.rb +31 -4
  140. data/test/unit/file_mio_test.rb +18 -13
  141. data/test/unit/filter_helpers_test.rb +10 -8
  142. data/test/unit/find_directive_parent_test.rb +174 -0
  143. data/test/unit/keyword_expander_test.rb +4 -2
  144. data/test/unit/mio_test.rb +18 -11
  145. data/test/unit/mtime_string_hash_mio_tree_test.rb +5 -1
  146. data/test/unit/parser_test.rb +41 -29
  147. data/test/unit/pathname_extensions_test.rb +1 -1
  148. data/test/unit/run_parser_test.rb +2 -2
  149. data/test/unit/simplified_directive_base_test.rb +256 -0
  150. data/test/unit/string_hash_mio_test.rb +5 -1
  151. data/test/unit/template_file_watcher_test.rb +2 -2
  152. data/test/unit/template_test.rb +221 -46
  153. metadata +86 -45
  154. data/lib/masterview/directives/testfilter.rb +0 -55
  155. data/lib/masterview/extras/init_rails_reparse_checking.rb +0 -62
@@ -0,0 +1,243 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ currentPath = File.dirname(__FILE__)
4
+ require File.join( currentPath, 'baseparser_with_doctype_fix' )
5
+ require 'rexml/parseexception'
6
+ require 'rexml/namespace'
7
+ require 'rexml/text'
8
+
9
+ # REXML version 3.1.5 and lower have a bug which does not trigger the doctype event for sax2 listener
10
+ # We have submitted a fix to REXML ticket number 92 with some test cases and a patch for 3.1.5
11
+ # http://www.germane-software.com/projects/rexml/ticket/92
12
+ # Until that is deployed this patched version of the REXML sax2 parser has the applied fixes
13
+ # The fix also requires a patched baseparser which is required above
14
+
15
+ module REXML
16
+ module Parsers
17
+ # SAX2Parser
18
+ class SAX2ParserWithDoctypeFix
19
+ def initialize source
20
+ @parser = BaseParserWithDoctypeFix.new(source)
21
+ @listeners = []
22
+ @procs = []
23
+ @namespace_stack = []
24
+ @has_listeners = false
25
+ @tag_stack = []
26
+ @entities = {}
27
+ end
28
+
29
+ def add_listener( listener )
30
+ @parser.add_listener( listener )
31
+ end
32
+
33
+ # Listen arguments:
34
+ #
35
+ # Symbol, Array, Block
36
+ # Listen to Symbol events on Array elements
37
+ # Symbol, Block
38
+ # Listen to Symbol events
39
+ # Array, Listener
40
+ # Listen to all events on Array elements
41
+ # Array, Block
42
+ # Listen to :start_element events on Array elements
43
+ # Listener
44
+ # Listen to All events
45
+ #
46
+ # Symbol can be one of: :start_element, :end_element,
47
+ # :start_prefix_mapping, :end_prefix_mapping, :characters,
48
+ # :processing_instruction, :doctype, :attlistdecl, :elementdecl,
49
+ # :entitydecl, :notationdecl, :cdata, :xmldecl, :comment
50
+ #
51
+ # There is an additional symbol that can be listened for: :progress.
52
+ # This will be called for every event generated, passing in the current
53
+ # stream position.
54
+ #
55
+ # Array contains regular expressions or strings which will be matched
56
+ # against fully qualified element names.
57
+ #
58
+ # Listener must implement the methods in SAX2Listener
59
+ #
60
+ # Block will be passed the same arguments as a SAX2Listener method would
61
+ # be, where the method name is the same as the matched Symbol.
62
+ # See the SAX2Listener for more information.
63
+ def listen( *args, &blok )
64
+ if args[0].kind_of? Symbol
65
+ if args.size == 2
66
+ args[1].each { |match| @procs << [args[0], match, blok] }
67
+ else
68
+ add( [args[0], nil, blok] )
69
+ end
70
+ elsif args[0].kind_of? Array
71
+ if args.size == 2
72
+ args[0].each { |match| add( [nil, match, args[1]] ) }
73
+ else
74
+ args[0].each { |match| add( [ :start_element, match, blok ] ) }
75
+ end
76
+ else
77
+ add([nil, nil, args[0]])
78
+ end
79
+ end
80
+
81
+ def deafen( listener=nil, &blok )
82
+ if listener
83
+ @listeners.delete_if {|item| item[-1] == listener }
84
+ @has_listeners = false if @listeners.size == 0
85
+ else
86
+ @procs.delete_if {|item| item[-1] == blok }
87
+ end
88
+ end
89
+
90
+ def parse
91
+ @procs.each { |sym,match,block| block.call if sym == :start_document }
92
+ @listeners.each { |sym,match,block|
93
+ block.start_document if sym == :start_document or sym.nil?
94
+ }
95
+ root = context = []
96
+ while true
97
+ event = @parser.pull
98
+ case event[0]
99
+ when :end_document
100
+ handle( :end_document )
101
+ break
102
+ when :start_doctype
103
+ handle( :doctype, *event[1..-1])
104
+ when :end_doctype
105
+ context = context[1]
106
+ when :start_element
107
+ @tag_stack.push(event[1])
108
+ # find the observers for namespaces
109
+ procs = get_procs( :start_prefix_mapping, event[1] )
110
+ listeners = get_listeners( :start_prefix_mapping, event[1] )
111
+ if procs or listeners
112
+ # break out the namespace declarations
113
+ # The attributes live in event[2]
114
+ event[2].each {|n, v| event[2][n] = @parser.normalize(v)}
115
+ nsdecl = event[2].find_all { |n, value| n =~ /^xmlns(:|$)/ }
116
+ nsdecl.collect! { |n, value| [ n[6..-1], value ] }
117
+ @namespace_stack.push({})
118
+ nsdecl.each do |n,v|
119
+ @namespace_stack[-1][n] = v
120
+ # notify observers of namespaces
121
+ procs.each { |ob| ob.call( n, v ) } if procs
122
+ listeners.each { |ob| ob.start_prefix_mapping(n, v) } if listeners
123
+ end
124
+ end
125
+ event[1] =~ Namespace::NAMESPLIT
126
+ prefix = $1
127
+ local = $2
128
+ uri = get_namespace(prefix)
129
+ # find the observers for start_element
130
+ procs = get_procs( :start_element, event[1] )
131
+ listeners = get_listeners( :start_element, event[1] )
132
+ # notify observers
133
+ procs.each { |ob| ob.call( uri, local, event[1], event[2] ) } if procs
134
+ listeners.each { |ob|
135
+ ob.start_element( uri, local, event[1], event[2] )
136
+ } if listeners
137
+ when :end_element
138
+ @tag_stack.pop
139
+ event[1] =~ Namespace::NAMESPLIT
140
+ prefix = $1
141
+ local = $2
142
+ uri = get_namespace(prefix)
143
+ # find the observers for start_element
144
+ procs = get_procs( :end_element, event[1] )
145
+ listeners = get_listeners( :end_element, event[1] )
146
+ # notify observers
147
+ procs.each { |ob| ob.call( uri, local, event[1] ) } if procs
148
+ listeners.each { |ob|
149
+ ob.end_element( uri, local, event[1] )
150
+ } if listeners
151
+
152
+ namespace_mapping = @namespace_stack.pop
153
+ # find the observers for namespaces
154
+ procs = get_procs( :end_prefix_mapping, event[1] )
155
+ listeners = get_listeners( :end_prefix_mapping, event[1] )
156
+ if procs or listeners
157
+ namespace_mapping.each do |prefix, uri|
158
+ # notify observers of namespaces
159
+ procs.each { |ob| ob.call( prefix ) } if procs
160
+ listeners.each { |ob| ob.end_prefix_mapping(prefix) } if listeners
161
+ end
162
+ end
163
+ when :text
164
+ #normalized = @parser.normalize( event[1] )
165
+ #handle( :characters, normalized )
166
+ copy = event[1].clone
167
+ @entities.each { |key, value| copy = copy.gsub("&#{key};", value) }
168
+ copy.gsub!( Text::NUMERICENTITY ) {|m|
169
+ m=$1
170
+ m = "0#{m}" if m[0] == ?x
171
+ [Integer(m)].pack('U*')
172
+ }
173
+ handle( :characters, copy )
174
+ when :entitydecl
175
+ @entities[ event[1] ] = event[2] if event.size == 3
176
+ handle( *event )
177
+ when :processing_instruction, :comment, :attlistdecl,
178
+ :elementdecl, :cdata, :notationdecl, :xmldecl
179
+ handle( *event )
180
+ end
181
+ handle( :progress, @parser.position )
182
+ end
183
+ end
184
+
185
+ private
186
+ def handle( symbol, *arguments )
187
+ tag = @tag_stack[-1]
188
+ procs = get_procs( symbol, tag )
189
+ listeners = get_listeners( symbol, tag )
190
+ # notify observers
191
+ procs.each { |ob| ob.call( *arguments ) } if procs
192
+ listeners.each { |l|
193
+ l.send( symbol.to_s, *arguments )
194
+ } if listeners
195
+ end
196
+
197
+ # The following methods are duplicates, but it is faster than using
198
+ # a helper
199
+ def get_procs( symbol, name )
200
+ return nil if @procs.size == 0
201
+ @procs.find_all do |sym, match, block|
202
+ #puts sym.inspect+"=="+symbol.inspect+ "\t"+match.inspect+"=="+name.inspect+ "\t"+( (sym.nil? or symbol == sym) and ((name.nil? and match.nil?) or match.nil? or ( (name == match) or (match.kind_of? Regexp and name =~ match)))).to_s
203
+ (
204
+ (sym.nil? or symbol == sym) and
205
+ ((name.nil? and match.nil?) or match.nil? or (
206
+ (name == match) or
207
+ (match.kind_of? Regexp and name =~ match)
208
+ )
209
+ )
210
+ )
211
+ end.collect{|x| x[-1]}
212
+ end
213
+ def get_listeners( symbol, name )
214
+ return nil if @listeners.size == 0
215
+ @listeners.find_all do |sym, match, block|
216
+ (
217
+ (sym.nil? or symbol == sym) and
218
+ ((name.nil? and match.nil?) or match.nil? or (
219
+ (name == match) or
220
+ (match.kind_of? Regexp and name =~ match)
221
+ )
222
+ )
223
+ )
224
+ end.collect{|x| x[-1]}
225
+ end
226
+
227
+ def add( pair )
228
+ if pair[-1].respond_to? :call
229
+ @procs << pair unless @procs.include? pair
230
+ else
231
+ @listeners << pair unless @listeners.include? pair
232
+ @has_listeners = true
233
+ end
234
+ end
235
+
236
+ def get_namespace( prefix )
237
+ uris = (@namespace_stack.find_all { |ns| not ns[prefix].nil? }) ||
238
+ (@namespace_stack.find { |ns| not ns[nil].nil? })
239
+ uris[-1][prefix] unless uris.nil? or 0 == uris.size
240
+ end
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,135 @@
1
+ require 'test/unit'
2
+ require 'test/unit/assertions'
3
+ currentPath = File.dirname(__FILE__)
4
+ require File.join( currentPath, '../lib/masterview' )
5
+
6
+ # Mixin class for testing directive implementations.
7
+ #
8
+ # Provides convenience services for constructing a template document element
9
+ # to use as a test fixture, constructing the directive handler for the
10
+ # directive being tested, and modelling the template document element
11
+ # processing events that a directive uses to hook up its handlers.
12
+ #
13
+ module DirectiveTestHelpers
14
+
15
+ MasterView::IOMgr.erb = MasterView::MIO::StringHashMIOTree.new({}, '.rhtml', :logging => true)
16
+ TestHelperRenderer = MasterView::TemplateProcessing::Renderer.new(:output_mio_tree => MasterView::IOMgr.erb)
17
+
18
+ MVCodeBaseDirDir = File.expand_path( File.join(File.dirname(__FILE__), '../lib/masterview' ) )
19
+ BuiltinDirectivesDir = "#{MasterView::ConfigSettings.mv_code_base_dir}/directives"
20
+ #put in some paranioa checking here...
21
+ if BuiltinDirectivesDir != "#{MVCodeBaseDirDir}/directives"
22
+ raise AssertionError, "MV dir resolution problem: MasterView::ConfigSettings.mv_code_base_dir=#{MasterView::ConfigSettings.mv_code_base_dir}, #{self.name}::BuiltinDirectivesDir=#{BuiltinDirectivesDir},"
23
+ end
24
+
25
+ # provide the equivalent of require 'appowner/directives/#{file_baseName}'
26
+ # for unit tests so that the directive loading happens in the proper context.
27
+ def self.load_directive(directive_file_name, directive_dir_path)
28
+ directive_file_path = "#{File.expand_path(directive_dir_path)}/#{directive_file_name}"
29
+ MasterView::DirectiveRegistry.current.load_directive_file( directive_file_path )
30
+ end
31
+
32
+ # provide the equivalent of require 'masterview/directives/#{file_baseName}'
33
+ # for unit tests so that the directive loading happens in the proper context
34
+ # for a built-in masterview directive in the standard mv: namespace.
35
+ def self.load_masterview_directive(directive_file_name)
36
+ directive_file_path = "#{BuiltinDirectivesDir}/#{directive_file_name}"
37
+ MasterView::DirectiveRegistry.current.load_directive_file( directive_file_path )
38
+ end
39
+
40
+ # brute force check everything that's supposed to be bolted down is in hardened metadata
41
+ def check_metadata(directive_class, expected_namespace, expected_attributeName)
42
+ check_metadata_keys(directive_class)
43
+ check_metadata_values(directive_class, expected_namespace, expected_attributeName)
44
+ end
45
+
46
+ # brute force check everything that's supposed to be bolted down is in hardened metadata
47
+ def check_metadata_values(directive_class, expected_namespace, expected_attributeName)
48
+ assert_equal expected_namespace, directive_class.namespace_name
49
+ assert_equal "#{expected_namespace}:", directive_class.namespace_prefix
50
+ assert_equal expected_attributeName, directive_class.attribute_name
51
+ assert_equal "#{expected_namespace}:#{expected_attributeName}", directive_class.attribute_qname
52
+ end
53
+
54
+ # brute force check everything that's supposed to be bolted down is in hardened metadata
55
+ def check_metadata_keys(directive_class)
56
+ dc_md = directive_class.metadata_values
57
+ expected_keys = MasterView::DirectiveMetadata::MARKUP_PROPERTY_NAMES
58
+ expected_keys.each { |key|
59
+ assert dc_md.has_key?(key), "MISSING MD KEY #{key.inspect}: #{dc_md.inspect}"
60
+ }
61
+ disallowed_keys = [ :namespace_name ]
62
+ disallowed_keys.each { |key|
63
+ assert ! dc_md.has_key?(key), "UNEXPECTED #{key.inspect} in metadata: #{dc_md.inspect}"
64
+ }
65
+ end
66
+
67
+ # create a template document processing element to use in a directive test
68
+ def create_template_element( tag_name='dummy', other_args={} )
69
+ attributes = other_args.fetch(:attributes, {})
70
+ mode_type = other_args.fetch(:mode_type, :normal)
71
+ parent_tag = other_args.fetch(:parent_tag, nil)
72
+ content = other_args.fetch(:content, nil)
73
+ @directives = MasterView::TemplateProcessing::DirectiveSet.new
74
+ @element_tag = MasterView::TemplateProcessing::Tag.new(@directives, tag_name, attributes, mode_type, parent_tag, nil, TestHelperRenderer)
75
+ #assert element_content == [], 'element can contain text chunks as well as child elements'
76
+ if content
77
+ if content.is_a? String
78
+ @element_tag.content << content
79
+ else
80
+ @element_tag.content.concat content
81
+ end
82
+ end
83
+ @element_tag
84
+ end
85
+
86
+ # construct a directive to test on the template element
87
+ def create_directive( directive_class, attr_value )
88
+ #??@directives = MasterView::TemplateProcessing::DirectiveSet.new
89
+ directive_instance = directive_class.new(attr_value)
90
+ @directives.directives.clear
91
+ @directives << directive_instance
92
+ directive_instance
93
+ end
94
+
95
+ def append_default_render_handler
96
+ @directives << MasterView::TemplateProcessing::SimpleRenderHandler.new
97
+ end
98
+
99
+ # Answer the directives configured to process the test element
100
+ # Ordinarily exactly one, the subject of the unit test
101
+ def element_directives
102
+ @directives.directives
103
+ end
104
+
105
+ # construct the test environment to evaluate the specified element processing event
106
+ def create_element_event_processor( event_name, context_values={} )
107
+ dcs = @directives.determine_dcs(event_name)
108
+ dcs.context = @element_tag.create_context( context_values )
109
+ dcs
110
+ end
111
+
112
+ # Evaluate the specified element processing event on the test element.
113
+ # Return the output contributed to the element rendering by this event.
114
+ def render_element_event( event_name, context_values={} )
115
+ dcs = create_element_event_processor( event_name, context_values )
116
+ render_result = dcs.render
117
+ render_result.flatten.join # assemble array of html chunks into final string
118
+ end
119
+
120
+ # answer the attributes hash for the element being tested
121
+ def element_attrs
122
+ @element_tag.attributes
123
+ end
124
+
125
+ # answer the value of the specified attribute of the element being tested
126
+ def element_attr_value( attr_name )
127
+ @element_tag.attributes[attr_name]
128
+ end
129
+
130
+ # answer the content of the element being tested
131
+ def element_content
132
+ @element_tag.content.flatten.join # assemble array of html chunks into final string
133
+ end
134
+
135
+ end
@@ -0,0 +1,18 @@
1
+ module MasterView
2
+ module DirectiveTests
3
+
4
+ # Example directive which does.. nothing
5
+ class IdCheck < MasterView::DirectiveBase
6
+
7
+ metadata :description => 'Silly example directive which checks id/name attributes'
8
+
9
+ ##attr_arg :foo,....
10
+ #event :stag do
11
+ #...something
12
+ #end
13
+
14
+ end
15
+
16
+ end
17
+ end
18
+
@@ -0,0 +1,70 @@
1
+
2
+ module MasterView
3
+ module DirectiveTests
4
+
5
+ class TestEventsDirective < MasterView::DirectiveBase
6
+
7
+ DEBUG_MD_LOADING = false
8
+
9
+ if DEBUG_MD_LOADING
10
+ STDOUT.puts "\n###DEBUG #{self.name} METADATA LOADING ###"
11
+ STDOUT.puts "...DirectiveMetadataRegistry (#{MasterView::DirectiveMetadata::DirectiveMetadataRegistry[self.name].object_id}): #{MasterView::DirectiveMetadata::DirectiveMetadataRegistry[self.name].inspect})"
12
+ STDOUT.puts "...before md decls: metadata_values=#{metadata_values.object_id}: #{metadata_values.inspect}"
13
+ end
14
+
15
+ metadata :namespace => 'mvtest', :attribute_name => 'test_events',
16
+ :priority => 'Low'
17
+
18
+ if DEBUG_MD_LOADING
19
+ STDOUT.puts "AFTER metadata DECLS:"
20
+ STDOUT.puts "...DirectiveMetadataRegistry (#{MasterView::DirectiveMetadata::DirectiveMetadataRegistry[self.name].object_id}): #{MasterView::DirectiveMetadata::DirectiveMetadataRegistry[self.name].inspect})"
21
+ STDOUT.puts "...after md decls: metadata_values #{metadata_values.object_id}: #{metadata_values.inspect}"
22
+ end
23
+
24
+ event :stag do
25
+ render ' stag '
26
+ render render_result
27
+ render ' stagend '
28
+ end
29
+
30
+ # sub-event notification w/in document content processing
31
+ def characters(dcs)
32
+ ret = []
33
+ ret << ' charsstart '
34
+ ret << dcs.render
35
+ ret << ' charsend '
36
+ end
37
+
38
+ # sub-event notification w/in document content processing
39
+ def comment(dcs)
40
+ ret = []
41
+ ret << ' commentstart '
42
+ ret << dcs.render
43
+ ret << ' commentend '
44
+ end
45
+
46
+ # sub-event notification w/in document content processing
47
+ def cdata(dcs)
48
+ ret = []
49
+ ret << ' cdatastart '
50
+ ret << dcs.render
51
+ ret << ' cdataend '
52
+ end
53
+
54
+ event :etag do
55
+ render ' etag '
56
+ render render_result
57
+ render ' etagend '
58
+ end
59
+
60
+ event :before_child_title_stag do
61
+ render ' before_child_title_stag '
62
+ end
63
+
64
+ event :child_title_content do
65
+ render attr_value
66
+ end
67
+
68
+ end
69
+ end
70
+ end
data/test/test_helper.rb CHANGED
@@ -5,21 +5,34 @@ require File.join( currentPath, '../lib/masterview' )
5
5
 
6
6
  MasterView::OmitGeneratedComments = true #turn off the generated comments to make it easier for us to test
7
7
 
8
+ module MasterView
9
+ module ParserMock
10
+ def self.createMockDCS
11
+ dcs = OpenStruct.new
12
+ dcs.context = {}
13
+ dcs.context[:tag] = OpenStruct.new
14
+ dcs.context[:tag].attributes = CaseInsensitiveHash.new
15
+ dcs
16
+ end
17
+ end
18
+ end
8
19
 
9
20
  module Test
10
21
  module Unit
11
22
  module Assertions
12
- def assert_template_result(expected, template, options={}, message=nil)
23
+ def assert_template_result(expected, template, options={}, message=nil, normalize_white_space=true)
13
24
  #options[:template_pathname] = Pathname.for_path(options.delete(:template_path)) if options[:template_path]
14
25
  output_hash = {}
15
26
  full_options = options.merge( { :output_mio_tree => MasterView::IOMgr.erb } )
16
27
  MasterView::Parser.parse(template, full_options)
17
- MasterView::IOMgr.erb.string_hash.each do |key,value|
18
- value.gsub!( /([^=]>)\s+/, '\1' ) #remove whitespace after tag but not =>
19
- value.gsub!( /(\s{2,}|\n\s*)/, ' ') #change double space or newline and spaces into single space
28
+ if normalize_white_space
29
+ MasterView::IOMgr.erb.string_hash.each do |key,value|
30
+ value.gsub!( /([^=]>)\s+/, '\1' ) #remove whitespace after tag but not =>
31
+ value.gsub!( /(\s{2,}|\n\s*)/, ' ') #change double space or newline and spaces into single space
32
+ end
20
33
  end
21
34
  assert_equal expected, MasterView::IOMgr.erb.string_hash, message
22
- end
35
+ end
23
36
  end
24
37
  end
25
38
  end
@@ -1,35 +1,35 @@
1
1
  <html>
2
2
  <head>
3
3
  <title>Product: <%= controller.action_name %></title>
4
- <%= stylesheet_link_tag 'scaffold' %>
4
+ <%= stylesheet_link_tag( 'scaffold' ) %>
5
5
 
6
6
  <!--
7
7
  These stylesheets below are for design time use to allow working with only one visible section at a time, especially
8
8
  when trying to use absolute positioning. You may comment/uncomment which ever section you wish to work with.
9
9
  You may comment all of them out to work with all at design time. These stylesheets will not be used at runtime
10
10
  -->
11
-
11
+ <%= %>
12
12
  <!-- <link rel="stylesheet" type="text/css" href="./extra/show_only_edit.css" mv:replace=""/> -->
13
13
  <!-- <link rel="stylesheet" type="text/css" href="./extra/show_only_show.css" mv:replace=""/> -->
14
14
  <!-- <link rel="stylesheet" type="text/css" href="./extra/show_only_list.css" mv:replace=""/> -->
15
15
  <!-- <link rel="stylesheet" type="text/css" href="./extra/show_only_destroy.css" mv:replace=""/> -->
16
16
 
17
- <%= javascript_include_tag :defaults %>
18
-
19
-
20
-
21
-
17
+ <%= javascript_include_tag( :defaults ) %>
18
+ <%= %>
19
+ <%= %>
20
+ <%= %>
21
+ <%= %>
22
22
  </head>
23
23
  <body>
24
24
 
25
- <% if @flash[:notice] %><div class="messages"><%= @flash[:notice] %></div><% end %>
25
+ <% if( @flash[:notice] ) -%><div class="messages"><%= @flash[:notice] %></div><% end -%>
26
26
 
27
27
  <div class="main">
28
28
  <%= @content_for_layout %>
29
29
  </div>
30
30
 
31
-
32
-
31
+ <%= %>
32
+ <%= %>
33
33
 
34
34
  </body>
35
35
  </html>
@@ -6,22 +6,22 @@
6
6
 
7
7
  <div class="record">
8
8
  <div class="label"><label for="product_name">Name:</label></div>
9
- <div class="field"><%= text_field 'product', 'name' %></div>
9
+ <div class="field"><%= text_field( 'product', 'name' ) %></div>
10
10
  </div>
11
11
 
12
12
  <div class="record">
13
13
  <div class="label"><label for="product_description">Description:</label></div>
14
- <div class="field"><%= text_area 'product', 'description', :rows => 5 %></div>
14
+ <div class="field"><%= text_area( 'product', 'description', :rows => 5 ) %></div>
15
15
  </div>
16
16
 
17
17
  <div class="record">
18
18
  <div class="label"><label for="product_cost">Cost:</label></div>
19
- <div class="field"><%= text_field 'product', 'cost' %></div>
19
+ <div class="field"><%= text_field( 'product', 'cost' ) %></div>
20
20
  </div>
21
21
 
22
22
  <div class="record">
23
23
  <div class="label"><label for="product_price">Price:</label></div>
24
- <div class="field"><%= text_field 'product', 'price' %></div>
24
+ <div class="field"><%= text_field( 'product', 'price' ) %></div>
25
25
  </div>
26
26
 
27
27
  </div>
@@ -5,9 +5,9 @@
5
5
  <td class="td_description"><%= h product.send(:description) %></td>
6
6
  <td class="td_cost"><%= h product.send(:cost) %></td>
7
7
  <td class="td_price"><%= h product.send(:price) %></td>
8
- <td><%= link_to 'Show', :action => 'show', :id => product %></td>
9
- <td><%= link_to 'Edit', :action => 'edit', :id => product %></td>
10
- <td><%= link_to 'Destroy', :action => 'destroy', :id => product %></td>
8
+ <td><%= link_to( 'Show', { :action => 'show', :id => product }, :class => "show_link" ) %></td>
9
+ <td><%= link_to( 'Edit', { :action => 'edit', :id => product }, :class => "edit_link" ) %></td>
10
+ <td><%= link_to( 'Destroy', { :action => 'destroy', :id => product }, :class => "destroy_link" ) %></td>
11
11
 
12
12
  <!-- [eolistline:product] -->
13
13
 
@@ -2,8 +2,8 @@
2
2
  <div class="product_destroy sidebar">
3
3
  <h1>Tasks:</h1>
4
4
  <ul>
5
- <li><%= link_to 'Back to overview', :action => 'list' %></li>
6
- <li><%= link_to 'Show this product', :action => 'show', :id => @product.id %></li>
5
+ <li><%= link_to( 'Back to overview', { :action => 'list' }, :class => "list_link" ) %></li>
6
+ <li><%= link_to( 'Show this product', { :action => 'show', :id => @product.id }, :class => "show_link" ) %></li>
7
7
  </ul>
8
8
  </div>
9
9
 
@@ -14,14 +14,14 @@
14
14
 
15
15
  <div class="messages">Are you sure you want to delete this item?</div>
16
16
  <br />
17
- <%= form_tag :action => 'destroy', :id => @product.id %>
17
+ <%= form_tag( :action => 'destroy', :id => @product.id ) %>
18
18
  <%= render :partial => 'show' %>
19
19
 
20
20
  <br />
21
21
  <div class="product_edit operations">
22
- <%= submit_tag 'Delete' %>
22
+ <%= submit_tag( 'Delete', :class => "save_button" ) %>
23
23
  <input class="cancel_button" onclick="window.location.href = '<%= url_for :action => %q{list} %>';" style="width: auto;" type="button" value="Cancel" />
24
24
  </div>
25
- <%= end_form_tag %>
25
+ </form>
26
26
  </div>
27
27
  </div>
@@ -2,14 +2,14 @@
2
2
  <div class="product_edit sidebar">
3
3
  <h1>Tasks:</h1>
4
4
  <ul>
5
- <li><%= link_to 'Back to overview', :action => 'list' %></li>
5
+ <li><%= link_to( 'Back to overview', { :action => 'list' }, :class => "list_link" ) %></li>
6
6
  </ul>
7
7
  </div>
8
8
 
9
9
  <div class="product_edit content">
10
10
  <h1>Products</h1>
11
11
 
12
- <%= form_tag :action => 'edit', :id => @product.id %>
12
+ <%= form_tag( :action => 'edit', :id => @product.id ) %>
13
13
 
14
14
  <div class="form">
15
15
  <h2>Editing product</h2>
@@ -18,9 +18,9 @@
18
18
 
19
19
  <br />
20
20
  <div class="product_edit operations">
21
- <%= submit_tag 'OK' %>
21
+ <%= submit_tag( 'OK', :class => "primary save_button" ) %>
22
22
  <input class="cancel_button" onclick="window.location.href = '<%= url_for :action => %q{list} %>';" style="width: auto;" type="button" value="Cancel" /> </div>
23
23
 
24
- <%= end_form_tag %>
24
+ </form>
25
25
  </div>
26
26
  </div>