masterview 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. data/CHANGELOG +15 -0
  2. data/RELEASE_NOTES +26 -2
  3. data/Rakefile +35 -10
  4. data/ReleaseAnnouncement +124 -0
  5. data/TODO +6 -0
  6. data/doc/configuration.html +39 -0
  7. data/doc/guide.html +14 -13
  8. data/doc/index.html +1 -1
  9. data/doc/installation.html +1 -1
  10. data/doc/media_list.html +19 -3
  11. data/examples/rails_app_config/masterview/settings.rb +5 -0
  12. data/lib/masterview/analyzer.rb +13 -1
  13. data/lib/masterview/directives/form.rb +4 -1
  14. data/lib/masterview/directives/stylesheet_link.rb +1 -1
  15. data/lib/masterview/extras/auto_copy.rb +80 -0
  16. data/lib/masterview/initializer.rb +87 -4
  17. data/lib/masterview/io.rb +11 -0
  18. data/lib/masterview/masterview_version.rb +1 -1
  19. data/lib/masterview/mtime_tracking_hash.rb +12 -3
  20. data/lib/masterview/parser.rb +2 -2
  21. data/lib/masterview/rails_ext/action_controller_reparse_checking.rb +10 -9
  22. data/lib/masterview/rails_ext/action_view_erb_direct.rb +53 -34
  23. data/lib/masterview/rails_ext/short_path_calc.rb +127 -0
  24. data/lib/masterview/template_spec.rb +27 -9
  25. data/lib/masterview.rb +1 -1
  26. data/test/tmp/dst/public/images/FOO.GIF +1 -0
  27. data/test/tmp/dst/public/images/bar/baz.png +1 -0
  28. data/test/tmp/dst/public/images/blank.gif +1 -0
  29. data/test/tmp/dst/public/images/cat/dog/mouse.JPG +1 -0
  30. data/test/tmp/dst/public/images/star.jpg +1 -0
  31. data/test/tmp/dst/public/images/test.png +1 -0
  32. data/test/tmp/dst/public/javascript/custom/script.js +1 -0
  33. data/test/tmp/dst/public/javascript/nested/custom/foo.js +1 -0
  34. data/test/tmp/dst/public/javascript/prototype.js +1 -0
  35. data/test/tmp/dst/public/javascript/scriptaculous.js +1 -0
  36. data/test/tmp/dst/public/stylesheets/client/standard/great1.css +1 -0
  37. data/test/tmp/dst/public/stylesheets/custom/blue.css +1 -0
  38. data/test/tmp/dst/public/stylesheets/style.css +1 -0
  39. data/test/tmp/src/css/client/standard/great1.css +1 -0
  40. data/test/tmp/src/css/custom/blue.css +1 -0
  41. data/test/tmp/src/css/style.css +1 -0
  42. data/test/tmp/src/images/FOO.GIF +1 -0
  43. data/test/tmp/src/images/bar/baz.png +1 -0
  44. data/test/tmp/src/images/blank.gif +1 -0
  45. data/test/tmp/src/images/cat/dog/mouse.JPG +1 -0
  46. data/test/tmp/src/images/star.jpg +1 -0
  47. data/test/tmp/src/images/test.png +1 -0
  48. data/test/tmp/src/mixed/FOO.GIF +1 -0
  49. data/test/tmp/src/mixed/bar/baz.png +1 -0
  50. data/test/tmp/src/mixed/blank.gif +1 -0
  51. data/test/tmp/src/mixed/cat/dog/mouse.JPG +1 -0
  52. data/test/tmp/src/mixed/client/standard/great1.css +1 -0
  53. data/test/tmp/src/mixed/custom/blue.css +1 -0
  54. data/test/tmp/src/mixed/custom/script.js +1 -0
  55. data/test/tmp/src/mixed/nested/custom/foo.js +1 -0
  56. data/test/tmp/src/mixed/prototype.js +1 -0
  57. data/test/tmp/src/mixed/scriptaculous.js +1 -0
  58. data/test/tmp/src/mixed/star.jpg +1 -0
  59. data/test/tmp/src/mixed/style.css +1 -0
  60. data/test/tmp/src/mixed/test.png +1 -0
  61. data/test/tmp/src/scripts/custom/script.js +1 -0
  62. data/test/tmp/src/scripts/nested/custom/foo.js +1 -0
  63. data/test/tmp/src/scripts/prototype.js +1 -0
  64. data/test/tmp/src/scripts/scriptaculous.js +1 -0
  65. data/test/unit/auto_copy_test.rb +181 -0
  66. data/test/unit/directive_form_test.rb +63 -0
  67. data/test/unit/directive_stylesheet_link_test.rb +3 -3
  68. data/test/unit/file_mio_test.rb +24 -8
  69. data/test/unit/mtime_string_hash_mio_tree_test.rb +19 -6
  70. data/test/unit/short_path_calc_test.rb +135 -0
  71. data/test/unit/string_hash_mio_test.rb +18 -5
  72. data/test/unit/template_test.rb +37 -0
  73. metadata +85 -2
