# -*- coding: utf-8 -*- # #-- # Copyright (C) 2009-2012 Thomas Leitner # # This file is part of kramdown. # # kramdown is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . #++ # module Kramdown # This module defines all options that are used by parsers and/or converters as well as providing # methods to deal with the options. module Options # Helper class introducing a boolean type for specifying boolean values (+true+ and +false+) as # option types. class Boolean # Return +true+ if +other+ is either +true+ or +false+ def self.===(other) FalseClass === other || TrueClass === other end end # ---------------------------- # :section: Option definitions # # This sections describes the methods that can be used on the Options module. # ---------------------------- # Struct class for storing the definition of an option. Definition = Struct.new(:name, :type, :default, :desc, :validator) # Allowed option types. ALLOWED_TYPES = [String, Integer, Float, Symbol, Boolean, Object] @options = {} # Define a new option called +name+ (a Symbol) with the given +type+ (String, Integer, Float, # Symbol, Boolean, Object), default value +default+ and the description +desc+. If a block is # specified, it should validate the value and either raise an error or return a valid value. # # The type 'Object' should only be used for complex types for which none of the other types # suffices. A block needs to be specified when using type 'Object' and it has to cope with # a value given as string and as the opaque type. def self.define(name, type, default, desc, &block) raise ArgumentError, "Option name #{name} is already used" if @options.has_key?(name) raise ArgumentError, "Invalid option type #{type} specified" if !ALLOWED_TYPES.include?(type) raise ArgumentError, "Invalid type for default value" if !(type === default) && !default.nil? raise ArgumentError, "Missing validator block" if type == Object && block.nil? @options[name] = Definition.new(name, type, default, desc, block) end # Return all option definitions. def self.definitions @options end # Return +true+ if an option called +name+ is defined. def self.defined?(name) @options.has_key?(name) end # Return a Hash with the default values for all options. def self.defaults temp = {} @options.each {|n, o| temp[o.name] = o.default} temp end # Merge the #defaults Hash with the *parsed* options from the given Hash, i.e. only valid option # names are considered and their value is run through the #parse method. def self.merge(hash) temp = defaults hash.each do |k,v| next unless @options.has_key?(k) temp[k] = parse(k, v) end temp end # Parse the given value +data+ as if it was a value for the option +name+ and return the parsed # value with the correct type. # # If +data+ already has the correct type, it is just returned. Otherwise it is converted to a # String and then to the correct type. def self.parse(name, data) raise ArgumentError, "No option named #{name} defined" if !@options.has_key?(name) if !(@options[name].type === data) data = data.to_s data = if @options[name].type == String data elsif @options[name].type == Integer Integer(data) rescue raise Kramdown::Error, "Invalid integer value for option '#{name}': '#{data}'" elsif @options[name].type == Float Float(data) rescue raise Kramdown::Error, "Invalid float value for option '#{name}': '#{data}'" elsif @options[name].type == Symbol data.strip! data = data[1..-1] if data[0] == ?: (data.empty? || data == 'nil' ? nil : data.to_sym) elsif @options[name].type == Boolean data.downcase.strip != 'false' && !data.empty? end end data = @options[name].validator[data] if @options[name].validator data end # ---------------------------- # :section: Option Validators # # This sections contains all pre-defined option validators. # ---------------------------- # Ensures that the option value +val+ for the option called +name+ is a valid array. The # parameter +val+ can be # # - a comma separated string which is split into an array of values # - or an array. # # Additionally, the array is checked for the correct size. def self.simple_array_validator(val, name, size) if String === val val = val.split(/,/) elsif !(Array === val) raise Kramdown::Error, "Invalid type #{val.class} for option #{name}" end if val.size != size raise Kramdown::Error, "Option #{name} needs exactly #{size} values" end val end # ---------------------------- # :section: Option Definitions # # This sections contains all option definitions that are used by the included # parsers/converters. # ---------------------------- define(:template, String, '', <hallo` the emphasis tag would normally be converted to an `:html` element with tag type `:em`. If `html_to_native` is `true`, then the emphasis would be converted to a native `:em` element. This is useful for converters that cannot deal with HTML elements. Default: false Used by: kramdown parser EOF define(:footnote_nr, Integer, 1, <