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
@@ -10,25 +10,146 @@ module MasterView
|
|
10
10
|
#
|
11
11
|
class DirectiveRegistry
|
12
12
|
|
13
|
-
|
13
|
+
DEBUG_TRACE_LOADING = false #:nodoc: ##DEBUG##
|
14
|
+
TRACE_MD_HARDENING_SUBJECT = nil #'MasterView::DirectiveTests::TestEventsDirective'
|
15
|
+
DEBUG_BUILTIN_DIRECTIVE_REGISTRATION = true #:nodoc:
|
16
|
+
DEBUG_CURRENT = false #:nodoc:
|
17
|
+
|
18
|
+
# The DirectiveRegistry for managing the loaded directives available
|
19
|
+
# for processing template markup.
|
20
|
+
# #
|
21
|
+
# DirectiveRegistry.current is ordinarily configured to load
|
22
|
+
# the directives registered on the DirectiveLoadPath.current.
|
23
|
+
def self.current
|
24
|
+
@@current
|
25
|
+
end
|
26
|
+
|
27
|
+
# Set the current directives registry for template processing.
|
28
|
+
# Ordinarily done once during masterview initialization.
|
29
|
+
def self.current=(registry) #:nodoc:
|
30
|
+
if self.class_variables.include?('@@current') && @@current && registry
|
31
|
+
# HACK: need to preserve the list of loaded classes.
|
32
|
+
# this shouldn't ordinarily happen, but repeated class loading
|
33
|
+
# occurs during, ah, say... test suite runs, yes!!
|
34
|
+
registry.update_loaded_classes_hack( @@current.loaded_classes )
|
35
|
+
end
|
36
|
+
@@current = registry
|
37
|
+
if DEBUG_CURRENT
|
38
|
+
STDOUT.puts "\n****#{self.name}.current set: #{current.object_id}"
|
39
|
+
STDOUT.puts "...#{current.inspect}\n"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def update_loaded_classes_hack(already_loaded_classes) #:nodoc:
|
44
|
+
@loaded_classes.concat( already_loaded_classes )
|
45
|
+
end
|
46
|
+
|
47
|
+
# Register default namespaces for directive metadata
|
48
|
+
# Should be invoked once during MasterView initialization
|
49
|
+
def self.register_default_namespaces(ns_prefix_masterview, ns_prefix_extensions) #:nodoc:
|
50
|
+
raise ArgumentError, "Invalid masterview namespace prefix '#{ns_prefix_masterview}'" if ns_prefix_masterview[-1..-1] != ':'
|
51
|
+
raise ArgumentError, "Invalid extensions namespace prefix '#{ns_prefix_extensions}'" if ns_prefix_extensions[-1..-1] != ':'
|
52
|
+
@@metadata_defaults_masterview = {
|
53
|
+
:namespace => ns_prefix_masterview[0...-1],
|
54
|
+
:namespace_prefix => ns_prefix_masterview,
|
55
|
+
}
|
56
|
+
@@metadata_defaults_extensions = {
|
57
|
+
:namespace => ns_prefix_extensions[0...-1],
|
58
|
+
:namespace_prefix => ns_prefix_extensions,
|
59
|
+
}
|
60
|
+
#assert NotReallyNecessary, 'because we'll just make sure we code up the right stuff here'
|
61
|
+
DirectiveMetadata.validate_metadata_props! @@metadata_defaults_masterview
|
62
|
+
DirectiveMetadata.validate_metadata_props! @@metadata_defaults_extensions
|
63
|
+
end
|
64
|
+
|
65
|
+
# Answer the namespace prefix of directives in the standard MasterView namespace.
|
66
|
+
def mv_namespace_prefix
|
67
|
+
##ISSUE: needs work?? Does this need to be an inst var of the registry??
|
68
|
+
@@metadata_defaults_masterview[:namespace_prefix]
|
69
|
+
end
|
70
|
+
|
71
|
+
# Answer the namespace prefix of directives in the default MasterView
|
72
|
+
# extensions directives namespace.
|
73
|
+
def mv_extensions_namespace_prefix
|
74
|
+
##ISSUE: needs work?? Does this need to be an inst var of the registry??
|
75
|
+
@@metadata_defaults_extensions[:namespace_prefix]
|
76
|
+
end
|
77
|
+
|
78
|
+
# Answer the list of directive classes which are loaded in the current configuration.
|
14
79
|
attr_reader :loaded_classes
|
15
80
|
|
16
|
-
|
81
|
+
# Answer a list of all namespaces in use
|
82
|
+
def loaded_namespaces
|
83
|
+
@directive_namespaces #??.clone for paranoid safety?
|
84
|
+
end
|
85
|
+
|
86
|
+
# Answer the fully-qualified names of the registered directives
|
87
|
+
def registered_directive_names
|
88
|
+
@directive_classes.keys
|
89
|
+
end
|
90
|
+
|
91
|
+
# Answer the the registered directives
|
92
|
+
def registered_directives
|
93
|
+
@directive_classes.values
|
94
|
+
end
|
95
|
+
|
96
|
+
# Hash containing default directive metadata properties
|
97
|
+
# Used during directive path loading to exploit default config specs.
|
98
|
+
def metadata_defaults #:nodoc:
|
99
|
+
@@metadata_defaults_masterview
|
100
|
+
end
|
101
|
+
|
102
|
+
# Hash containing default directive metadata properties
|
103
|
+
# Used during directive path loading to exploit default config specs.
|
104
|
+
def metadata_defaults_extensions #:nodoc:
|
105
|
+
@@metadata_defaults_extensions
|
106
|
+
end
|
107
|
+
|
108
|
+
# internal mechanism to provide directory/path load context during directive class loading
|
109
|
+
attr_reader :load_context #:nodoc:
|
110
|
+
def set_load_context(context_settings, check_for_conflict=true) #:nodoc:
|
111
|
+
if check_for_conflict && context_settings && @load_context
|
112
|
+
raise RuntimeError, "Directive load operation already in progress: current context=#{@load_context.inspect}, requested context=#{context_settings.inspect}"
|
113
|
+
end
|
114
|
+
@load_context = context_settings
|
115
|
+
end
|
116
|
+
|
117
|
+
def initialize()
|
17
118
|
@loaded_classes = []
|
119
|
+
set_load_context( nil )
|
18
120
|
clear_directive_maps()
|
121
|
+
#STDOUT.puts "\n###Created DirectiveRegistry=#{self.object_id}"
|
122
|
+
#STDOUT.puts "...loaded_classes=#{self.loaded_classes.inspect}"
|
123
|
+
#STDOUT.puts "...load_context=#{self.load_context.inspect}"
|
124
|
+
end
|
125
|
+
|
126
|
+
protected
|
127
|
+
def clear_directive_maps() #:nodoc:
|
128
|
+
@directive_namespaces = []
|
129
|
+
@directive_classes = {} # map fully qualified directive attr name to directive_class
|
130
|
+
@global_directives = [] # conditional global directives
|
131
|
+
@auto_directives = [] # unconditional global directives
|
19
132
|
end
|
133
|
+
public #nasty modal access specification mechanism
|
20
134
|
|
21
135
|
# Register a directive implementation.
|
22
136
|
#
|
23
137
|
# A directive is ordinarily a subclass of MasterView::DirectiveBase.
|
24
138
|
#
|
25
|
-
#--
|
26
|
-
#TODO: A directive implementation must support the following services:....
|
27
|
-
#++
|
28
|
-
#
|
29
139
|
def register_directive(directive_class)
|
30
|
-
#
|
140
|
+
# harden the DirectiveMetadata by filling in unspecified values from defaults
|
141
|
+
#assert directive_class.ancestors.include? DirectiveMetadata
|
142
|
+
##AARGH: can't do this yet, the timing is premature [DJL 06-Oct-2006]
|
143
|
+
#PUNT: md_defaults = load_context.nil? ? metadata_defaults_extensions : load_context[:metadata_defaults]
|
144
|
+
#PUNT: directive_class.harden_metadata( md_defaults )
|
31
145
|
@loaded_classes << directive_class
|
146
|
+
##DEBUG##
|
147
|
+
if TRACE_MD_HARDENING_SUBJECT and directive_class.name == TRACE_MD_HARDENING_SUBJECT
|
148
|
+
STDOUT.puts "\nREGISTERED DIRECTIVE #{directive_class.name}"
|
149
|
+
STDOUT.puts "...TOO SOON TO HARDEN METADATA"
|
150
|
+
STDOUT.puts "...#{directive_class.metadata_values.object_id}: #{directive_class.metadata_values.inspect}\n"
|
151
|
+
end
|
152
|
+
##DEBUG##
|
32
153
|
end
|
33
154
|
|
34
155
|
# Answer the (base) names of the loaded directive classes.
|
@@ -47,109 +168,178 @@ module MasterView
|
|
47
168
|
dc.name.split(':').last
|
48
169
|
end
|
49
170
|
|
50
|
-
# Answer the attribute name implemented by a directive class
|
51
|
-
def directive_attr_name( dc )
|
52
|
-
dc.respond_to?(:attr_name) ? dc.attr_name : simple_class_name(dc).downcase_first_letter
|
53
|
-
end
|
54
|
-
|
55
|
-
# Answer the fully qualified name of the attribute implemented by a directive class.
|
56
|
-
# attr_qname ::= <ns_name>:<attr_name>
|
57
|
-
def directive_full_attr_name( dc, mv_ns )
|
58
|
-
(dc.respond_to? :full_attr_name) ? dc.full_attr_name(mv_ns) : build_full_attribute_name(mv_ns, dc)
|
59
|
-
end
|
60
|
-
|
61
|
-
# this method is invoked to build the full attribute name (the attribute which will be watched for in
|
62
|
-
# html attibutes. It concatenates the namespace prefix to the class name after first removing any module
|
63
|
-
# prefixes and then downcasing the first letter
|
64
|
-
def build_full_attribute_name(mv_ns, dc) #:nodoc:
|
65
|
-
#ISSUE: need to allow directives to control their name space
|
66
|
-
mv_ns + simple_class_name(dc).downcase_first_letter
|
67
|
-
end
|
68
|
-
|
69
171
|
# Ensure that all directives on the load path are loaded.
|
70
172
|
# Build the directive processing tables used by the template parser.
|
71
173
|
#
|
72
|
-
def process_directives_load_path(
|
73
|
-
|
74
|
-
|
174
|
+
def process_directives_load_path( load_path=nil )
|
175
|
+
load_path = MasterView::DirectiveLoadPath.current if load_path.nil?
|
176
|
+
load_directives( load_path )
|
177
|
+
build_directive_maps
|
75
178
|
end
|
76
179
|
|
77
180
|
# Ensure that all directives on the load path are loaded.
|
78
181
|
#
|
79
182
|
# require {directives_dir}/foo_directive.rb
|
80
|
-
def load_directives(
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
if
|
86
|
-
|
183
|
+
def load_directives( load_path=nil ) #:nodoc:
|
184
|
+
load_path = MasterView::DirectiveLoadPath.current if load_path.nil?
|
185
|
+
STDOUT.puts "\n-------- LOAD DIRECTIVES ON PATH -------" if DEBUG_TRACE_LOADING
|
186
|
+
load_path.each do | dpe |
|
187
|
+
STDOUT.puts "DIRECTORY PATH ENTRY: #{dpe.inspect}" if DEBUG_TRACE_LOADING
|
188
|
+
if dpe.exists?
|
189
|
+
self.load_from_directive_path_entry( dpe )
|
87
190
|
else
|
88
|
-
#raise InvalidPathError.new('Directive load path dir does not exist:'+
|
89
|
-
Log.error "Directive load path dir does not exist: '#{
|
191
|
+
#raise InvalidPathError.new('Directive load path dir does not exist:'+dir_path)
|
192
|
+
Log.error "Directive load path dir does not exist: '#{dpe.dir_path}'" if MasterView.const_defined?(:Log) #backstop for test case startup
|
90
193
|
end
|
91
194
|
end
|
92
|
-
clear_directive_maps() # ensure we take a clean point of view on
|
195
|
+
clear_directive_maps() # ensure we take a clean point of view on whatever just got loaded
|
196
|
+
STDOUT.puts "------ END LOAD DIRECTIVES ------" if DEBUG_TRACE_LOADING
|
197
|
+
end
|
198
|
+
|
199
|
+
# Load a specific directive implementation class
|
200
|
+
#
|
201
|
+
# Mainly provided for use by unit tests
|
202
|
+
def load_directive_file(directive_file_path)
|
203
|
+
dir_path = File.dirname(directive_file_path)
|
204
|
+
dpe = MasterView::DirectiveLoadPath::PathEntry.new(dir_path)
|
205
|
+
load_from_directive_path_entry( dpe, directive_file_path )
|
93
206
|
end
|
94
207
|
|
208
|
+
def load_from_directive_path_entry(dpe, directive_file_path=nil) #:nodoc:
|
209
|
+
dir_md_specs = dpe.load_metadata_specs # :create_if_not_defined => true
|
210
|
+
is_mv_directives_dir = dir_md_specs.fetch(:use_masterview_namespace, false)
|
211
|
+
app_md_defaults = is_mv_directives_dir ? metadata_defaults : metadata_defaults_extensions
|
212
|
+
md_defaults = MasterView::DirectiveLoadPath.compute_md_defaults(
|
213
|
+
app_md_defaults,
|
214
|
+
dir_md_specs[:default],
|
215
|
+
dpe.metadata_defaults )
|
216
|
+
begin
|
217
|
+
dir_load_context = { :metadata_defaults => md_defaults, }
|
218
|
+
set_load_context( dir_load_context )
|
219
|
+
if directive_file_path
|
220
|
+
# load specific file from a directive path entry directory
|
221
|
+
dir_load_context[:directive_file] = directive_file_path
|
222
|
+
load_single_directive_file(directive_file_path, md_defaults) #WAS: require directive_file_path
|
223
|
+
else
|
224
|
+
# load all directives in the directory
|
225
|
+
dir_path = dpe.dir_path
|
226
|
+
#??Log.debug { "Loading directives from #{dir_path} with metadata defaults #{md_defaults.inspect}" }
|
227
|
+
#??load_context[:synonyms] = dir_md_specs.fetch(:synonyms, {})??
|
228
|
+
#??files_to_ignore = dir_md_specs.fetch(:ignore, []).collect { |fn| fn.ends_with?('.rb') ? fn : "#{fn}.rb" }
|
229
|
+
Dir.entries( dir_path ).each { |fn|
|
230
|
+
# we assume all .rb files in a directives dir are directive impls
|
231
|
+
if fn =~ /[.]rb$/ #?? && ! files_to_ignore.include?(fn)
|
232
|
+
directive_file_path = "#{dir_path}/#{fn}"
|
233
|
+
dir_load_context[:directive_file] = directive_file_path
|
234
|
+
#??@load_context[:directive_synonyms] = dir_synonyms_specs.fetch(fn, [])??
|
235
|
+
load_single_directive_file(directive_file_path, md_defaults) #WAS: require directive_file_path
|
236
|
+
end
|
237
|
+
}
|
238
|
+
end
|
239
|
+
ensure
|
240
|
+
set_load_context( nil )
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
protected
|
245
|
+
# we can't to this in register_directive as desired because
|
246
|
+
# the class defn hasn't been processed yet at that point, it seems.
|
247
|
+
def load_single_directive_file(directive_file_path, md_defaults)
|
248
|
+
last_class_loaded = @loaded_classes.last
|
249
|
+
require directive_file_path
|
250
|
+
if @loaded_classes.last != last_class_loaded
|
251
|
+
directive_class = @loaded_classes.last
|
252
|
+
directive_class.harden_metadata( md_defaults )
|
253
|
+
##DEBUG##
|
254
|
+
if TRACE_MD_HARDENING_SUBJECT and directive_class.name == TRACE_MD_HARDENING_SUBJECT
|
255
|
+
STDOUT.puts "\nFINISHED LOADING DIRECTIVE #{directive_class.name}"
|
256
|
+
STDOUT.puts "...SHOULD HAVE HARDENED METADATA NOW:"
|
257
|
+
STDOUT.puts "...#{directive_class.metadata_values.object_id}: #{directive_class.metadata_values.inspect}\n"
|
258
|
+
STDOUT.puts ""
|
259
|
+
end
|
260
|
+
##DEBUG##
|
261
|
+
end
|
262
|
+
end
|
263
|
+
public #nasty modal access specification mechanism
|
264
|
+
|
95
265
|
# Build the directive processing tables used by the template parser.
|
96
|
-
def build_directive_maps(
|
266
|
+
def build_directive_maps() #:nodoc:
|
97
267
|
|
98
|
-
mv_ns = MasterView::NamespacePrefix if mv_ns.nil?
|
99
268
|
clear_directive_maps() # ensure we take a clean point of view on the matter at hand
|
100
269
|
|
101
270
|
Log.debug { 'directive plugins loaded:' + loaded_class_names.inspect } if MasterView.const_defined?(:Log) #backstop for test case startup
|
102
271
|
loaded_classes.each do |dc|
|
103
|
-
dc.on_load if dc.respond_to?(:on_load)
|
104
|
-
attr_qname =
|
105
|
-
|
106
|
-
raise NameError, "Directive qname requires namespace: '#{attr_qname} - #{dc.name}" if
|
107
|
-
|
272
|
+
dc.on_load if dc.respond_to?(:on_load) #ISSUE: needs review (timing, intent) [DJL 08-Oct-2006]
|
273
|
+
attr_qname = dc.attribute_qname
|
274
|
+
ns_name = dc.namespace_name
|
275
|
+
raise NameError, "Directive qname requires namespace: '#{attr_qname} - #{dc.name}" if ns_name.nil? || ns_name.strip().empty?
|
276
|
+
if DEBUG_BUILTIN_DIRECTIVE_REGISTRATION and dc.name.starts_with?('MasterView::Directives::')
|
277
|
+
if ns_name != 'mv' #! attr_qname.starts_with(MasterView.mv_namespace_prefix)
|
278
|
+
STDOUT.puts "****BUILTIN DIRECTIVE NAMESPACE PROBLEM: #{dc.name} qname='#{attr_qname}'"
|
279
|
+
raise RuntimeError, "BUILTIN DIRECTIVE NS PROBLEM: #{dc.name} qname='#{attr_qname}"
|
280
|
+
end
|
281
|
+
end
|
108
282
|
@directive_namespaces << ns_name if ! @directive_namespaces.include?( ns_name )
|
109
283
|
@directive_classes[attr_qname] = dc
|
110
|
-
|
111
|
-
|
112
|
-
|
284
|
+
# global directives can be automatically attached to all elements (e.g., generated comments)
|
285
|
+
# or conditionally attached (e.g., a directive that wants to glom onto all elements of a specific type)
|
286
|
+
dc_global_spec = dc.metadata_values.fetch(:global_directive?, false)
|
287
|
+
if dc_global_spec == true # unconditional global directive - applied to all elements
|
288
|
+
@auto_directives << dc
|
289
|
+
elsif dc_global_spec # conditional global directive - applied when match condition satisfied
|
290
|
+
@global_directives << dc
|
291
|
+
end
|
113
292
|
end
|
114
|
-
Log.debug { 'directives='+@directive_classes.keys().sort!().inspect } if MasterView.const_defined?(:Log) #backstop for test case startup
|
115
|
-
Log.debug { 'auto_directives='+@auto_directives.inspect } if MasterView.const_defined?(:Log) #backstop for test case startup
|
116
293
|
|
294
|
+
if MasterView.const_defined?(:Log) #backstop for test case startup
|
295
|
+
Log.debug { "directives=#{@directive_classes.keys().sort!().inspect}" }
|
296
|
+
Log.debug { "global_directives=#{@global_directives.inspect}" }
|
297
|
+
Log.debug { "auto_directives=#{@auto_directives.inspect}" }
|
298
|
+
end
|
117
299
|
end
|
118
300
|
|
119
301
|
# Construct directive processors needed to handle the
|
120
302
|
# attributes defined on a template document element.
|
121
303
|
#
|
122
304
|
# Constructs processors for all global directives
|
123
|
-
# and for any directive attributes.
|
305
|
+
# and for any directive attributes defined on the element.
|
124
306
|
#
|
307
|
+
# Removes all directive attributes from the element's attributes.
|
125
308
|
#
|
126
|
-
def construct_directive_processors(
|
309
|
+
def construct_directive_processors( tag_name, element_attrs )
|
127
310
|
directive_processors = []
|
128
311
|
# always instantiate global directive handlers
|
129
312
|
@auto_directives.each do | dc |
|
130
313
|
directive_processors << dc.new(nil)
|
131
314
|
end
|
315
|
+
# conditionally instantiate global directives which are interested in this element
|
316
|
+
@global_directives.each do | dc |
|
317
|
+
attr_value = dc.metadata_values[:global_directive?].call( tag_name, element_attrs )
|
318
|
+
if attr_value
|
319
|
+
directive_processors << dc.new(attr_value)
|
320
|
+
end
|
321
|
+
end
|
132
322
|
# instantiate the directive processor on the attribute value if its attr present
|
133
|
-
# remove the MV attribute from the document so that
|
323
|
+
# remove the MV attribute from the document so that its only effect is from the processor action
|
134
324
|
@directive_classes.each do | attr_qname, dc |
|
135
|
-
|
325
|
+
directive_attr_value = element_attrs[attr_qname]
|
326
|
+
if directive_attr_value
|
327
|
+
# convert the directive into a processing handler for this element
|
328
|
+
element_attrs.delete(attr_qname)
|
329
|
+
directive_processors << dc.new(directive_attr_value)
|
330
|
+
end
|
136
331
|
end
|
137
332
|
directive_processors
|
138
333
|
end
|
139
334
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
end
|
335
|
+
# class initialization
|
336
|
+
self.current = self.new()
|
337
|
+
#STDOUT.puts "INITIALIZED DirectiveRegistry.current=#{DirectiveRegistry.current.object_id}" ##DEBUG##
|
338
|
+
#STDOUT.puts "...DirectiveRegistry.current=#{DirectiveRegistry.current.inspect}" ##DEBUG##
|
339
|
+
#STDOUT.puts "END DirectiveRegistry definition\n" ##DEBUG##
|
146
340
|
|
147
341
|
end
|
148
342
|
|
149
|
-
# The DirectivesRegistry manages the loaded directives available
|
150
|
-
# for processing template markup
|
151
|
-
DirectivesRegistry = DirectiveRegistry.new()
|
152
|
-
|
153
343
|
# Register a directive implementation.
|
154
344
|
#
|
155
345
|
# Registration is handled automatically for directives
|
@@ -158,12 +348,12 @@ module MasterView
|
|
158
348
|
# module in a directive implementation class.
|
159
349
|
#
|
160
350
|
# Directive registration ordinarily occurs during MasterView
|
161
|
-
# initialization, when directive classes
|
162
|
-
# <code>
|
351
|
+
# initialization, when directive classes from the configured
|
352
|
+
# <code>directive_load_path</code> directories are automatically
|
163
353
|
# loaded and registered with the template engine.
|
164
354
|
#
|
165
355
|
def self.register_directive(directive_class)
|
166
|
-
|
356
|
+
DirectiveRegistry.current.register_directive(directive_class)
|
167
357
|
end
|
168
358
|
|
169
|
-
end
|
359
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# MasterView directive metadata specifications
|
2
|
+
# Built-in MasterView directives
|
3
|
+
|
4
|
+
# default metadata values for directives loaded from this directory
|
5
|
+
default:
|
6
|
+
namespace: mv
|
7
|
+
description: Built-in MasterView directive
|
8
|
+
|
9
|
+
# optionally specify any .rb files in this directory to ignore
|
10
|
+
#ignore: []
|
11
|
+
|
12
|
+
# optionally specify synonyms to register directives
|
13
|
+
# Mainly intended to provide backwards compat (hopefully for short-term transition only...)
|
14
|
+
# Synonym spec ::= [ <directive attribute name>, <synonym> [, <synonym]...
|
15
|
+
#synonyms:
|
16
|
+
# - [ foo, oldfoo ]
|
@@ -1,27 +1,28 @@
|
|
1
1
|
module MasterView
|
2
2
|
module Directives
|
3
|
+
|
3
4
|
# takes the attr_value and parses it as a hash :foo => 'bar', :baz => 'cat'
|
4
5
|
# it sets/overrides the tag's attribute values for each value.
|
5
6
|
# To use erb output simply wrap the content in #{ foo } for example :foo => #{h product.name}
|
7
|
+
#
|
6
8
|
class Attr < MasterView::DirectiveBase
|
7
9
|
# substitution to make it easy to parse erb in attr
|
8
10
|
SubstForErb = '#'+InlineErbStart+'= \1 '+InlineErbEnd+'#'
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
metadata :priority => 'MediumLow',
|
13
|
+
:category => 'general',
|
14
|
+
:description => 'Replaces attribute value(s) on an element with the value of an expression'
|
15
|
+
|
16
|
+
event :stag do
|
16
17
|
attr_value.gsub!( /#\{([^}]*)\}/, SubstForErb ) #taking #{h product.name} and changing to #{{{= h product.name}}}# for easy parsing
|
17
18
|
arr = attr_value.scan( /:(\w+)\s*=>\s*(['"#])([^,\2]*)\2,?/ )
|
18
19
|
arr.each do |scn|
|
19
20
|
n = scn[0]
|
20
21
|
v = scn[2]
|
21
|
-
|
22
|
+
element_attrs[n] = v
|
22
23
|
end
|
23
|
-
dcs.render
|
24
24
|
end
|
25
|
+
|
25
26
|
end
|
26
27
|
end
|
27
28
|
end
|
@@ -1,27 +1,24 @@
|
|
1
1
|
module MasterView
|
2
2
|
module Directives
|
3
3
|
|
4
|
-
#outputs a block around the text tags, if left bracket count is higher than right
|
5
|
-
#assume that the end is a right bracket otherwise use end
|
4
|
+
# outputs a block around the text tags, if left bracket count is higher than right
|
5
|
+
# assume that the end is a right bracket otherwise use end
|
6
|
+
#
|
6
7
|
class Block < MasterView::DirectiveBase
|
7
|
-
def priority
|
8
|
-
DirectivePriorities::MediumHigh
|
9
|
-
end
|
10
8
|
|
11
|
-
|
9
|
+
metadata :priority => 'MediumHigh',
|
10
|
+
:category => 'erb',
|
11
|
+
:description => 'Expands to the equivalent rhtml (erb) block code around the element'
|
12
|
+
|
13
|
+
event :before_stag do
|
12
14
|
count_left_brackets = attr_value.scan( /\{/ ).size
|
13
15
|
count_right_brackets = attr_value.scan( /\}/ ).size
|
14
16
|
@end_block = (count_left_brackets > count_right_brackets) ? '}' : 'end'
|
15
|
-
|
16
|
-
ret = []
|
17
|
-
ret << erb(attr_value)
|
18
|
-
ret << directive_call_stack.render
|
17
|
+
render erb_eval(attr_value)
|
19
18
|
end
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
ret << directive_call_stack.render
|
24
|
-
ret << erb(@end_block)
|
20
|
+
event :after_etag do
|
21
|
+
render erb_eval(@end_block)
|
25
22
|
end
|
26
23
|
|
27
24
|
end
|
@@ -3,27 +3,22 @@ module MasterView
|
|
3
3
|
|
4
4
|
# create check_box helper, quoting object and method if necessary
|
5
5
|
# merging in any html options specified
|
6
|
-
|
7
|
-
|
8
|
-
#eat
|
9
|
-
end
|
10
|
-
|
11
|
-
def etag(dcs)
|
12
|
-
args = parse_attr_value
|
13
|
-
obj = args[0]
|
14
|
-
method = args[1]
|
15
|
-
options_and_on_off = args[2..-1].join(', ')
|
6
|
+
#
|
7
|
+
class CheckBox < MasterView::DirectiveBase
|
16
8
|
|
17
|
-
|
18
|
-
|
9
|
+
metadata :priority => :default,
|
10
|
+
:category => 'form',
|
11
|
+
:description => 'Replaces the element with a Rails check_box form helper',
|
12
|
+
:element_usage => 'input'
|
19
13
|
|
20
|
-
|
14
|
+
attr_arg :object_name, :quote => true
|
15
|
+
attr_arg :method, :quote => true
|
16
|
+
attr_arg :options, :default => {}, :append_element_attrs => [:common_html]
|
17
|
+
attr_arg :checked_value, :default => "'1'"
|
18
|
+
attr_arg :unchecked_value
|
21
19
|
|
22
|
-
|
23
|
-
|
24
|
-
a << method
|
25
|
-
a << options_and_on_off if options_and_on_off && !options_and_on_off.strip.empty?
|
26
|
-
erb_content(a.join(', '))
|
20
|
+
event :element do
|
21
|
+
render erb_content( 'check_box', :object_name, :method, :options, :checked_value, :unchecked_value )
|
27
22
|
end
|
28
23
|
|
29
24
|
end
|
@@ -5,38 +5,24 @@ module MasterView
|
|
5
5
|
# html options specfied on element into any html options in attr_value
|
6
6
|
# attr_value syntax:
|
7
7
|
# object, method, collection, value_method, text_method, options = {}, html_options = {}
|
8
|
-
|
9
|
-
|
10
|
-
#eat
|
11
|
-
end
|
12
|
-
|
13
|
-
def etag(dcs)
|
14
|
-
args = parse_attr_value
|
15
|
-
obj = quote_if(args[0])
|
16
|
-
method = quote_if(args[1])
|
17
|
-
collection = quote_if(args[2])
|
18
|
-
value_method = quote_if(args[3])
|
19
|
-
text_method = quote_if(args[4])
|
20
|
-
options = args[5]
|
21
|
-
html_options = args[6]
|
8
|
+
#
|
9
|
+
class CollectionSelect < MasterView::DirectiveBase
|
22
10
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
options = '{}' if options.to_s.empty? && !html_options.to_s.empty? # if we have html_options but no options, still need empty hash
|
11
|
+
metadata :priority => :default,
|
12
|
+
:category => 'form',
|
13
|
+
:description => 'Replaces the element with a Rails collection_select form helper.',
|
14
|
+
:element_usage => 'select'
|
28
15
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
a << html_options unless html_options.to_s.strip.empty?
|
16
|
+
attr_arg :object_name, :quote => true
|
17
|
+
attr_arg :method, :quote => true
|
18
|
+
attr_arg :collection , :quote => true
|
19
|
+
attr_arg :value_method, :quote => true
|
20
|
+
attr_arg :text_method, :quote => true
|
21
|
+
attr_arg :options, :default => {}
|
22
|
+
attr_arg :html_options, :append_element_attrs => [:common_html]
|
37
23
|
|
38
|
-
|
39
|
-
erb_content(
|
24
|
+
event :element do
|
25
|
+
render erb_content( 'collection_select', :object_name, :method, :collection, :value_method, :text_method, :options, :html_options)
|
40
26
|
end
|
41
27
|
|
42
28
|
end
|
@@ -1,10 +1,16 @@
|
|
1
1
|
module MasterView
|
2
2
|
module Directives
|
3
3
|
class Content < MasterView::DirectiveBase
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
|
5
|
+
metadata :priority => :default,
|
6
|
+
:category => 'general',
|
7
|
+
:description => 'Replaces the content of the element with the value of the expression'
|
8
|
+
|
9
|
+
# replace the content of the element with <%= attr_value %>
|
10
|
+
event :content do
|
11
|
+
render erb_content( attr_value )
|
7
12
|
end
|
13
|
+
|
8
14
|
end
|
9
15
|
end
|
10
16
|
end
|
@@ -1,23 +1,25 @@
|
|
1
1
|
module MasterView
|
2
2
|
module Directives
|
3
|
-
|
3
|
+
|
4
|
+
# outputs an else/end block around the text tags removing previous end tag (from if/elsif)
|
5
|
+
#
|
4
6
|
class Else < MasterView::DirectiveBase
|
5
|
-
def priority
|
6
|
-
DirectivePriorities::High
|
7
|
-
end
|
8
7
|
|
9
|
-
|
8
|
+
metadata :priority => 'High',
|
9
|
+
:category => 'erb',
|
10
|
+
:description => 'Used in conjunction with the mv:if and mv:elsif directives to allow you to create if... elsif... else... end blocks'
|
11
|
+
|
12
|
+
# remove the last <% end -%> from the parent tag's content and add a <% else -%> instead.
|
13
|
+
# This is a continuation of previous if or elsif statement.
|
14
|
+
event :before_stag do
|
10
15
|
tag = @directive_call_stack.context[:tag]
|
11
|
-
delete_last_in_parent(tag,
|
12
|
-
|
13
|
-
ret << erb('else')
|
14
|
-
ret << directive_call_stack.render
|
16
|
+
delete_last_in_parent(tag, erb_eval('end') )
|
17
|
+
render erb_eval( 'else' )
|
15
18
|
end
|
16
19
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
ret << erb('end')
|
20
|
+
# output <% end -%>
|
21
|
+
event :after_etag do
|
22
|
+
render erb_eval( 'end' )
|
21
23
|
end
|
22
24
|
|
23
25
|
end
|