@@ -290,6 +290,31 @@ module MasterView
290
290
  #
291
291
  attr_accessor :template_asset_base_ref_pattern
292
292
 
293
+ # Configuration for locations of files that MasterView will autocopy to a runtime location.
294
+ # This autocopy feature makes it easy to have prototypes that display properly for
295
+ # WYSIWYG editing and design, using files (images, stylesheets, and javascript) which are not
296
+ # in the standard Rails locations but are automatically copied to proper place for runtime.
297
+ # This is commonly used in conjunction with template_src_dir_path (which changes the
298
+ # location of the source MasterView templates). Developers can have their templates in a non-standard
299
+ # location along with images, stylesheets, and javascript, and then still have everything copied to
300
+ # the proper runtime locations when Rails is fired up. In debug mode, timestamps are checked on every request,
301
+ # in production mode, copy is done only on startup.
302
+ #
303
+ # Each entry is a hash that needs to contain
304
+ # :source => 'path to source folder' # absolute or relative to RAILS_ROOT
305
+ # :destination => 'path to place to copy to' # absolute or relative to RAILS_ROOT
306
+ #
307
+ # and can optionally have a file extension filter
308
+ # :extensions => [:gif, :GIF, :jpg, :JPG] # limit files copied to this array of extensions, case sensitive
309
+ #
310
+ # Default: [] (empty array) - nothing is auto copied
311
+ # Example usage showing three paths added to be auto copied:
312
+ # config.auto_copy_file_entries << { :source => 'path_to_my_images', :destination => 'public/images' }
313
+ # config.auto_copy_file_entries << { :source => 'path_to_my_scripts', :destination => 'public/javascripts', :extensions => [:js, :JS] } # only copy js files
314
+ # config.auto_copy_file_entries << { :source => 'path_to_my_css', :destination => 'public/stylesheets' }
315
+ attr_accessor :auto_copy_file_entries
316
+
317
+
293
318
  # === Template Generation Options
294
319
 
295
320
  # Path to the directory where Masterview template output (rhtml)
@@ -401,6 +426,15 @@ module MasterView
401
426
  # Needs to match +inline_erb_start+ and +inline_erb_end+.
402
427
  attr_accessor :inline_erb_substitution_regex
403
428
 
429
+ # boolean to specify whether to use the original (unpatched) rexml sax2parser.
430
+ # If this is not true and the REXML version is 3.1.4 -3.1.6 MasterView will use
431
+ # a patched version of rexml sax2parser which properly handles doctypes.
432
+ # If this is true or the REXML version is outside this range the original
433
+ # sax2 parser will be used
434
+ #
435
+ # Default: <tt>false</tt>
436
+ attr_accessor :use_original_rexml_sax2parser
437
+
404
438
  # === Rails Application Options
405
439
 
406
440
  # Boolean which specifies whether masterview templates are parsed
@@ -537,9 +571,10 @@ module MasterView
537
571
  @mv_code_base_dir = File.expand_path( File.dirname(__FILE__) )
538
572
  builtin_directives_path = File.join( mv_code_base_dir, 'directives')
539
573
  @mv_installation_dir = File.expand_path( "#{File.dirname(__FILE__)}/../.." )
574
+ @auto_copy_file_entries = []
540
575
 
541
576
  #ISSUE: should probably also detect std console or breakpointer launch scripts [DJL 10-Jun-2006]
542
- @rails_runner_scripts_pattern = /server|dispatch|mongrel_rails|cgi/ #cgi picks up scgi and fcgi
577
+ @rails_runner_scripts_pattern = /server|dispatch|mongrel_rails|cgi|-e/ #cgi picks up scgi and fcgi, -e for RadRails
543
578
 
544
579
  @has_rails_context = (defined?(::RAILS_ROOT) != nil)
545
580
  decide_if_running_rails
@@ -676,6 +711,7 @@ module MasterView
676
711
  @running_rails = has_rails_context && ($PROGRAM_NAME =~ rails_runner_scripts_pattern) != nil
677
712
 
678
713
  # TODO could try checking if things are defined instead but what would we check for?? Something related to Dispatcher?
714
+ # Dispatcher doesn't seemed to be defined when we come through here
679
715
  # @running_rails = has_rails_context && (defined?(::Dispatcher.dispatch)) != nil
680
716
  end
