dilcom/gnuplotrb

View on GitHub
lib/gnuplotrb/mixins/option_handling.rb

Summary

Maintainability
A
0 mins
Test Coverage
module GnuplotRB
  ##
  # This module contains methods which are mixed into several classes
  # to set, get and convert their options.
  module OptionHandling
    class << self
      # Some values of options should be quoted to be read by gnuplot properly
      #
      # @todo update list with data from gnuplot documentation !!!
      QUOTED_OPTIONS = %w(
        title
        output
        xlabel
        x2label
        ylabel
        y2label
        clabel
        cblabel
        zlabel
        rgb
        font
        background
        format
        format_x
        format_y
        format_xy
        format_x2
        format_y2
        format_z
        format_cb
        timefmt
        dt
        dashtype
      )

      private_constant :QUOTED_OPTIONS

      ##
      # Replace '_' with ' ' is made to allow passing several options
      # with the same first word of key. See issue #7 for more info.
      # @param key [Symbol, String] key to modify
      # @return [String] given key with '_' replaced with ' '
      def string_key(key)
        key.to_s.gsub(/_/) { ' ' } + ' '
      end

      ##
      # Recursive function that converts Ruby option to gnuplot string
      #
      # @param key [Symbol] name of option in gnuplot
      # @param option an option that should be converted
      # @example
      #   option_to_string(['png', size: [300, 300]])
      #   #=> 'png size 300,300'
      #   option_to_string(xrange: 0..100)
      #   #=> 'xrange [0:100]'
      #   option_to_string(multiplot: true)
      #   #=> 'multiplot'
      def option_to_string(key = nil, option)
        return string_key(key) if !!option == option # check for boolean
        value = ruby_class_to_gnuplot(option)
        value = "\"#{value}\"" if QUOTED_OPTIONS.include?(key.to_s)
        ## :+ here is necessary, because using #{value} will remove quotes
        value = string_key(key) + value if key
        value
      end

      ##
      # @private
      # Method for inner use.
      # Needed to convert several ruby classes into
      # value that should be piped to gnuplot.
      def ruby_class_to_gnuplot(option_object)
        case option_object
        when Array
          option_object.map { |el| option_to_string(el) }
                       .join(option_object[0].is_a?(Numeric) ? ',' : ' ')
        when Hash
          option_object.map { |i_key, i_val| option_to_string(i_key, i_val) }
                       .join(' ')
        when Range
          "[#{option_object.begin}:#{option_object.end}]"
        else
          option_object.to_s
        end
      end

      ##
      # Check if given terminal available for use.
      #
      # @param terminal [String] terminal to check (e.g. 'png', 'qt', 'gif')
      # @return [Boolean] true or false
      def valid_terminal?(terminal)
        Settings.available_terminals.include?(terminal)
      end

      ##
      # Check if given options are valid for gnuplot.
      # Raises ArgumentError if invalid options found.
      # Now checks only terminal name.
      #
      # @param options [Hash] options to check (e.g. "{ term: 'qt', title: 'Plot title' }")
      def validate_terminal_options(options)
        terminal = options[:term]
        return unless terminal
        terminal = terminal[0] if terminal.is_a?(Array)
        message = 'Seems like your Gnuplot does not ' \
                  "support that terminal (#{terminal}), please see " \
                  'supported terminals with Settings::available_terminals'
        fail(ArgumentError, message) unless valid_terminal?(terminal)
      end
    end

    ##
    # @private
    # You should implement #initialize in classes that use OptionsHelper
    def initialize(*_)
      fail NotImplementedError, 'You should implement #initialize' \
                                ' in classes that use OptionsHelper!'
    end

    ##
    # @private
    # You should implement #new_with_options in classes that use OptionsHelper
    def new_with_options(*_)
      fail NotImplementedError, 'You should implement #new_with_options' \
                                ' in classes that use OptionsHelper!'
    end

    ##
    # Create new Plot (or Dataset or Splot or Multiplot) object where current
    # options are merged with given. If no options
    # given it will just return existing set of options.
    #
    # @param options [Hash] options to add
    # @return [Dataset, Splot, Multiplot] new object created with given options
    # @return [Hamster::Hash] current options if given options empty
    # @example
    #   sin_graph = Plot.new(['sin(x)', title: 'Sin'], title: 'Sin on [0:3]', xrange: 0..3)
    #   sin_graph.plot
    #   sin_graph_update = sin_graph.options(title: 'Sin on [-10:10]', xrange: -10..10)
    #   sin_graph_update.plot
    #   # sin_graph IS NOT affected
    def options(**options)
      @options ||= Hamster::Hash.new
      if options.empty?
        @options
      else
        new_with_options(@options.merge(options))
      end
    end

    ##
    # Update existing Plot (or Dataset or Splot or Multiplot) object with given options.
    #
    # @param options [Hash] options to add
    # @return [Dataset, Splot, Multiplot] self
    # @example
    #   sin_graph = Plot.new(['sin(x)', title: 'Sin'], title: 'Sin on [0:3]', xrange: 0..3)
    #   sin_graph.plot
    #   sin_graph.options!(title: 'Sin on [-10:10]', xrange: -10..10)
    #   sin_graph.plot
    #   # second #plot call will plot not the same as first, sin_graph IS affected
    def options!(**options)
      @options = @options ? @options.merge(options) : Hamster::Hash.new(options)
      self
    end

    private

    ##
    # Return current option value if no value given. Create new
    # object with given option set if value given.
    def option(key, *value)
      if value.empty?
        value = options[key]
        value = value[0] if value && value.size == 1
        value
      else
        options(key => value)
      end
    end

    ##
    # Just set an option.
    def option!(key, *value)
      options!(key => value)
    end
  end
end