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.
- data/CHANGELOG +31 -1
- data/README +70 -69
- data/RELEASE_NOTES +70 -64
- data/Rakefile +26 -27
- data/TODO +13 -29
- data/doc/about.html +246 -0
- data/doc/configuration.html +49 -36
- data/doc/developer.html +423 -41
- data/doc/directives.html +139 -51
- data/doc/guide.html +19 -9
- data/doc/index.html +90 -224
- data/doc/installation.html +36 -28
- data/doc/media_list.html +30 -20
- data/doc/simple_diagram.html +3 -5
- data/doc/stylesheets/masterview.css +16 -1
- data/examples/rails_app_config/masterview/settings.rb +2 -1
- data/init.rb +1 -1
- data/lib/#ChangeLog# +6 -0
- data/lib/masterview/analyzer.rb +48 -34
- data/lib/masterview/attr_string_parser.rb +5 -1
- data/lib/masterview/case_insensitive_hash.rb +69 -0
- data/lib/masterview/{pathname_extensions.rb → core_ext/pathname.rb} +0 -0
- data/lib/masterview/{string_extensions.rb → core_ext/string.rb} +0 -0
- data/lib/masterview/deprecated/directive_base.rb +362 -0
- data/lib/masterview/directive_base.rb +201 -179
- data/lib/masterview/directive_dsl.rb +457 -0
- data/lib/masterview/directive_helpers.rb +28 -141
- data/lib/masterview/directive_load_path.rb +388 -0
- data/lib/masterview/directive_metadata.rb +377 -0
- data/lib/masterview/directive_registry.rb +259 -69
- data/lib/masterview/directives/.metadata +16 -0
- data/lib/masterview/directives/attr.rb +9 -8
- data/lib/masterview/directives/block.rb +11 -14
- data/lib/masterview/directives/check_box.rb +13 -18
- data/lib/masterview/directives/collection_select.rb +15 -29
- data/lib/masterview/directives/content.rb +9 -3
- data/lib/masterview/directives/else.rb +15 -13
- data/lib/masterview/directives/elsif.rb +14 -13
- data/lib/masterview/directives/eval.rb +20 -0
- data/lib/masterview/directives/form.rb +56 -9
- data/lib/masterview/directives/form_remote.rb +26 -0
- data/lib/masterview/directives/global_inline_erb.rb +10 -14
- data/lib/masterview/directives/hidden_field.rb +11 -20
- data/lib/masterview/directives/if.rb +13 -12
- data/lib/masterview/directives/image_tag.rb +20 -28
- data/lib/masterview/directives/import.rb +5 -12
- data/lib/masterview/directives/import_render.rb +7 -19
- data/lib/masterview/directives/insert_generated_comment.rb +8 -11
- data/lib/masterview/directives/javascript_include.rb +21 -12
- data/lib/masterview/directives/link_to.rb +14 -8
- data/lib/masterview/directives/link_to_function.rb +22 -0
- data/lib/masterview/directives/link_to_if.rb +15 -13
- data/lib/masterview/directives/link_to_remote.rb +13 -8
- data/lib/masterview/directives/omit_tag.rb +32 -16
- data/lib/masterview/directives/password_field.rb +10 -22
- data/lib/masterview/directives/radio_button.rb +11 -22
- data/lib/masterview/directives/replace.rb +7 -8
- data/lib/masterview/directives/select.rb +11 -24
- data/lib/masterview/directives/stylesheet_link.rb +20 -12
- data/lib/masterview/directives/submit.rb +11 -5
- data/lib/masterview/directives/text_area.rb +10 -23
- data/lib/masterview/directives/text_field.rb +10 -22
- data/lib/masterview/exceptions.rb +21 -0
- data/lib/masterview/extras/app/controllers/masterview_controller.rb +102 -75
- data/lib/masterview/extras/app/views/layouts/masterview_admin.rhtml +24 -23
- data/lib/masterview/extras/app/views/layouts/masterview_admin_config.rhtml +81 -0
- data/lib/masterview/extras/app/views/masterview/admin/configuration.rhtml +5 -1
- data/lib/masterview/extras/app/views/masterview/admin/create.rhtml +2 -2
- data/lib/masterview/extras/app/views/masterview/admin/directives.rhtml +5 -0
- data/lib/masterview/extras/app/views/masterview/admin/features.rhtml +5 -79
- data/lib/masterview/extras/app/views/masterview/admin/interact.rhtml +5 -0
- data/lib/masterview/extras/app/views/masterview/admin/list.rhtml +3 -71
- data/lib/masterview/extras/init_mv_admin_pages.rb +42 -23
- data/lib/masterview/filter_helpers.rb +26 -0
- data/lib/masterview/initializer.rb +99 -53
- data/lib/masterview/io.rb +19 -15
- data/lib/masterview/keyword_expander.rb +7 -2
- data/lib/masterview/masterview_info.rb +229 -23
- data/lib/masterview/masterview_version.rb +2 -2
- data/lib/masterview/parser.rb +275 -105
- data/lib/masterview/parser_helpers.rb +54 -0
- data/lib/masterview/rails_ext/action_controller_erb_direct.rb +29 -0
- data/lib/masterview/rails_ext/action_controller_reparse_checking.rb +27 -0
- data/lib/masterview/{extras/init_rails_erb_mv_direct.rb → rails_ext/action_view_erb_direct.rb} +12 -59
- data/lib/masterview/template_spec.rb +3 -2
- data/lib/masterview.rb +21 -12
- data/lib/rexml/parsers/baseparser_with_doctype_fix.rb +473 -0
- data/lib/rexml/parsers/sax2parser_with_doctype_fix.rb +243 -0
- data/test/directive_test_helper.rb +135 -0
- data/test/fixtures/directives/id_check.rb +18 -0
- data/test/fixtures/directives/test_directive_events.rb +70 -0
- data/test/test_helper.rb +18 -5
- data/test/tmp/views/layouts/product.rhtml +10 -10
- data/test/tmp/views/product/_form.rhtml +4 -4
- data/test/tmp/views/product/_product.rhtml +3 -3
- data/test/tmp/views/product/destroy.rhtml +5 -5
- data/test/tmp/views/product/edit.rhtml +4 -4
- data/test/tmp/views/product/list.rhtml +3 -3
- data/test/tmp/views/product/new.rhtml +4 -4
- data/test/tmp/views/product/show.rhtml +2 -2
- data/test/unit/attr_string_parser_test.rb +105 -0
- data/test/unit/case_insensitive_hash_mod_test.rb +104 -0
- data/test/unit/config_settings_test.rb +13 -1
- data/test/unit/default_generate_mio_filter_test.rb +3 -3
- data/test/unit/deprecated_directive_base_test.rb +30 -0
- data/test/unit/directive_attr_test.rb +111 -35
- data/test/unit/directive_base_test.rb +520 -1
- data/test/unit/directive_block_test.rb +30 -22
- data/test/unit/directive_content_test.rb +24 -11
- data/test/unit/directive_else_test.rb +18 -15
- data/test/unit/directive_elsif_test.rb +17 -15
- data/test/unit/directive_form_remote_test.rb +59 -0
- data/test/unit/directive_form_test.rb +31 -39
- data/test/unit/directive_global_inline_erb_test.rb +28 -17
- data/test/unit/directive_grid_test_notready.rb +38 -0
- data/test/unit/directive_helpers_test.rb +39 -0
- data/test/unit/directive_hidden_field_test.rb +44 -29
- data/test/unit/directive_if_test.rb +10 -7
- data/test/unit/directive_image_tag_test.rb +69 -61
- data/test/unit/directive_import_render_test.rb +28 -38
- data/test/unit/directive_import_test.rb +16 -14
- data/test/unit/directive_insert_generated_comment_test.rb +32 -0
- data/test/unit/directive_javascript_include_test.rb +40 -43
- data/test/unit/directive_link_to_function_test.rb +40 -0
- data/test/unit/directive_link_to_if_test.rb +52 -12
- data/test/unit/directive_link_to_remote_test.rb +58 -0
- data/test/unit/directive_link_to_test.rb +46 -31
- data/test/unit/directive_load_path_test.rb +257 -0
- data/test/unit/directive_metadata_test.rb +313 -0
- data/test/unit/directive_omit_tag_test.rb +73 -21
- data/test/unit/directive_password_field_test.rb +44 -38
- data/test/unit/directive_registry_test.rb +44 -0
- data/test/unit/directive_replace_test.rb +28 -12
- data/test/unit/directive_stylesheet_link_test.rb +43 -36
- data/test/unit/directive_submit_test.rb +29 -30
- data/test/unit/directive_text_area_test.rb +40 -36
- data/test/unit/directive_text_field_test.rb +44 -38
- data/test/unit/example_directive_child_events_test.rb +41 -0
- data/test/unit/example_test.rb +31 -4
- data/test/unit/file_mio_test.rb +18 -13
- data/test/unit/filter_helpers_test.rb +10 -8
- data/test/unit/find_directive_parent_test.rb +174 -0
- data/test/unit/keyword_expander_test.rb +4 -2
- data/test/unit/mio_test.rb +18 -11
- data/test/unit/mtime_string_hash_mio_tree_test.rb +5 -1
- data/test/unit/parser_test.rb +41 -29
- data/test/unit/pathname_extensions_test.rb +1 -1
- data/test/unit/run_parser_test.rb +2 -2
- data/test/unit/simplified_directive_base_test.rb +256 -0
- data/test/unit/string_hash_mio_test.rb +5 -1
- data/test/unit/template_file_watcher_test.rb +2 -2
- data/test/unit/template_test.rb +221 -46
- metadata +86 -45
- data/lib/masterview/directives/testfilter.rb +0 -55
- 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
|
-
|
18
|
-
|
19
|
-
|
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]
|
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
|
-
|
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
|
-
|
24
|
+
</form>
|
25
25
|
</div>
|
26
26
|
</div>
|