681
717
 
@@ -713,12 +749,12 @@ module MasterView
713
749
 
714
750
  # Add masterview directive implementations from plugins
715
751
  # to the masterview directive load path
716
- #
752
+ #
717
753
  # EXPERIMENTAL PROTOTYPE - NOT YET PUBLICIZED [DJL 13-Feb-2007]
718
754
  def add_plugin_directives(*base_paths) #:nodo:
719
755
  # append plugin directives in alphabetical order, per Rails plugin loading
720
756
  plugin_directives = find_plugin_directives(base_paths)
721
- plugin_directives.each { | plugin_dir_path |
757
+ plugin_directives.each { | plugin_dir_path |
722
758
  add_directive_path plugin_dir_path
723
759
  }
724
760
  plugin_directives
@@ -728,7 +764,7 @@ module MasterView
728
764
  # Return list of directives paths from plugins
729
765
  def find_plugin_directives(*base_paths)
730
766
  directive_paths = []
731
- find_plugins(base_paths).each do | plugin_dir_path |
767
+ find_plugins(base_paths).each do | plugin_dir_path |
732
768
  next if File.basename(plugin_dir_path) == 'masterview' #skip masterview's own built-in directives
733
769
  directive_dir_path = File.join(plugin_dir_path, 'directives')
734
770
  if File.directory?(directive_dir_path)
@@ -974,6 +1010,7 @@ module MasterView
974
1010
  MasterView.const_set('InlineErbStart', config.inline_erb_start)
975
1011
  MasterView.const_set('InlineErbEnd', config.inline_erb_end)
976
1012
  MasterView.const_set('InlineErbSubstitutionRegex', config.inline_erb_substitution_regex)
1013
+ MasterView.const_set('UseOriginalRexmlSax2Parser', config.use_original_rexml_sax2parser)
977
1014
 
978
1015
  # Rails application options
979
1016
  MasterView.const_set('ParseMasterViewTemplatesAtStartup', config.parse_masterview_templates_at_startup)
@@ -996,6 +1033,8 @@ module MasterView
996
1033
  configuration.namespace_prefix_extensions )
997
1034
  initialize_logger
998
1035
  initialize_mio
1036
+ set_rexml_parser_class
1037
+ initialize_auto_copy_files
999
1038
  #Back out experiment: causes load order problems
1000
1039
  ##load_directives # held off on this until logger is installed
1001
1040
  install_in_rails
@@ -1039,6 +1078,45 @@ module MasterView
1039
1078
  MasterView::LoadedFeatures[:tidy_template_read] = config.default_parser_options[:tidy]
1040
1079
  end
1041
1080
 
1081
+ # Initialize the auto_copy_files MasterView I/O and AutoCopy setup
1082
+ def initialize_auto_copy_files
1083
+ require 'masterview/extras/auto_copy'
1084
+ auto_copy_file_entries = configuration.auto_copy_file_entries
1085
+ if auto_copy_file_entries
1086
+ auto_copy_file_entries.each do |entry|
1087
+ if entry[:source] && entry[:destination]
1088
+ sourceMIO = MasterView::MIO::FileMIOTree.new(entry[:source])
1089
+ destMIO = MasterView::MIO::FileMIOTree.new(entry[:destination])
1090
+ extensions = entry[:extensions] || []
1091
+ filename_patterns = extensions.collect{ |ext| "*.#{ext.to_s}" }
1092
+ filename_patterns = ['*'] if filename_patterns.empty?
1093
+ filename_pattern_for_log = (filename_patterns.empty?) ? '*' : filename_patterns.join(',')
1094
+ MasterView::AutoCopy.register(sourceMIO, destMIO, filename_patterns)
1095
+ MasterView::Log.info{ "Auto copying files from #{entry[:source]} to #{entry[:destination]} with filter #{filename_patterns.join(',')}" }
1096
+ else
1097
+ MasterView::Log.error{ 'config.auto_copy_file_entries entry is missing required :source or :destination values, will be ignored. entry='+entry.inspect }
1098
+ end
1099
+ end
1100
+ end
1101
+ end
1102
+
1103
+ # set the REXML SAX2ParserClass, use patched parser or standard SAX2Parser
1104
+ # checks version of REXML and value of :use_patched_rexml_sax2parser
1105
+ def set_rexml_parser_class
1106
+ if REXML::Version < '3.1.4' # below minimum version
1107
+ MasterView::Log.error { 'Fatal error: MasterView requires REXML version 3.1.4 or greater' }
1108
+ raise 'Fatal error: MasterView requires REXML version 3.1.4 or greater'
1109
+ elsif MasterView::UseOriginalRexmlSax2Parser || !['3.1.4', '3.1.5', '3.1.6'].include?(REXML::Version)
1110
+ MasterView::Log.info { 'MasterView using REXML Sax2Parser version = '+REXML::Version }
1111
+ require 'rexml/parsers/sax2parser'
1112
+ MasterView.const_set('REXMLSax2ParserClass', REXML::Parsers::SAX2Parser)
1113
+ else
1114
+ MasterView::Log.info { 'MasterView using REXML '+REXML::VERSION+' Sax2ParserWithDoctypeFix' }
1115
+ require 'rexml/parsers/sax2parser_with_doctype_fix'
1116
+ MasterView.const_set('REXMLSax2ParserClass', REXML::Parsers::SAX2ParserWithDoctypeFix)
1117
+ end
1118
+ end
1119
+
1042
1120
  #NOTE: not currently used - caused problems during startup, so reverted to original
