masterview 0.1.5 → 0.2.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 +17 -0
- data/README +36 -504
- data/RELEASE_NOTES +126 -45
- data/Rakefile +215 -86
- data/TODO +8 -3
- data/doc/configuration.html +485 -0
- data/doc/directives.html +1085 -0
- data/doc/guide.html +243 -0
- data/doc/index.html +287 -0
- data/doc/installation.html +376 -0
- data/doc/stylesheets/masterview.css +206 -0
- data/doc/stylesheets/mv-config.css +23 -0
- data/doc/stylesheets/mv-directives.css +18 -0
- data/doc/stylesheets/mv-installation.css +10 -0
- data/doc/troubleshooting.html +18 -0
- data/examples/product.html +256 -0
- data/examples/product.html.old +107 -0
- data/examples/rails_app_config/masterview/environment/development.rb +22 -0
- data/examples/rails_app_config/masterview/environment/production.rb +9 -0
- data/examples/rails_app_config/masterview/settings.rb +59 -0
- data/examples/test.import +80 -0
- data/init.rb +26 -12
- data/lib/masterview/analyzer.rb +25 -15
- data/lib/masterview/directive_base.rb +4 -0
- data/lib/masterview/directive_helpers.rb +7 -5
- data/lib/masterview/directives/import_render.rb +6 -0
- data/lib/masterview/directives/insert_generated_comment.rb +8 -8
- data/lib/masterview/extras/app/controllers/masterview_controller.rb +154 -2
- data/lib/masterview/extras/app/views/masterview/admin/create.rhtml +4 -4
- data/lib/masterview/extras/app/views/masterview/admin/empty.rhtml +1 -1
- data/lib/masterview/extras/app/views/masterview/admin/list.rhtml +14 -9
- data/lib/masterview/extras/app/views/masterview/admin/view_rhtml.rhtml +70 -0
- data/lib/masterview/extras/init_logger.rb +102 -0
- data/lib/masterview/extras/init_rails_erb_mv_direct.rb +117 -0
- data/lib/masterview/extras/init_rails_reparse_checking.rb +59 -0
- data/lib/masterview/extras/watcher.rb +17 -23
- data/lib/masterview/filter_helpers.rb +26 -0
- data/lib/masterview/initializer.rb +912 -0
- data/lib/masterview/io.rb +352 -0
- data/lib/masterview/keyword_expander.rb +116 -0
- data/lib/masterview/masterview_version.rb +2 -2
- data/lib/masterview/mtime_tracking_hash.rb +44 -0
- data/lib/masterview/parser.rb +64 -92
- data/lib/masterview/pathname_extensions.rb +33 -0
- data/lib/masterview/template_spec.rb +49 -56
- data/lib/masterview.rb +40 -85
- data/test/fixtures/configs/fake_rails_app_with_config/config/masterview/environments/development.rb +12 -0
- data/test/fixtures/configs/fake_rails_app_with_config/config/masterview/environments/production.rb +11 -0
- data/test/fixtures/configs/fake_rails_app_with_config/config/masterview/settings.rb +23 -0
- data/test/fixtures/templates/product.html +256 -0
- data/test/fixtures/templates/test.import +80 -0
- data/test/test_helper.rb +5 -3
- data/test/tmp/template/foo.txt +1 -0
- data/test/tmp/templates_src/product.html +256 -0
- data/test/tmp/views/layouts/product.rhtml +35 -0
- data/test/tmp/views/product/_form.rhtml +30 -0
- data/test/tmp/views/product/_product.rhtml +14 -0
- data/test/tmp/views/product/_show.rhtml +27 -0
- data/test/tmp/views/product/destroy.rhtml +27 -0
- data/test/tmp/views/product/edit.rhtml +26 -0
- data/test/tmp/views/product/list.rhtml +31 -0
- data/test/tmp/views/product/new.rhtml +29 -0
- data/test/tmp/views/product/show.rhtml +16 -0
- data/test/unit/config_settings_test.rb +172 -0
- data/test/{attr_test.rb → unit/directive_attr_test.rb} +2 -2
- data/test/{block_test.rb → unit/directive_block_test.rb} +2 -2
- data/test/{content_test.rb → unit/directive_content_test.rb} +2 -2
- data/test/{else_test.rb → unit/directive_else_test.rb} +2 -2
- data/test/{elsif_test.rb → unit/directive_elsif_test.rb} +2 -2
- data/test/{form_test.rb → unit/directive_form_test.rb} +2 -2
- data/test/{global_inline_erb_test.rb → unit/directive_global_inline_erb_test.rb} +2 -2
- data/test/{hidden_field_test.rb → unit/directive_hidden_field_test.rb} +2 -2
- data/test/{if_test.rb → unit/directive_if_test.rb} +2 -2
- data/test/unit/directive_import_render_test.rb +62 -0
- data/test/{import_test.rb → unit/directive_import_test.rb} +2 -2
- data/test/{javascript_include_test.rb → unit/directive_javascript_include_test.rb} +2 -2
- data/test/{link_to_if_test.rb → unit/directive_link_to_if_test.rb} +2 -2
- data/test/{link_to_test.rb → unit/directive_link_to_test.rb} +2 -2
- data/test/{omit_tag_test.rb → unit/directive_omit_tag_test.rb} +2 -2
- data/test/{password_field_test.rb → unit/directive_password_field_test.rb} +2 -2
- data/test/{replace_test.rb → unit/directive_replace_test.rb} +2 -2
- data/test/{stylesheet_link_test.rb → unit/directive_stylesheet_link_test.rb} +2 -2
- data/test/{submit_test.rb → unit/directive_submit_test.rb} +2 -2
- data/test/{text_area_test.rb → unit/directive_text_area_test.rb} +2 -2
- data/test/{text_field_test.rb → unit/directive_text_field_test.rb} +2 -2
- data/test/{example_test.rb → unit/example_test.rb} +1 -1
- data/test/unit/file_mio_test.rb +368 -0
- data/test/{filter_helpers_test.rb → unit/filter_helpers_test.rb} +1 -1
- data/test/unit/keyword_expander_test.rb +95 -0
- data/test/unit/mio_test.rb +110 -0
- data/test/unit/mtime_string_hash_mio_tree_test.rb +289 -0
- data/test/unit/mtime_tracking_hash_test.rb +38 -0
- data/test/{parser_test.rb → unit/parser_test.rb} +19 -1
- data/test/unit/pathname_extensions_test.rb +46 -0
- data/test/{run_parser_test.rb → unit/run_parser_test.rb} +7 -3
- data/test/unit/string_hash_mio_test.rb +320 -0
- data/test/unit/template_file_watcher_test.rb +107 -0
- data/test/{template_spec_test.rb → unit/template_spec_test.rb} +57 -21
- data/test/{template_test.rb → unit/template_test.rb} +123 -22
- data/test/xtras/config-mv-logger_config.rb +109 -0
- data/test/xtras/config_initialize_standalone.rb +53 -0
- metadata +111 -38
- data/lib/masterview/extras/rails_init.rb +0 -72
- data/test/import_render_test.rb +0 -30
- data/test/template_file_watcher_test.rb +0 -50
data/lib/masterview/parser.rb
CHANGED
|
@@ -178,37 +178,22 @@ module MasterView
|
|
|
178
178
|
end
|
|
179
179
|
end
|
|
180
180
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class MIOSerializer
|
|
184
|
+
def initialize(options)
|
|
185
|
+
raise RequiredArgumentMissingError.new("Required argument is missing, specify the MasterViewIO object in options[:output_mio_tree]") unless options[:output_mio_tree]
|
|
186
|
+
@options = options
|
|
187
|
+
@mio_tree = options[:output_mio_tree]
|
|
184
188
|
end
|
|
185
|
-
end
|
|
186
189
|
|
|
187
|
-
# Serializer which can serialize output to file system.
|
|
188
|
-
# It will create any directories that are necessary before writing the file.
|
|
189
|
-
# It will overwrite any existing file that existed.
|
|
190
|
-
class FileSerializer
|
|
191
190
|
def serialize(render_mode, tag)
|
|
192
|
-
Log.debug { "outputting mode=#{render_mode.mode_type} to file=#{render_mode.output}".indent(2*(Renderer.last_renderer.render_levels.size - 1)) }
|
|
193
|
-
dir_name = File.dirname render_mode.output
|
|
194
|
-
FileUtils.makedirs(dir_name) unless File.exist?(dir_name) #ensure path exists
|
|
195
|
-
|
|
196
191
|
data_to_write = tag.data.join
|
|
197
|
-
|
|
198
|
-
existing_file_contents = File.readlines(render_mode.output).join
|
|
199
|
-
if data_to_write == existing_file_contents
|
|
200
|
-
Log.debug { "file identical, skipping output of #{render_mode.output}" }
|
|
201
|
-
return false
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
File.open(render_mode.output, 'w') do |io|
|
|
206
|
-
io << data_to_write
|
|
207
|
-
end
|
|
208
|
-
true
|
|
192
|
+
@mio_tree.path(render_mode.output).write(data_to_write, @options)
|
|
209
193
|
end
|
|
210
194
|
end
|
|
211
195
|
|
|
196
|
+
=begin
|
|
212
197
|
# Serializer which simply serializes output to the console
|
|
213
198
|
class ConsoleSerializer
|
|
214
199
|
def serialize(render_mode, tag)
|
|
@@ -234,11 +219,20 @@ module MasterView
|
|
|
234
219
|
true
|
|
235
220
|
end
|
|
236
221
|
end
|
|
222
|
+
=end
|
|
237
223
|
|
|
238
224
|
class Renderer
|
|
239
225
|
include DirectiveHelpers
|
|
240
|
-
|
|
241
|
-
|
|
226
|
+
|
|
227
|
+
# Set of element names that can be simplified. Used in simplify_empty_elements.
|
|
228
|
+
# Only simplify elements that are specified in the DTD as being empty, collapsing others can cause parsing
|
|
229
|
+
# or rendering problems in browsers.
|
|
230
|
+
# http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_XHTML-1.0-Strict
|
|
231
|
+
# base, meta, link, hr, br, param, img, area, input, col
|
|
232
|
+
XHTMLEmptyElementNameSet = %w{ base meta link hr br param img area input col }.to_set
|
|
233
|
+
|
|
234
|
+
attr_reader :directive_load_paths, :mv_ns, :keyword_expander, :default_extension
|
|
235
|
+
attr_accessor :render_levels, :directive_classes, :default_render_handler, :serializer, :template_pathname, :template_full_pathname
|
|
242
236
|
|
|
243
237
|
def self.last_renderer; @@last_renderer; end
|
|
244
238
|
|
|
@@ -250,11 +244,14 @@ module MasterView
|
|
|
250
244
|
]
|
|
251
245
|
|
|
252
246
|
serializer = options[:serializer] || DefaultSerializer
|
|
253
|
-
self.serializer = serializer.is_a?(Class) ? serializer.new : serializer #one can pass in Serializer class or an instance
|
|
254
|
-
self.restrict_output_to_directory = options[:output_dir] || nil
|
|
247
|
+
self.serializer = serializer.is_a?(Class) ? serializer.new(options) : serializer #one can pass in Serializer class or an instance
|
|
255
248
|
self.mv_ns = options[:namespace] || NamespacePrefix
|
|
249
|
+
self.template_pathname = options[:template_pathname]
|
|
250
|
+
self.template_full_pathname = IOMgr.template.path(self.template_pathname).full_pathname if self.template_pathname
|
|
251
|
+
@default_extension = (options[:output_mio_tree]) ? options[:output_mio_tree].default_extension : IOMgr.erb.default_extension
|
|
252
|
+
@keyword_expander = KeywordExpander.new
|
|
253
|
+
@keyword_expander.set_template_pathname(self.template_pathname, @default_extension)
|
|
256
254
|
self.directive_load_paths = ( DefaultDirectiveLoadPaths << options[:additional_directive_paths] ).flatten
|
|
257
|
-
self.template_path = options[:template_path] || ''
|
|
258
255
|
end
|
|
259
256
|
|
|
260
257
|
def mv_ns=(namespace_prefix)
|
|
@@ -262,11 +259,6 @@ module MasterView
|
|
|
262
259
|
Log.debug { 'namespace_prefix set to '+namespace_prefix }
|
|
263
260
|
end
|
|
264
261
|
|
|
265
|
-
def restrict_output_to_directory=(dir)
|
|
266
|
-
@restrict_output_to_directory = (!dir.nil?) ? File.expand_path(dir) : nil
|
|
267
|
-
Log.debug { 'restrict_output_to_directory set to '+@restrict_output_to_directory.to_s }
|
|
268
|
-
end
|
|
269
|
-
|
|
270
262
|
# Sets directive_load_paths, re-requiring all the new load paths, however any directives that were
|
|
271
263
|
# already required (and loaded) will still be in memory because these are not reset.
|
|
272
264
|
def directive_load_paths=( directive_paths )
|
|
@@ -274,7 +266,7 @@ module MasterView
|
|
|
274
266
|
@auto_directives = []
|
|
275
267
|
directive_paths.each do |directive_path|
|
|
276
268
|
next if directive_path.nil?
|
|
277
|
-
raise
|
|
269
|
+
raise InvalidPathError.new('directive_path does not exist, path='+directive_path) unless File.exist? directive_path
|
|
278
270
|
Dir.open( directive_path ).each { |fn| require "#{directive_path}/#{fn}" if fn =~ /[.]rb$/ }
|
|
279
271
|
|
|
280
272
|
end
|
|
@@ -305,23 +297,9 @@ module MasterView
|
|
|
305
297
|
@render_levels.last.render_modes
|
|
306
298
|
end
|
|
307
299
|
def push_level(render_level)
|
|
308
|
-
render_level.render_modes.each do |mode|
|
|
309
|
-
enforce_sandbox!(mode.output)
|
|
310
|
-
end
|
|
311
300
|
@render_levels.push render_level
|
|
312
301
|
end
|
|
313
302
|
|
|
314
|
-
def enforce_sandbox!(path)
|
|
315
|
-
unless @restrict_output_to_directory.nil?
|
|
316
|
-
expanded_path = File.expand_path(path, @restrict_output_to_directory)
|
|
317
|
-
unless expanded_path.starts_with? @restrict_output_to_directory
|
|
318
|
-
raise InvalidPathException.new( "invalid path=#{path} resticted to path=#{@restrict_output_to_directory}")
|
|
319
|
-
end
|
|
320
|
-
path.replace expanded_path
|
|
321
|
-
end
|
|
322
|
-
end
|
|
323
|
-
|
|
324
|
-
|
|
325
303
|
def push_tag(tag_name, attributes)
|
|
326
304
|
modes.each do |mode|
|
|
327
305
|
attributes_copy = attributes.clone #these get changed in select_active_directives
|
|
@@ -373,19 +351,35 @@ module MasterView
|
|
|
373
351
|
pop_level if need_to_pop_level
|
|
374
352
|
end
|
|
375
353
|
|
|
354
|
+
|
|
355
|
+
# Simplify (collapse) empty elements so that <foo></foo> from rexml parsing ends up being <foo/> .
|
|
356
|
+
# Only simplify elements that are specified in the DTD as being empty, collapsing others can cause parsing
|
|
357
|
+
# or rendering problems in browsers. Uses constant XHTMLEmptyElementNameSet to find elements that should be
|
|
358
|
+
# collapsed.
|
|
359
|
+
# http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_XHTML-1.0-Strict
|
|
360
|
+
# xhtml-1.0-Strict empty elements are:
|
|
361
|
+
# base, meta, link, hr, br, param, img, area, input, col
|
|
376
362
|
def simplify_empty_elements(content) #relies on the fact that > and </ are individual strings and are back to back with nothing in between
|
|
377
363
|
ret = []
|
|
364
|
+
current_element_name = nil
|
|
378
365
|
next_to_last = nil
|
|
379
366
|
last = nil
|
|
380
367
|
content.flatten!
|
|
381
368
|
content.each do |item|
|
|
382
|
-
if next_to_last == '>' && last == '</'
|
|
369
|
+
if next_to_last == '>' && last == '</' && XHTMLEmptyElementNameSet.include?(current_element_name)
|
|
383
370
|
ret.pop #remove '>'
|
|
384
371
|
ret.pop #remove '</'
|
|
385
|
-
ret << '/>'
|
|
372
|
+
ret << ' />' # adding in a space to make xhtml more compatible with html editors and older browsers
|
|
386
373
|
else
|
|
387
374
|
ret << item
|
|
388
375
|
end
|
|
376
|
+
unless item.nil?
|
|
377
|
+
if !item.starts_with?('</') && item.starts_with?('<')
|
|
378
|
+
current_element_name = /^<(\S*)/.match(item)[1] # depending on what is after < this might be empty string
|
|
379
|
+
elsif last && last.starts_with?('</')
|
|
380
|
+
current_element_name = nil
|
|
381
|
+
end
|
|
382
|
+
end
|
|
389
383
|
next_to_last = last
|
|
390
384
|
last = item
|
|
391
385
|
end
|
|
@@ -474,20 +468,20 @@ module MasterView
|
|
|
474
468
|
@renderer.append_raw ERB_EVAL+value+ERB_END
|
|
475
469
|
end
|
|
476
470
|
|
|
477
|
-
# handle a mv:
|
|
471
|
+
# handle a mv:gen_partial attribute, which calls generate and outputs a token
|
|
478
472
|
# it takes an optional :dir => 'foo/bar' which is prepended to partial path,
|
|
479
473
|
# otherwise it just uses what is in partial.
|
|
480
474
|
# This creates a generate attribute value which will be used later.
|
|
481
475
|
# Parameters
|
|
482
|
-
# value = attribute value for
|
|
476
|
+
# value = attribute value for gen_partial
|
|
483
477
|
# attributes = all remaining attributes hash
|
|
484
|
-
def
|
|
485
|
-
value =
|
|
478
|
+
def handle_gen_partial(attributes)
|
|
479
|
+
value = @renderer.keyword_expander.resolveAttrAndDelete(attributes, @renderer.mv_ns+'gen_partial')
|
|
486
480
|
if value
|
|
487
481
|
prepend_dir = find_string_val_in_string_hash(value, :dir) #only used for masterview
|
|
488
482
|
partial = find_string_val_in_string_hash(value, :partial)
|
|
489
483
|
return if partial.nil?
|
|
490
|
-
path = render_partial_name_to_file_name(partial)
|
|
484
|
+
path = render_partial_name_to_file_name(partial, @renderer.default_extension)
|
|
491
485
|
path = File.join(prepend_dir, path) if prepend_dir
|
|
492
486
|
generate_attribute = attributes[@renderer.mv_ns+'generate'] || '' # check if we need to add to existing generate
|
|
493
487
|
generate_attribute = path + (generate_attribute.blank? ? '' : ', '+generate_attribute)
|
|
@@ -497,14 +491,15 @@ module MasterView
|
|
|
497
491
|
end
|
|
498
492
|
|
|
499
493
|
def push_levels(attributes)
|
|
500
|
-
|
|
494
|
+
handle_gen_partial(attributes)
|
|
501
495
|
|
|
502
|
-
gen_replace =
|
|
496
|
+
gen_replace = @renderer.keyword_expander.resolveAttrAndDelete(attributes, @renderer.mv_ns+'gen_replace') #get and delete from map
|
|
503
497
|
generate_replace( gen_replace ) unless gen_replace.nil?
|
|
504
498
|
|
|
505
|
-
gen =
|
|
499
|
+
gen = @renderer.keyword_expander.resolveAttrAndDelete(attributes, @renderer.mv_ns+'generate') #get and delete from map
|
|
506
500
|
if gen
|
|
507
|
-
|
|
501
|
+
omit_comment = OmitGeneratedComments || File.extname(gen) != MasterView::IOMgr.erb.default_extension
|
|
502
|
+
attributes[@renderer.mv_ns+'insert_generated_comment'] = @renderer.template_full_pathname.to_s unless omit_comment #add the comment directive, so it will be written to each gen'd file
|
|
508
503
|
render_level = nil
|
|
509
504
|
gen_values = parse_eval_into_hash(gen, :normal)
|
|
510
505
|
|
|
@@ -515,7 +510,7 @@ module MasterView
|
|
|
515
510
|
arr_values = (value.is_a?(Enumerable)) ? value : [value] #if not enumerable add it to array
|
|
516
511
|
value.each do |path|
|
|
517
512
|
path.strip!
|
|
518
|
-
Log.debug { ('pushing mode='+mode_type.to_s+' path='+path).indent(2*@renderer.render_levels.size) }
|
|
513
|
+
#Log.debug { ('pushing mode='+mode_type.to_s+' path='+path).indent(2*@renderer.render_levels.size) }
|
|
519
514
|
render_level ||= RenderLevel.new
|
|
520
515
|
render_level.push RenderMode.new(path, mode_type)
|
|
521
516
|
end
|
|
@@ -528,31 +523,26 @@ module MasterView
|
|
|
528
523
|
end
|
|
529
524
|
|
|
530
525
|
class Parser
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
output_dir ||= '.'
|
|
536
|
-
Log.debug { "Parsing file=#{File.expand_path(template_file_path)} output_dir=#{File.expand_path(output_dir)}" }
|
|
537
|
-
options[:template_path]=File.expand_path(template_file_path)
|
|
538
|
-
options[:output_dir] = output_dir
|
|
539
|
-
template = File.new( template_file_path )
|
|
540
|
-
template = template.readlines.join if options[:tidy] || options[:escape_erb]
|
|
526
|
+
def self.parse_mio( template_mio, output_mio_tree, options = {})
|
|
527
|
+
options[:template_pathname]= template_mio.pathname
|
|
528
|
+
options[:output_mio_tree] = output_mio_tree
|
|
529
|
+
template = template_mio.read
|
|
541
530
|
self.parse( template, options )
|
|
542
531
|
end
|
|
543
532
|
|
|
533
|
+
|
|
544
534
|
# parse a MasterView template and render output.
|
|
545
535
|
# template param is actual template source passed in as string or array.
|
|
546
|
-
# options are the optional parameters which control the output (:
|
|
536
|
+
# options are the optional parameters which control the output (:output_mio_tree, :namespace, :serializer)
|
|
547
537
|
def self.parse( template, options = DefaultParserOptions.clone)
|
|
548
538
|
options[:listeners] ||= [MasterViewListener]
|
|
549
539
|
options[:rescue_exceptions] = RescueExceptions unless options.include?(:rescue_exceptions)
|
|
550
540
|
|
|
551
541
|
begin
|
|
552
542
|
if options[:tidy]
|
|
553
|
-
template =
|
|
543
|
+
template = TidyHelper.tidy(template)
|
|
554
544
|
elsif options[:escape_erb]
|
|
555
|
-
template =
|
|
545
|
+
template = EscapeErbHelper.escape_erb(template)
|
|
556
546
|
end
|
|
557
547
|
|
|
558
548
|
parser = REXML::Parsers::SAX2Parser.new( template )
|
|
@@ -574,24 +564,6 @@ module MasterView
|
|
|
574
564
|
end
|
|
575
565
|
end
|
|
576
566
|
|
|
577
|
-
def self.tidy(html)
|
|
578
|
-
Tidy.path = TidyPath unless Tidy.path
|
|
579
|
-
xml = Tidy.open do |tidy|
|
|
580
|
-
tidy.options.output_xml = true
|
|
581
|
-
tidy.options.indent = true
|
|
582
|
-
tidy.options.wrap = 0
|
|
583
|
-
xml = tidy.clean(html)
|
|
584
|
-
end
|
|
585
|
-
xml = self.escape_erb(xml)
|
|
586
|
-
Log.debug { 'tidy corrected xml='+xml }
|
|
587
|
-
xml
|
|
588
|
-
end
|
|
589
|
-
|
|
590
|
-
def self.escape_erb(html)
|
|
591
|
-
html = html.gsub(/<%/, InlineErbStart)
|
|
592
|
-
html.gsub!(/%>/, InlineErbEnd)
|
|
593
|
-
html
|
|
594
|
-
end
|
|
595
567
|
end
|
|
596
568
|
|
|
597
569
|
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
|
|
3
|
+
class Pathname
|
|
4
|
+
# create a new Pathname object for path, any backslashes are converted to /,
|
|
5
|
+
# if Pathname object is passed in then returns this existing pathname object
|
|
6
|
+
def self.for_path(path)
|
|
7
|
+
return path if path.is_a? Pathname
|
|
8
|
+
Pathname.new(path.gsub('\\','/'))
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# return the concatenated path, cleaning up any backslashes, ignores nulls or empty pathnames
|
|
12
|
+
def self.join(*args)
|
|
13
|
+
pathnames = args.collect { |a| Pathname.for_path(a) }
|
|
14
|
+
pathnames.inject { |fullpath, p| self.safe_concat(fullpath, p) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# prepends a pathname to existing, ignores nulls or empty pathnames
|
|
18
|
+
def self.safe_concat(prepend_pn, pathname)
|
|
19
|
+
return nil if ((prepend_pn.nil? || prepend_pn.to_s.empty?) && (pathname.nil? || pathname.to_s.empty?)) #both are nil or empty
|
|
20
|
+
return prepend_pn if (pathname.nil? || pathname.to_s.empty?)
|
|
21
|
+
return pathname if (prepend_pn.nil? || prepend_pn.to_s.empty?)
|
|
22
|
+
prepend_pn+pathname
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# return the path string without the extension
|
|
26
|
+
def path_no_ext
|
|
27
|
+
dirname = self.dirname
|
|
28
|
+
ext = self.extname
|
|
29
|
+
base = self.basename(ext)
|
|
30
|
+
presult = (dirname.to_s.empty?) ? base : dirname+base
|
|
31
|
+
presult.to_s
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -26,36 +26,35 @@ module MasterView
|
|
|
26
26
|
File.basename @path
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
# get path relative to working dir
|
|
30
|
+
def long_path
|
|
31
|
+
IOMgr.template.path(@path).full_pathname.to_s
|
|
32
|
+
end
|
|
33
|
+
|
|
29
34
|
# scan the directory of templates, building template_specs and the content_hash
|
|
30
35
|
def self.scan(options = {}, &block)
|
|
31
36
|
content_hash = {}
|
|
32
37
|
template_specs = {}
|
|
33
|
-
files = []
|
|
34
|
-
Find.find(File.join('app/views', TemplateSrcRelativePath)){ |f| files << f }
|
|
35
|
-
files.sort!
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
template_specs[path] = scan_template_file(path, content_hash)
|
|
40
|
-
end
|
|
39
|
+
IOMgr.template.find(:pattern => TemplateFilenamePattern) do |mio|
|
|
40
|
+
template_specs[mio.pathname.to_s] = scan_template_mio(mio, content_hash)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
end
|
|
50
|
-
yield template_specs[path], content_hash if block
|
|
43
|
+
IOMgr.template.find(:pattern => TemplateFilenamePattern) do |mio|
|
|
44
|
+
path = mio.pathname.to_s
|
|
45
|
+
if template_specs[path].status == Status::OK
|
|
46
|
+
invalid_parts = template_mio_out_of_sync?(path, content_hash)
|
|
47
|
+
template_spec = template_specs[path]
|
|
48
|
+
template_spec.update_status_from_invalid_parts(invalid_parts)
|
|
51
49
|
end
|
|
50
|
+
yield template_specs[path], content_hash if block_given?
|
|
52
51
|
end
|
|
53
|
-
|
|
54
52
|
return template_specs, content_hash
|
|
55
53
|
end
|
|
56
54
|
|
|
57
|
-
def self.
|
|
58
|
-
template =
|
|
55
|
+
def self.scan_template_mio(mio, content_hash = {} )
|
|
56
|
+
template = mio.read(:disable_cache => true)
|
|
57
|
+
path = mio.pathname.to_s
|
|
59
58
|
self.scan_template(template, path, content_hash)
|
|
60
59
|
end
|
|
61
60
|
|
|
@@ -69,7 +68,7 @@ module MasterView
|
|
|
69
68
|
# create a template_spec
|
|
70
69
|
def self.scan_template(template, path, content_hash = {})
|
|
71
70
|
template_spec = TemplateSpec.new(path)
|
|
72
|
-
listener = MasterView::Analyzer::Listener.new
|
|
71
|
+
listener = MasterView::Analyzer::Listener.new( :template_pathname => Pathname.for_path(path) )
|
|
73
72
|
begin
|
|
74
73
|
MasterView::Parser.parse( template, :rescue_exceptions => false, :listeners => [listener])
|
|
75
74
|
template_spec.build_list = listener.list
|
|
@@ -89,14 +88,16 @@ module MasterView
|
|
|
89
88
|
template_spec
|
|
90
89
|
end
|
|
91
90
|
|
|
92
|
-
def self.
|
|
93
|
-
|
|
91
|
+
def self.template_mio_out_of_sync?(path, content_hash)
|
|
92
|
+
mio = IOMgr.template.path(path)
|
|
93
|
+
template = mio.read(:disable_cache => true)
|
|
94
|
+
self.template_out_of_sync?(template, path, content_hash)
|
|
94
95
|
end
|
|
95
96
|
|
|
96
97
|
# check if the template is out of sync with the source content (check imports), return array of invalid
|
|
97
|
-
def self.template_out_of_sync?(template, content_hash)
|
|
98
|
+
def self.template_out_of_sync?(template, path, content_hash)
|
|
98
99
|
invalid = []
|
|
99
|
-
listener = MasterView::Analyzer::Listener.new(:content_hash => content_hash, :only_check_hash => true)
|
|
100
|
+
listener = MasterView::Analyzer::Listener.new(:content_hash => content_hash, :template_pathname => Pathname.for_path(path), :only_check_hash => true)
|
|
100
101
|
Parser.parse( template, :rescue_exceptions => false, :listeners => [listener] )
|
|
101
102
|
invalid_list_items = listener.list.find_all { |li| li.hash_invalid? }
|
|
102
103
|
invalid_with_dups = invalid_list_items.collect { |li| li.name }
|
|
@@ -107,8 +108,8 @@ module MasterView
|
|
|
107
108
|
# rebuild template updating all imports, returns the string contents,
|
|
108
109
|
# if options[:write_to_file] = true then it will write the contents to file if different and return true, if identical then returns false
|
|
109
110
|
# otherwise this method returns the content of the template
|
|
110
|
-
# raise error for any other problems. options[:backup] = true to make backup before rebuilding, uses
|
|
111
|
-
# path for where to store backup files. If
|
|
111
|
+
# raise error for any other problems. options[:backup] = true to make backup before rebuilding, uses MasterView::IOMgr.backup to determine
|
|
112
|
+
# path for where to store backup files. If MasterView::IOMgr.backup is nil, then no backup is created.
|
|
112
113
|
def rebuild_template(content_hash, options = {} )
|
|
113
114
|
out = []
|
|
114
115
|
builder = MasterView::Analyzer::Builder.new(content_hash)
|
|
@@ -117,20 +118,18 @@ module MasterView
|
|
|
117
118
|
con = builder.data(li.name, li.index)
|
|
118
119
|
if li.import
|
|
119
120
|
con = con.gsub NamespacePrefix+'generate', NamespacePrefix+'import'
|
|
120
|
-
con.gsub! NamespacePrefix+'
|
|
121
|
+
con.gsub! NamespacePrefix+'gen_partial', NamespacePrefix+'import_render'
|
|
121
122
|
end
|
|
122
123
|
out << con
|
|
123
124
|
end
|
|
124
125
|
template = out.join
|
|
125
126
|
if options[:write_to_file]
|
|
126
|
-
backup = !options[:backup].nil? ? options[:backup] :
|
|
127
|
-
orig =
|
|
127
|
+
backup = !options[:backup].nil? ? options[:backup] : !IOMgr.backup.nil?
|
|
128
|
+
orig = IOMgr.template.path(self.path).read(:disable_cache => true)
|
|
128
129
|
file_written = false
|
|
129
130
|
unless template == orig
|
|
130
131
|
self.backup_file if backup
|
|
131
|
-
|
|
132
|
-
io << template
|
|
133
|
-
end
|
|
132
|
+
IOMgr.template.path(self.path).write(template, :force => true) #force write in case tidy had cleaned up
|
|
134
133
|
file_written = true
|
|
135
134
|
end
|
|
136
135
|
return file_written
|
|
@@ -141,11 +140,11 @@ module MasterView
|
|
|
141
140
|
|
|
142
141
|
# create backup file by appending secs since epoch to filename
|
|
143
142
|
def backup_file(options={} )
|
|
144
|
-
return unless
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
Log.debug { 'creating backup file '+
|
|
148
|
-
|
|
143
|
+
return unless IOMgr.backup
|
|
144
|
+
contents_to_backup = IOMgr.template.path(self.path).read
|
|
145
|
+
backup_path = self.path+'.'+Time.new.to_i.to_s
|
|
146
|
+
Log.debug { 'creating backup file '+backup_path }
|
|
147
|
+
IOMgr.backup.path(backup_path).write(contents_to_backup)
|
|
149
148
|
end
|
|
150
149
|
|
|
151
150
|
# create empty shell file consisting of layout and a comment for where to insert new content, return contents
|
|
@@ -179,6 +178,7 @@ module MasterView
|
|
|
179
178
|
end
|
|
180
179
|
end
|
|
181
180
|
|
|
181
|
+
|
|
182
182
|
# create empty shell file consisting of layout and a comment for where to insert new content. Use action_to_create
|
|
183
183
|
# to infer the destination name controller_action.html and to customize the inserted place holder. Pass the
|
|
184
184
|
# empty insert erb (rhtml) content in which will be rendered with appropriate controller and action values.
|
|
@@ -187,36 +187,29 @@ module MasterView
|
|
|
187
187
|
# :template_source => source_for_template (use this source instead of reading from file)
|
|
188
188
|
# :content_hash => use this content_hash, otherwise it will scan the masterview template directory to create content_hash
|
|
189
189
|
def self.create_empty_shell_for_action(path_to_copy_shell_from, action_to_create, empty_insert_erb, options={} )
|
|
190
|
-
|
|
191
|
-
controller_name =
|
|
192
|
-
extension =
|
|
193
|
-
short_name.scan( /^([^_.]+)_?([^.]*)(\.?\w*)$/ ) do |c, a, e|
|
|
194
|
-
controller_name = c
|
|
195
|
-
extension = e
|
|
196
|
-
end
|
|
197
|
-
extension = '.html' if extension.empty?
|
|
198
|
-
short_name = File.basename(short_name, extension)+extension #ensure short_name has extension
|
|
199
|
-
|
|
190
|
+
path = IOMgr.template.cleanup_path_get_relative_pathname(path_to_copy_shell_from).to_s
|
|
191
|
+
controller_name = Pathname.for_path(path).dirname.to_s
|
|
192
|
+
extension = IOMgr.template.default_extension
|
|
200
193
|
erb_values = CreateShellERBValues.new(controller_name, action_to_create)
|
|
201
194
|
template = ERB.new(empty_insert_erb)
|
|
202
195
|
content_to_insert = template.result(erb_values.get_binding).strip #clear off surrounding whitespace that makes it difficult to debug
|
|
203
196
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
197
|
+
basename_with_extension = erb_values.action_name
|
|
198
|
+
basename_with_extension += extension if extension
|
|
199
|
+
dst_path = (Pathname.for_path(path).dirname+(basename_with_extension)).to_s
|
|
200
|
+
src = (tsrc = options[:template_source]) ? tsrc : IOMgr.template.path(path).read
|
|
207
201
|
|
|
208
202
|
template_specs = {}
|
|
209
203
|
content_hash = options[:content_hash]
|
|
210
204
|
template_specs, content_hash = TemplateSpec.scan unless content_hash
|
|
211
|
-
template_spec_to_copy = template_specs[
|
|
205
|
+
template_spec_to_copy = template_specs[path] || TemplateSpec.scan_template(src, path)
|
|
212
206
|
|
|
213
|
-
result =
|
|
207
|
+
result = TemplateSpec.create_empty_shell( template_spec_to_copy, content_hash, content_to_insert)
|
|
214
208
|
if options[:write_to_file]
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
return dst_file
|
|
209
|
+
template_mio = IOMgr.template.path(dst_path)
|
|
210
|
+
raise 'Template '+dst_path+' already exists, operation aborted.' if template_mio.exist?
|
|
211
|
+
template_mio.write(result)
|
|
212
|
+
return dst_path
|
|
220
213
|
end
|
|
221
214
|
result
|
|
222
215
|
end
|