lib/masterview/initializer.rb in masterview-0.2.5 vs lib/masterview/initializer.rb in masterview-0.3.0

- old
+ new

@@ -44,11 +44,18 @@ # [JJB 13-Jun-2006] #++ # require 'date' +# we can't be assured of the load path state if initializer is run standalone +# to construct a configuration prior to full MasterView initialization +currentPath = File.dirname(__FILE__) +require File.join( currentPath, 'directive_metadata' ) +require File.join( currentPath, 'directive_load_path' ) +DEBUG_DIRECTIVE_PATH = false #:nodoc: # temp debug + module MasterView # A Configuration holds all the configuration settings used the # MasterView::Initializer to configure the template engine. # @@ -152,14 +159,47 @@ # is installed by default. If the application provides # a <tt>masterview/directives</tt> directory in its +root_path+, # that directory is also automatically added to the directives # load path. # - # Append additional path(s) to load custom directives from specific - # locations. - attr_accessor :directive_paths + # A directive load path entry specifiess a directory path + # from which masterview directives are loaded. + # Configuration options can optionally be specified to override + # or extend any .metadata specifications in the directory. + # + # Use add_directive_path to append additional directory path(s) + # from which to load custom directives. + # + attr_accessor :directive_load_path + # Deprecated - use directive_load_path + def directive_paths + @directive_load_path + end + + # For use by Initializer to reset cleaned path + def directive_load_path=(path) #:nodoc: + @directive_load_path = path + end + + # Add an entry to the directive_load_path list for a directory + # containing directive implementation classes to be loaded + # into the MasterView processing configuration. + # + # Optionally specify options for the directives loaded + # from this directory: + # + # :default - metadata defaults + # + # Metadata defaults extend or override any defaults specified + # in the dir_path/.metadata file, if defined, allowing application + # customization of the default defaults. + # + def add_directive_path(dir_path, options=nil) + directive_load_path << DirectiveLoadPath::PathEntry.new( dir_path, options ) + end + # Relative path from +root_path+ of the temp directory used for creating # backup files before rebuilding/updating a template file. # # Set to nil to suppress backups. # @@ -331,16 +371,22 @@ # Path on this system to tidy library if <tt>:tidy</tt> parser option is enabled # so that masterview templates will be run through tidy before being parsed. # Allows invalid xhmtl to be corrected before masterview template parsing is performed. attr_accessor :tidy_path - # XML name space prefix for MasterView directive attributes in template html. + # XML name space prefix for builtin MasterView directive attributes in template html. # e.g. mv:generate="target.rhtml". # # Default: <tt>'mv:'</tt> attr_accessor :namespace_prefix + # XML name space prefix for MasterView extension directive attributes in template html. + # e.g. mvx:custom_directive="foo". + # + # Default: <tt>'mvx:'</tt> + attr_accessor :namespace_prefix_extensions + # Xhtml-safe substitution for '<%' in a masterview template # NOTE: you must also update inline_erb_substitution_regex if this is changed. # # Default: <tt>'{{{'</tt> attr_accessor :inline_erb_start @@ -420,10 +466,14 @@ # will be merged with these to arrive at the result. This allows us to easily # add new defaults in and even if users empty this hash, the defaults will get added # to disable they specifically set something to false or nil OriginalDefaultParserOptions = { :tidy => false, :escape_erb => true, :default_generate => true } # :nodoc: save the originals + # list of [ :log_level, msg ] pairs for config initialization/validation messages + # used by the initializer to validate load path and report any problems + attr_accessor :initialization_messages #:nodoc: + # Create a new Configuration instance, initialized with the default # values. # # Optional arguments to the constructor allow for initializing defaults # for a rails app when not actually running rails or for initializing @@ -436,18 +486,12 @@ # # The +app_root_path+ and +rails_app_root_path+ arguments are mutually exclusive. # Use +rails_app_root_path+ when operating on a Rails application which # isn't actually running; use +app_root_path+ for a non-rails application. # + def initialize( params={} ) #:nodoc: - # list of [ :log_level, msg ] pairs for config initialization/validation messages - # used by the initializer to validate load path and report any problems - attr_accessor :initialization_messages #:nodoc: - ####:invalid_directive_paths - - def initialize( params={} ) - rails_env = (defined?(RAILS_ENV)) ? RAILS_ENV : nil # unpack the supported keyword args app_root_path = params[:app_root_path] rails_app_root_path = params[:rails_app_root_path] env = params[:environment] || rails_env @@ -522,11 +566,12 @@ end # For a rails app, we have a point of view on where to find config files. # A standalone client needs to proactively tell us where to find their settings. self.config_dir_path = rails_app? ? "config/masterview" : nil self.environment = on_rails? ? ::RAILS_ENV : env - self.directive_paths = [ builtin_directives_path ] + self.directive_load_path = DirectiveLoadPath::Path.new + add_directive_path builtin_directives_path, { :use_masterview_namespace => true, } discover_standard_directive_path_additions() #TODO: if rails_app? && File.exist?( "#{rails_root_path}/app/masterview/directives" ) THEN append it as well self.rebuild_backups_tmp_dir_path = rails_app? ? File.join( rails_root_path, 'tmp/masterview/rebuild/backups') : nil # use Log4r by default if available, otherwise Logger from standard ruby library @@ -538,11 +583,11 @@ if debug_TRACE_HACK STDOUT.puts "...root_path=#{root_path}" STDOUT.puts "...config_dir_path=#{config_dir_path || 'nil'}" STDOUT.puts "...environment=#{environment || 'nil'}" - STDOUT.puts "...directive_paths=[ #{directive_paths.join(', ')} ]" + STDOUT.puts "...directive_load_path=[ #{directive_load_path.directory_paths.join(', ')} ]" end # template source options self.template_src_dir_path = rails_app? ? 'app/views' : 'masterview/templates' # bolts down abs ref self.template_filename_pattern = '*.html' @@ -573,10 +618,11 @@ self.handle_parse_exceptions = true self.default_parser_options = OriginalDefaultParserOptions.clone #we'll merge in whatever changes they make over the original set in const above # default locations where tidy likely to be found; assume on user's PATH if on Windows self.tidy_path = RUBY_PLATFORM =~ /mswin32/ ? 'c:/tidy/lib/tidy.dll' : '/usr/lib/libtidy.so' self.namespace_prefix = 'mv:' + self.namespace_prefix_extensions = 'mvx:' self.inline_erb_start = '{{{' self.inline_erb_end = '}}}' self.inline_erb_substitution_regex = /\{\{\{(([^}]|\}[^}]|\}\}[^}])*)\}\}\}/ # Rails application options @@ -613,11 +659,12 @@ def discover_standard_directive_path_additions() #:nodoc: return if ! rails_app? #?or can we take a point of view of std loc? e.g., 'masterview/directives' app_directives_path = rails_app? ? "app/masterview/directives" : nil #??"masterview/directives"? app_directives_path = File.join( root_path, app_directives_path ) if File.directory?(app_directives_path) #?and not empty? - directive_paths << app_directives_path #root_path already expanded + #{ :use_masterview_namespace => false, } + add_directive_path app_directives_path #root_path already expanded end end # The path to the application's config settings file. # By default the file is at <tt>config/masterview/settings.rb</tt>. @@ -727,11 +774,10 @@ install_config_settings # make a final check for running_rails? (in case config settings changed scripts spec) configuration.decide_if_running_rails # keep a permananent record of how we got started configuration.freeze - configuration.directive_paths.freeze MasterView.const_set('ConfigSettings', configuration) end # Load configuration settings from <tt>{config.config_dir_path}/settings.rb</tt> # and <tt>{config.config_dir_path}/environments/{config.environment}.rb</tt>. @@ -766,25 +812,19 @@ config = configuration #??config.root_path = File.expand_path(config.root_path) if config.root_path #?? ensure bolted down firmly? # ensure that the directive load path entries are clean and available - if not config.directive_paths.empty? - clean_paths = [] - config.directive_paths.each { | dir | - if dir.nil?: continue; end - if ! File.directory?(dir) - err_msg = "Invalid directive load path directory: '#{dir}'" - #overzealous: raise InvalidPathError.new(err_msg) - # just note the problem and let initializer report it later - config.initialization_messages << [ :error, err_msg ] - else - dir_path = File.expand_path( dir ) - clean_paths << dir_path if ! clean_paths.include?(dir_path) # no dups - end + if not config.directive_load_path.empty? + STDOUT.puts "###DEBUG: cleaning directive_load_path #{config.directive_load_path.inspect}" if DEBUG_DIRECTIVE_PATH + clean_path = DirectiveLoadPath.clean_path( config.directive_load_path, :dup_policy => :use_latest ) { | ex | + # record validation error from a path entry and press on + # just note the problem and let initializer report it later + config.initialization_messages << [ :error, ex.message ] } - config.directive_paths = clean_paths + config.directive_load_path = clean_path + STDOUT.puts "###DEBUG: CLEANED directive_load_path #{config.directive_load_path.inspect}" if DEBUG_DIRECTIVE_PATH end # template source and generation options if config.on_rails? #TODO: ensure that the config.template_dst_dir_path is @@ -807,28 +847,24 @@ end # Install the configuration settings def install_config_settings #:nodoc: set_module_constants + DirectiveLoadPath.default_path_specs = configuration.directive_load_path # does clone before freezing config end def set_module_constants #:nodoc: config = configuration - - # save configuration so we can get to it later - MasterView.const_set('LoadedConfiguration', config) # create loaded feature map - this map will track exactly what was loaded taking into account failures, so it can differ # from what is configured. key = feature symbol, value = true if enabled and loaded MasterView.const_set('LoadedFeatures', {} ) # we don't record root_path or config_dir_path - their purpose is satisfied # by the time we're done processing this installation configuration - MasterView.const_set('DefaultDirectiveLoadPaths', config.directive_paths.clone) # clone before freezing - # template source options MasterView.const_set('TemplateFilenamePattern', config.template_filename_pattern) # template generation options MasterView.const_set('OutputExtension', config.output_filename_extension) ###??IS THIS NEEDED??? @@ -837,37 +873,33 @@ # template parsing options MasterView.const_set('RescueExceptions', config.handle_parse_exceptions) MasterView.const_set('DefaultParserOptions', Configuration::OriginalDefaultParserOptions.merge(config.default_parser_options)) # merge in changes with original, so we can add more defaults later, users have to explicitly set an option to false to cancel them MasterView.const_set('TidyPath', config.tidy_path) - MasterView.const_set('NamespacePrefix', config.namespace_prefix) MasterView.const_set('InlineErbStart', config.inline_erb_start) MasterView.const_set('InlineErbEnd', config.inline_erb_end) MasterView.const_set('InlineErbSubstitutionRegex', config.inline_erb_substitution_regex) # Rails application options MasterView.const_set('ParseMasterViewTemplatesAtStartup', config.parse_masterview_templates_at_startup) MasterView.const_set('ReparseChangedMasterViewTemplates', config.reparse_changed_masterview_templates) MasterView.const_set('EnableMasterViewAdminPages', config.enable_admin_pages) MasterView.const_set('EnableMasterViewAdminViewRHTML', config.enable_view_rhtml) - # convenience constants for MV attributes involving output generation and imports - MasterView.const_set('GenerateAttribute', config.namespace_prefix + 'generate') - MasterView.const_set('ImportAttribute', config.namespace_prefix + 'import') - MasterView.const_set('GenRenderAttribute', config.namespace_prefix + 'gen_partial') - MasterView.const_set('ImportRenderAttribute', config.namespace_prefix + 'import_render') - end # Load the masterview code def load_plugin #:nodoc: require 'masterview' #:nodoc: end # Complete installation of masterview after its own code has been loaded def complete_plugin_installation #:nodoc: #?? return if MasterView.const_defined?(:Initialized) ?? + MasterView::DirectiveRegistry.register_default_namespaces( + configuration.namespace_prefix, + configuration.namespace_prefix_extensions ) initialize_logger initialize_mio #Back out experiment: causes load order problems ##load_directives # held off on this until logger is installed install_in_rails @@ -887,38 +919,39 @@ end # Initialize the MasterView I/O subsystem def initialize_mio config = configuration - MasterView.const_set('DefaultSerializer', MIOSerializer) + MasterView.const_set('DefaultSerializer', TemplateProcessing::MIOSerializer) # all root_path directory anchor points for I/O are expanded absolute paths - io_mgr = MIOTrees.new + io_mgr = MIO::MIOTrees.new template_extension = File.extname( config.template_filename_pattern ) - io_mgr.template = FileMIOTree.new( config.template_src_dir_path, template_extension, + io_mgr.template = MIO::FileMIOTree.new( config.template_src_dir_path, template_extension, :escape_erb => DefaultParserOptions[:escape_erb], # use DefaultParserOptions since already has config merged :tidy => DefaultParserOptions[:tidy], :default_generate => DefaultParserOptions[:default_generate], #TODO: expose the following in Configuration.default_parser_options and document :caching => false, :logging => true ) if config.generate_rhtml_files - io_mgr.erb = FileMIOTree.new( config.template_dst_dir_path, config.generated_file_default_extension, :logging => true) + io_mgr.erb = MIO::FileMIOTree.new( config.template_dst_dir_path, config.generated_file_default_extension, :logging => true) else - io_mgr.erb = RailsErbCacheMIOTree.new( config.generated_file_default_extension, :logging => true) + io_mgr.erb = MIO::RailsErbCacheMIOTree.new( config.generated_file_default_extension, :logging => true) end - io_mgr.backup = FileMIOTree.new( config.rebuild_backups_tmp_dir_path ) if config.rebuild_backups_tmp_dir_path + io_mgr.backup = MIO::FileMIOTree.new( config.rebuild_backups_tmp_dir_path ) if config.rebuild_backups_tmp_dir_path MasterView.const_set('IOMgr', io_mgr) MasterView::LoadedFeatures[:tidy_template_read] = config.default_parser_options[:tidy] end + #NOTE: not currently used - caused problems during startup, so reverted to original + # scheme where loading is triggered on demand by template parsing + # [SJL 20-Sep-2006] def load_directives #:nodoc: # get the directives loaded prior to firing up any template parsing return if ! configuration.on_rails? #ISSUE: causes problem for test cases; is this ever a good idea?? - MasterView::DirectivesRegistry.process_directives_load_path( - configuration.directive_paths, - configuration.namespace_prefix ) + MasterView::DirectiveRegistry.current.process_directives_load_path( configuration.directive_load_path ) end def install_in_rails #:nodoc: return if ! configuration.on_rails? enable_mv_admin_pages @@ -943,22 +976,35 @@ end MasterView::LoadedFeatures[:rails_parse_at_startup] = true end end + #-- + # DBC-style notation per DbC - rubydbc-0.1 (Andy Hunt's Design by Contract) + #pre( MasterView::ParseMasterViewTemplatesAtStartup ) + #pre( MasterView::ReparseChangedMasterViewTemplates ) + #pre( defined?(ActionController) && ActionController::Base.perform_caching ) + #++ def enable_reparse_changed_templates #:nodoc: if configuration.reparse_changed_masterview_templates #MasterView::ReparseChangedMasterViewTemplates # if not caching then check for masterview updates on every request - # assert not ActionController::Base.perform_caching + # DBC-style notation per DbC - rubydbc-0.1 (Andy Hunt's Design by Contract) + #pre( MasterView::ParseMasterViewTemplatesAtStartup ) + #pre( defined?(ActionController) && ActionController::Base.perform_caching ) require 'masterview/extras/watcher' #:nodoc: - require 'masterview/extras/init_rails_reparse_checking' + MasterView::Log.info { 'Adding hook to allow MasterView to check for templates that have changed when processing a request' } + require 'masterview/rails_ext/action_controller_reparse_checking' #:nodoc: + MasterView::LoadedFeatures[:rails_reparse_checking] = true end end def enable_rails_erb_direct #:nodoc: unless configuration.generate_rhtml_files # if not generating rhtml the read erb directly from masterview - require 'masterview/extras/init_rails_erb_mv_direct' + MasterView::Log.info { 'Adding hooks to enable Rails to read erb directly from MasterView' } + require 'masterview/rails_ext/action_view_erb_direct' #:nodoc: + require 'masterview/rails_ext/action_controller_erb_direct' #:nodoc: + MasterView::LoadedFeatures[:rails_erb_mv_direct] = true end end # Fires the user-supplied after_initialize block (Configuration#after_initialize) def after_initialize #:nodoc: