diff options
author | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2007-03-14 17:49:19 +0000 |
---|---|---|
committer | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2007-03-14 17:49:19 +0000 |
commit | df4595e98f953432267756c84a37a5495e9720ef (patch) | |
tree | deda10a6947d4c6ce4777d3f9ea9922a0ddc7a70 /lib/puppet/util/fileparsing.rb | |
parent | b05ae2ae1262469df264e3a35b30f7a1d1805c18 (diff) | |
download | puppet-df4595e98f953432267756c84a37a5495e9720ef.tar.gz puppet-df4595e98f953432267756c84a37a5495e9720ef.tar.xz puppet-df4595e98f953432267756c84a37a5495e9720ef.zip |
Significantly reworking the internals of the fileparsing code. It now
passes around an instance of a FileRecord, rather than just a hash, which I
think makes it much easier to understand.
Moved the sshkey parsed provider test to its own directory and made it better.
This work is all being done so I can move cron jobs to using providers
instead of the current unmaintainable state of affairs.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@2281 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib/puppet/util/fileparsing.rb')
-rw-r--r-- | lib/puppet/util/fileparsing.rb | 215 |
1 files changed, 136 insertions, 79 deletions
diff --git a/lib/puppet/util/fileparsing.rb b/lib/puppet/util/fileparsing.rb index 8d8d92df9..08f1c79d9 100644 --- a/lib/puppet/util/fileparsing.rb +++ b/lib/puppet/util/fileparsing.rb @@ -24,10 +24,75 @@ # You could then call 'parser.to_line(hash)' on any of those hashes to generate # the text line again. +require 'puppet/util/methodhelper' + module Puppet::Util::FileParsing include Puppet::Util attr_writer :line_separator, :trailing_separator + class FileRecord + include Puppet::Util + include Puppet::Util::MethodHelper + attr_accessor :absent, :joiner, :rts, + :separator, :rollup, :name, :match + + attr_reader :fields, :optional, :type + + INVALID_FIELDS = [:record_type, :target, :on_disk] + + # Customize this so we can do a bit of validation. + def fields=(fields) + @fields = fields.collect do |field| + r = symbolize(field) + if INVALID_FIELDS.include?(r) + raise ArgumentError.new("Cannot have fields named %s" % r) + end + r + end + end + + def initialize(type, options = {}, &block) + @type = symbolize(type) + unless [:record, :text].include?(@type) + raise ArgumentError, "Invalid record type %s" % @type + end + + set_options(options) + + if self.type == :record + # Now set defaults. + self.absent ||= "" + self.separator ||= /\s+/ + self.joiner ||= " " + self.optional ||= [] + unless defined? @rollup + @rollup = true + end + end + + if block_given? + meta_def(:process, &block) + end + end + + # Customize this so we can do a bit of validation. + def optional=(optional) + @optional = optional.collect do |field| + symbolize(field) + end + end + + # Create a hook that modifies the hash resulting from parsing. + def post_parse=(block) + meta_def(:post_parse, &block) + end + + # Create a hook that modifies the hash just prior to generation. + def pre_gen=(block) + meta_def(:pre_gen, &block) + end + end + # Clear all existing record definitions. Only used for testing. def clear_records @record_types.clear @@ -35,44 +100,45 @@ module Puppet::Util::FileParsing end def fields(type) - type = symbolize(type) - if @record_types.include?(type) - @record_types[type][:fields].dup + if record = record_type(type) + record.fields.dup else nil end end # Try to match a specific text line. - def handle_text_line(line, hash) - if line =~ hash[:match] - return {:record_type => hash[:name], :line => line} + def handle_text_line(line, record) + if line =~ record.match + return {:record_type => record.name, :line => line} else return nil end end # Try to match a record. - def handle_record_line(line, hash) - if method = hash[:method] - if ret = send(method, line.dup) - ret[:record_type] = hash[:name] + def handle_record_line(line, record) + if record.respond_to?(:process) + if ret = record.send(:process, line.dup) + unless ret.is_a?(Hash) + raise Puppet::DevError, + "Process record type %s returned non-hash" % record.name + end + ret[:record_type] = record.name return ret else return nil end - elsif regex = hash[:match] + elsif regex = record.match raise "Cannot use matches to handle records yet" # In this case, we try to match the whole line and then use the # match captures to get our fields. if match = regex.match(line) fields = [] - ignore = hash[:ignore] || [] - p match.captures + ignore = record.ignore || [] match.captures.each_with_index do |value, i| fields << value unless ignore.include? i end - p fields nil else Puppet.info "Did not match %s" % line @@ -80,21 +146,29 @@ module Puppet::Util::FileParsing end else ret = {} - sep = hash[:separator] + sep = record.separator # String "helpfully" replaces ' ' with /\s+/ in splitting, so we # have to work around it. if sep == " " sep = / / end - hash[:fields].zip(line.split(sep)) do |param, value| - if value and value != "" + line_fields = line.split(sep) + record.fields.each do |param| + value = line_fields.shift + if value and value != record.absent ret[param] = value else ret[param] = :absent end end - ret[:record_type] = hash[:name] + + unless line_fields.empty? or ! record.rollup + last_field = record.fields[-1] + val = ([ret[last_field]] + line_fields).join(record.joiner) + ret[last_field] = val + end + ret[:record_type] = record.name return ret end end @@ -127,18 +201,24 @@ module Puppet::Util::FileParsing end @record_order.each do |name| - hash = @record_types[name] - unless hash - raise Puppet::DevError, "Did not get hash for %s: %s" % + record = record_type(name) + unless record + raise Puppet::DevError, "Did not get record type for %s: %s" % [name, @record_types.inspect] end - method = "handle_%s_line" % hash[:type] + + # These are basically either text or record lines. + method = "handle_%s_line" % record.type if respond_to?(method) - if result = send(method, line, hash) + if result = send(method, line, record) + if record.respond_to?(:post_parse) + record.send(:post_parse, result) + end return result end else - raise Puppet::DevError, "Somehow got invalid line type %s" % hash[:type] + raise Puppet::DevError, + "Somehow got invalid line type %s" % record.type end end @@ -162,42 +242,10 @@ module Puppet::Util::FileParsing raise ArgumentError, "Must include a list of fields" end - invalidfields = [:record_type, :target, :on_disk] - options[:fields] = options[:fields].collect do |field| - r = symbolize(field) - if invalidfields.include?(r) - raise ArgumentError.new("Cannot have fields named %s" % r) - end - r - end - - options[:absent] ||= "" - - if options[:optional] - options[:optional] = options[:optional].collect { |f| symbolize(f) } - else - options[:optional] = [] - end - - options[:separator] ||= /\s+/ + record = FileRecord.new(:record, options, &block) + record.name = symbolize(name) - # Unless they specified a string-based joiner, just use a single - # space as the join value. - unless options[:separator].is_a?(String) or options[:joiner] - options[:joiner] = " " - end - - - if block_given? - method = "handle_record_line_%s" % name - if respond_to?(method) - raise "Already have a method defined for this record" - end - meta_def(method, &block) - options[:method] = method - end - - new_line_type(name, :record, options) + new_line_type(record) end # Are there any record types defined? @@ -206,12 +254,15 @@ module Puppet::Util::FileParsing end # Define a new type of text record. - def text_line(name, options) + def text_line(name, options, &block) unless options.include?(:match) raise ArgumentError, "You must provide a :match regex for text lines" end - new_line_type(name, :text, options) + record = FileRecord.new(:text, options, &block) + record.name = symbolize(name) + + new_line_type(record) end # Generate a file from a bunch of hash records. @@ -227,20 +278,25 @@ module Puppet::Util::FileParsing # Convert our parsed record into a text record. def to_line(details) - unless type = @record_types[details[:record_type]] + unless record = record_type(details[:record_type]) raise ArgumentError, "Invalid record type %s" % details[:record_type] end - case type[:type] + if record.respond_to?(:pre_gen) + details = details.dup + record.send(:pre_gen, details) + end + + case record.type when :text: return details[:line] else - joinchar = type[:joiner] || type[:separator] + joinchar = record.joiner - line = type[:fields].collect { |field| + line = record.fields.collect { |field| # If the field is marked absent, use the appropriate replacement if details[field] == :absent or details[field].nil? - if type[:optional].include?(field) - type[:absent] + if record.optional.include?(field) + record.absent else raise ArgumentError, "Field %s is required" % field end @@ -249,7 +305,7 @@ module Puppet::Util::FileParsing end }.reject { |c| c.nil?}.join(joinchar) - if regex = type[:rts] + if regex = record.rts # If they say true, then use whitespace; else, use their regex. if regex == true regex = /\s+$/ @@ -272,7 +328,7 @@ module Puppet::Util::FileParsing def valid_attr?(type, attr) type = symbolize(type) - if @record_types[type] and @record_types[type][:fields].include?(symbolize(attr)) + if record = record_type(type) and record.fields.include?(symbolize(attr)) return true else if symbolize(attr) == :ensure @@ -285,22 +341,23 @@ module Puppet::Util::FileParsing private # Define a new type of record. - def new_line_type(name, type, options) + def new_line_type(record) @record_types ||= {} @record_order ||= [] - name = symbolize(name) - - if @record_types.include?(name) - raise ArgumentError, "Line type %s is already defined" % name + if @record_types.include?(record.name) + raise ArgumentError, "Line type %s is already defined" % record.name end - options[:name] = name - options[:type] = type - @record_types[name] = options - @record_order << name + @record_types[record.name] = record + @record_order << record.name + + return record + end - return options + # Retrive the record object. + def record_type(type) + @record_types[symbolize(type)] end end |