1043
1121
  # scheme where loading is triggered on demand by template parsing
1044
1122
  # [SJL 20-Sep-2006]
@@ -1052,6 +1130,7 @@ module MasterView
1052
1130
  return if ! configuration.on_rails?
1053
1131
  enable_mv_admin_pages
1054
1132
  parse_templates_at_startup
1133
+ auto_copy_at_startup
1055
1134
  enable_reparse_changed_templates
1056
1135
  enable_rails_erb_direct
1057
1136
  MasterView::Log.info{ 'MasterView plugin initialized - Version '+MasterView::VERSION::STRING }
@@ -1074,6 +1153,10 @@ module MasterView
1074
1153
  end
1075
1154
  end
1076
1155
 
1156
+ def auto_copy_at_startup #:nodoc:
1157
+ MasterView::AutoCopy.copy_all_updated_files
1158
+ end
1159
+
1077
1160
  #--
1078
1161
  # DBC-style notation per DbC - rubydbc-0.1 (Andy Hunt's Design by Contract)
1079
1162
  #pre( MasterView::ParseMasterViewTemplatesAtStartup )
data/lib/masterview/io.rb CHANGED
@@ -259,6 +259,11 @@ module MIO
259
259
  end
260
260
  written
261
261
  end
262
+
263
+ # remove - delete file
264
+ def remove
265
+ full_pathname.delete
266
+ end
262
267
  end
263
268
 
264
269
  class StringMIO < MIOBase
@@ -295,6 +300,11 @@ module MIO
295
300
  def exist?
296
301
  @string_hash.has_key?(self.pathname.to_s)
297
302
  end
303
+
304
+ # remove - delete hash entry
305
+ def remove
306
+ @string_hash.delete(self.pathname.to_s)
307
+ end
298
308
  end
299
309
 
300
310
  # used by MTimeStringHashMIOTree to create MIO objects that check
@@ -314,6 +324,7 @@ module MIO
314
324
  include MasterViewIOIdenticalMixin
315
325
  def exist?; end
316
326
  def mtime; end
327
+ def remove; end
317
328
  end
318
329
 
319
330
 
@@ -2,7 +2,7 @@ module MasterView
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 3
5
- TINY = 2
5
+ TINY = 3
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -1,9 +1,9 @@
1
1
  module MasterView
2
2
 
3
3
  # this hash is designed to track modified times of its values
4
- # this behaviour is only supported through using []= and store
4
+ # this behaviour is only supported through using []= and store
5
5
  # mechanisms and thus default semantics have been excluded
6
- # other update methods such as merge! have been excluded from
6
+ # other update methods such as merge! have been excluded from
7
7
  # this implementation and will raise errors
8
8
  class MTimeTrackingHash < Hash
9
9
 
@@ -13,7 +13,7 @@ module MasterView
13
13
  super
14
14
  @mtimes = {}
15
15
  end
16
-
16
+
17
17
  def []=(key, value)
18
18
  update_mtime(key)
19
19
  super
@@ -24,6 +24,11 @@ module MasterView
24
24
  super
25
25
  end
26
26
 
27
+ def delete(key)
28
+ delete_mtime(key)
29
+ super
30
+ end
31
+
27
32
  # returns the Time object for this key
28
33
  def mtime(key)
29
34
  @mtimes[key]
@@ -35,6 +40,10 @@ module MasterView
35
40
  @mtimes[key] = Time.now
36
41
  end
37
42
 
43
+ def delete_mtime(key)
44
+ @mtimes.delete(key)
45
+ end
46
+
38
47
  def default=(obj); raise "Not implemented"; end
39
48
  def merge!(obj,&block); raise "Not implemented"; end
40
49
  def reject!(obj,&block); raise "Not implemented"; end
@@ -596,7 +596,7 @@ module MasterView
596
596
  end
597
597
 
598
598
  def generate_replace(value)
599
- @renderer.append_raw ERB_EVAL_START+value+ERB_EVAL_END
599
+ @renderer.append_raw ERB_CONTENT_START+value+ERB_CONTENT_END
600
600
  end
601
601
 
602
602
  # handle a mv:gen_partial attribute, which calls generate and outputs a token
@@ -690,7 +690,7 @@ module MasterView
690
690
  ensure_standard_options_configured(options)
691
691
 
692
692
  begin
693
- sax_parser = REXML::Parsers::SAX2ParserWithDoctypeFix.new( template )
693
+ sax_parser = MasterView::REXMLSax2ParserClass.new( template )
694
694
  options[:listeners].each do |listener|
695
695
  if listener.is_a? Class
696
696
  sax_parser.listen( listener.new(options) )
@@ -1,16 +1,16 @@
1
1
  #
2
2
  # Installs hooks to insert MasterView changed-template reparsing
3
3
  # into the Rails request dispatching handler in ActionController::Base.
4
- #
4
+ #
5
5
  # Ensures that MasterView output files are always current while running a
6
6
  # Rails application. The <code>:rails_reparse_checking</code> option in the
7
- # MasterView configuration should generally be enabled/disabled in conjunction
8
- # with Rails class load caching, which is normally off in a development environment
9
- # configuration.
10
- #
11
- # Requires that MasterView templates be parsed at application startup to properly
7
+ # MasterView configuration should generally be enabled/disabled in conjunction
8
+ # with Rails class load caching, which is normally off in a development environment
9
+ # configuration.
10
+ #
11
+ # Requires that MasterView templates be parsed at application startup to properly
12
12
  # initialize the mechanism for detecting template changes after application startup.
13
- #
13
+ #
14
14
 
15
15
  module ActionController #:nodoc:
16
16
  class Base #:nodoc
@@ -18,10 +18,11 @@ module ActionController #:nodoc:
18
18
  # changed templates during Rails request dispatching.
19
19
  alias :process_pre_mv :process #:nodoc:
20
20
  def process(request, response, method = :perform_action, *arguments) #:nodoc:
21
- MasterView::TemplateWatcher.check_updated(MasterView::IOMgr.template, MasterView::TemplateFilenamePattern ) do |mio|
21
+ MasterView::TemplateWatcher.check_updated(MasterView::IOMgr.template, MasterView::TemplateFilenamePattern ) do |mio|
22
22
  MasterView::Parser.parse_mio( mio, MasterView::IOMgr.erb )
23
23
  end
24
+ MasterView::AutoCopy.copy_all_updated_files
24
25
  process_pre_mv(request, response, method, *arguments)
25
26
  end
26
27
  end
27
- end
28
+ end
@@ -12,6 +12,8 @@
12
12
  # to rewrite this code to use the provided hooks.
13
13
  #
14
14
 
15
+ require File.join( File.dirname(__FILE__), 'short_path_calc' )
16
+
15
17
  module ActionView #:nodoc:
16
18
 
17
19
  class Base #:nodoc:
@@ -20,20 +22,31 @@ module ActionView #:nodoc:
20
22
  # check MasterView paths because might not exist on file system
21
23
  alias :template_exists_pre_mv? :template_exists? #:nodoc:
22
24
  def template_exists?(template_path, extension) #:nodoc:
23
- short_path = template_path+'.'+extension.to_s
24
- template_exists_pre_mv?(template_path, extension) || MasterView::IOMgr.erb.path(short_path).exist?
25
+ found = template_exists_pre_mv?(template_path, extension)
26
+ #MasterView::Log.debug{ "template_exists? tpath=#{template_path} base=#{self.base_path}"}
27
+
28
+ # template is relative to view_base
29
+ unless found
30
+ short_path_no_ext = MasterView::RailsExt::ShortPathCalculator.calc_short_path(self.base_path, template_path)
31
+ if short_path_no_ext
32
+ short_path = short_path_no_ext+'.'+extension.to_s
33
+ found = MasterView::IOMgr.erb.path(short_path).exist?
34
+ end
35
+ end
36
+ found
25
37
  end
26
38
 
27
39
  # checks for the existence of the template in MasterView first before using the
28
40
  # rails file system based read_template_file
29
41
  alias :read_template_file_pre_mv :read_template_file #:nodoc:
30
- def read_template_file(template_path, extension) #:nodoc:
31
- short_path_mio = MasterView::IOMgr.erb.path(short_relative_path_mv(template_path))
32
- if short_path_mio.exist?
33
- short_path_mio.read
34
- else # use original rails read_template_file
35
- read_template_file_pre_mv(template_path, extension)
36
- end
42
+ def read_template_file(template_path_with_ext, extension) #:nodoc:
43
+ #MasterView::Log.debug{ "read_template_file tpath=#{template_path_with_ext} base=#{self.base_path}"}
44
+
45
+ # template is relative to Rails root
46
+ # if short_path found and exists in mio, then read and return otherwise use original read method
47
+ MasterView::RailsExt::ShortPathCalculator.calc_short_path_and_retrieve(MasterView::ConfigSettings.rails_root_path, template_path_with_ext,
48
+ MasterView::IOMgr.erb) ||
49
+ read_template_file_pre_mv(template_path_with_ext, extension)
37
50
  end
38
51
 
39
52
  # I submitted a patch to rails to make this easier it was picked up in changeset 5587
@@ -47,16 +60,18 @@ module ActionView #:nodoc:
47
60
  # not stored on the file system can hook and extend appropriately
48
61
  alias :template_changed_since_pre_mv? :template_changed_since?
49
62
  def template_changed_since?(file_name, compile_time)
50
- short_path_mio = (file_name && !file_name.empty?) ? MasterView::IOMgr.erb.path(short_relative_path_mv(file_name)) : nil
51
- if short_path_mio && short_path_mio.exist? # mio template so use mv to check if changed
52
- compile_time < short_path_mio.mtime
63
+ #MasterView::Log.debug { "template_changed_since? tpath=#{file_name} base=#{self.base_path}"}
64
+
65
+ # template is relative to rails root
66
+ mio_mtime = MasterView::RailsExt::ShortPathCalculator.calc_short_path_and_get_mtime(MasterView::ConfigSettings.rails_root_path, file_name,
67
+ MasterView::IOMgr.erb)
68
+ if mio_mtime
69
+ compile_time < mio_mtime
53
70
  else # delegate to rails original file based version
54
71
  template_changed_since_pre_mv?(file_name, compile_time)
55
72
  end
56
73
  end
57
74
 
58
- public
59
-
60
75
  else # older rails needs to use old method
61
76
 
62
77
  # Check whether compilation is necessary.
@@ -66,15 +81,19 @@ module ActionView #:nodoc:
66
81
  # original code.
67
82
  alias :compile_template_pre_mv? :compile_template? #:nodoc:
68
83
  def compile_template?(template, file_name, local_assigns) #:nodoc:
69
- short_path_mio = (file_name && !file_name.empty?) ? MasterView::IOMgr.erb.path(short_relative_path_mv(file_name)) : nil
70
- if short_path_mio && short_path_mio.exist? # mio template
84
+ #MasterView::Log.debug { "compile_template? tpath=#{file_name} base=#{self.base_path}"}
85
+
86
+ # templat is relative to Rails root
87
+ mio_mtime = MasterView::RailsExt::ShortPathCalculator.calc_short_path_and_get_mtime(MasterView::ConfigSettings.rails_root_path, file_name,
88
+ MasterView::IOMgr.erb)
89
+ if mio_mtime
71
90
  method_key = file_name || template
72
91
  render_symbol = @@method_names[method_key]
73
92
 
74
93
  if @@compile_time[render_symbol] && supports_local_assigns?(render_symbol, local_assigns)
75
94
  if file_name && !@@cache_template_loading
76
- need_to_compile = (@@compile_time[render_symbol] < short_path_mio.mtime) # use mio.mtime instead of File.mtime
77
- MasterView::Log.debug{ 'compile_template? template_short_path = '+short_path_mio.pathname.to_s+' compile?='+need_to_compile.to_s }
95
+ need_to_compile = (@@compile_time[render_symbol] < mio_mtime) # use mio.mtime instead of File.mtime
96
+ MasterView::Log.debug{ 'compile_template? file_name = '+file_name+' compile?='+need_to_compile.to_s }
78
97
  need_to_compile
79
98
  end
80
99
  else
@@ -87,14 +106,6 @@ module ActionView #:nodoc:
87
106
 
88
107
  end
89
108
 
90
-
91
- private
92
-
93
- # returns the short path relative to view_base since MasterView uses relative paths exclusively
94
- def short_relative_path_mv(template_path) #:nodoc:
95
- (template_path.starts_with?(self.base_path)) ? template_path[self.base_path.length+1..-1] : template_path
96
- end
97
-
98
109
  end
99
110
 
100
111
  # The TemplateError exception is raised when the compilation of the template fails. This exception then gathers a
@@ -108,10 +119,14 @@ module ActionView #:nodoc:
108
119
  return unless num = line_number
109
120
  num = num.to_i
110
121
 
111
- #added/modified the following three lines to support reading from MasterView with fallback to file system
112
- relative_path = (@file_path.starts_with?(@base_path)) ? @file_path[@base_path.length+1..-1] : @file_path
113
- short_path_mio = (relative_path && !relative_path.empty?) ? MasterView::IOMgr.erb.path(relative_path) : nil
114
- source_code = (short_path_mio && short_path_mio.exist?) ? StringIO.new(short_path_mio.read).readlines : IO.readlines(@file_path)
122
+ #MasterView::Log.debug { "source_extract12 tpath=#{@file_path} base=#{@base_path}"}
123
+
124
+ # template is relative to Rails root
125
+
126
+ #added/modified the following two lines to support reading from MasterView with fallback to file system
127
+ filedata = MasterView::RailsExt::ShortPathCalculator.calc_short_path_and_retrieve(MasterView::ConfigSettings.rails_root_path, @file_path,
128
+ MasterView::IOMgr.erb)
129
+ source_code = (filedata) ? StringIO.new(filedata).readlines : IO.readlines(@file_path) # mio.exists? use it, else original file read
115
130
  # source_code = IO.readlines(@file_path) # original rails 1.2 source
116
131
 
117
132
  start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
@@ -133,10 +148,14 @@ module ActionView #:nodoc:
133
148
  class TemplateError < ActionViewError #:nodoc:
134
149
  def source_extract(indention = 0) #:nodoc:
135
150
 
136
- #added/modified the following three lines to support reading from MasterView with fallback to file system
137
- relative_path = (@file_name.starts_with?(@base_path)) ? @file_name[@base_path.length+1..-1] : @file_name
138
- short_path_mio = (relative_path && !relative_path.empty?) ? MasterView::IOMgr.erb.path(relative_path) : nil
139
- source_code = (short_path_mio && short_path_mio.exist?) ? StringIO.new(short_path_mio.read).readlines : IO.readlines(@file_name)
151
+ #MasterView::Log.debug { "source_extract116 tpath=#{@file_name} base=#{@base_path}"}
152
+
153
+ # template is relative to Rails root
154
+
155
+ #added/modified the following two lines to support reading from MasterView with fallback to file system
156
+ filedata = MasterView::RailsExt::ShortPathCalculator.calc_short_path_and_retrieve(MasterView::ConfigSettings.rails_root_path, @file_name,
157
+ MasterView::IOMgr.erb)
158
+ source_code = (filedata) ? StringIO.new(filedata).readlines : IO.readlines(@file_name) # mio.exists? use it, else original file read
140
159
 
141
160
  start_on_line = [ line_number - SOURCE_CODE_RADIUS - 1, 0 ].max
142
161
  end_on_line = [ line_number + SOURCE_CODE_RADIUS - 1, source_code.length].min
@@ -0,0 +1,127 @@
1
+ module MasterView
2
+ module RailsExt
3
+
4
+ # short path calculator, computes the short relative path for given the mv_view_path, a base_path,
5
+ # and a template_path which may be relative or absolute. This calculator checks to see if it is
6
+ # absolute or relative and cleans up the ./../ returning the short_path relative to mv_view_path
7
+ # if it can be calculated, otherwise simply returns full_path.
8
+ #
9
+ # Example usage:
10
+ # short_path = MasterView::RailsExt::ShortPathCalculator.calc_short_path(view_base_path, template_path)
11
+ #
12
+ # Requires Pathname extensions
13
+ # Requires String#starts_with?
14
+ # Requires MasterView::ConfigSettings.rails_views_dir_path be set before this class is required
15
+ #
16
+ # Example data:
17
+ #
18
+ # MV_RAILS_ROOT_VIEW_DIR = /home/foo/app/views
19
+ # RAILS_ROOT = /home/foo/config/..
20
+ # RAILS_ROOT_VIEW_DIR = /home/foo/config/../app/views
21
+ # BASE_DIR = /home/foo/config/../app/views
22
+ # BASE_DIR = /home/foo/vendor/plugins/goldberg/lib/../app/views
23
+ # template_path = store/list # already relative, return existing
24
+ # template_path = /home/foo/config/../app/views/store/list # starts with RAILS_ROOT_VIEW_DIR return short
25
+ # template_path = /home/foo/vendor/plugins/goldberg/lib/../app/views/../../../../../app/views/store/list # cleaned starts with RAILS_ROOT_VIEW_DIR return short
26
+ # template_path = ../../../../../app/views/store/list # when combined with base_path and cleaned starts with RAILS_ROOT_VIEW_DIR, return short
27
+ # template_path = /home/foo/vendor/plugins/goldberg/lib/../app/views/not/railsroot, return nil
28
+ # template_path = store/list # but base_path is not RAILS_ROOT_VIEW_DIR, return nil
29
+ #
30
+ class ShortPathCalculator
31
+
32
+ PATH_NOT_FOUND = -1
33
+
34
+ class << self # class methods
35
+ # cleans and expands the path storing in a class instance var
36
+ def mv_path=(view_path)
37
+ @clean_mv_view_path = Pathname.for_path(view_path).expand_path.cleanpath.to_s unless view_path.nil?
38
+ #MasterView::Log.debug { "ShortPathCalculator.mv_path=#{@clean_mv_view_path}"}
39
+ end
40
+
41
+ def mv_path
42
+ @clean_mv_view_path
43
+ end
44
+ end
45
+
46
+ # hash of base_path to hash of template_paths containing short_path, stored as class instance variable
47
+ @short_path_cache = {} # one and only cache, double hash
48
+
49
+ # set mv_view_path
50
+ self.mv_path = MasterView::ConfigSettings.rails_views_dir_path
51
+
52
+
53
+ class << self # class methods
54
+ # class method that computes the short relative path to mv_view_path for a template_path which
55
+ # may be absolute or relative. If short_path cannot be calculated (not under mv_view_path)
56
+ # then return nil. This calculation cleans up messy paths including ./.. and
57
+ # resultant calculations are hashed.
58
+ def calc_short_path(view_base_path, template_path)
59
+ base_path_cache = short_path_cache(view_base_path)
60
+ short_path = base_path_cache.fetch(template_path, PATH_NOT_FOUND) # defaults to PATH_NOT_FOUND
61
+ #MasterView::Log.debug { "calc_short_path - short_path=#{short_path} using cache view_base=#{view_base_path} template_path=#{template_path}"} unless short_path == PATH_NOT_FOUND #todo comment out
62
+ return short_path unless short_path == PATH_NOT_FOUND # if already cached (even if nil was saved), then return it
63
+
64
+
65
+ clean_view_base_pathname = Pathname.for_path(view_base_path).expand_path.cleanpath # expanded and cleaned
66
+ clean_view_base_str = clean_view_base_pathname.to_s
67
+ clean_template_pathname = Pathname.for_path(template_path).expand_path(clean_view_base_pathname).cleanpath # expanded from view_base and cleaned
68
+ clean_template_str = clean_template_pathname.to_s
69
+
70
+
71
+ raise "MasterView::ConfigSettings.rails_views_dir_path is nil, need to set config.rails_views_dir_path" if mv_path.nil?
72
+
73
+ short_path = nil
74
+ if clean_template_str.starts_with?(mv_path) # if clean_template_path starts with MV_VIEW_DIR (both are absolute)
75
+ short_path = clean_template_str[mv_path.length+1..-1] # strip off MV_VIEW_DIR and return short path
76
+ end
77
+
78
+
79
+
80
+ #MasterView::Log.debug { "calc_short_path - short_path=#{short_path} view_base=#{view_base_path} template_path=#{template_path} RAILS_VIEW=#{@clean_mv_view_path}"} # todo comment out
81
+ #MasterView::Log.debug { "cspdetails- #{clean_view_base_str}+#{clean_template_str} <=> #{mv_path}"}
82
+ base_path_cache[template_path] = short_path # store in cache and return
83
+ end
84
+
85
+ # calculate the short path and if exists then retrieve the data from the mioTree, otherwise return nil
86
+ def calc_short_path_and_retrieve(view_base_path, template_path, mioTree)
87
+ calc_short_path_and_call_block(view_base_path, template_path, mioTree) do |mio|
88
+ mio.read
89
+ end
90
+ end
91
+
92
+ # calculate the short_path and if exists return the mtime, otherwise nil
93
+ def calc_short_path_and_get_mtime(view_base_path, template_path, mioTree)
94
+ calc_short_path_and_call_block(view_base_path, template_path, mioTree) do |mio|
95
+ mio.mtime
96
+ end
97
+ end
98
+
99
+ private
100
+
101
+ # get/create hash for view_base_path, class method
102
+ def short_path_cache(view_base_path) #:nodoc:
103
+ @short_path_cache[view_base_path] ||= {}
104
+ end
105
+
106
+ # calculate short path and if exists execute block
107
+ def calc_short_path_and_call_block(view_base_path, template_path, mioTree, &block)
108
+ short_path = calc_short_path(view_base_path, template_path)
109
+ if short_path
110
+ short_path_mio = mioTree.path(short_path)
111
+ if short_path_mio && short_path_mio.exist?
112
+ yield short_path_mio
113
+ else
114
+ nil
115
+ end
116
+ else
117
+ nil
118
+ end
119
+ end
120
+
121
+ end
122
+
123
+ end
124
+
125
+
126
+ end
127
+ end