class Sane class Device def initialize(options) @name = options[:name] @vendor = options[:vendor] @model = options[:model] @type = options[:type] @handle = nil end def closed? @handle.nil? end def open? !closed? end def open ensure_closed! @handle = Sane.instance.send(:open, @name) if block_given? begin yield(self) ensure close end end end def close ensure_open! Sane.instance.send(:close, @handle) @handle = nil end def start ensure_open! Sane.instance.start(@handle) end def read ensure_open! Sane.instance.send(:read, @handle) end def option_count ensure_open! Sane.instance.send(:get_option, @handle, 0) end def parameters ensure_open! Sane.instance.send(:get_parameters, @handle) end def [](option) ensure_open! Sane.instance.send(:get_option, @handle, option_lookup(option)) end def []=(option, value) ensure_open! Sane.instance.send(:set_option, @handle, option_lookup(option), value) end def option_descriptors option_count.times.map { |i| Sane.instance.send(:get_option_descriptor, @handle, i) } end def option_names option_descriptors.map { |option| option[:name] } end def option_values option_count.times.map do |i| begin self[i] rescue Error nil # we can't get values of some options, ignore them end end end def options {}.tap { |hash| option_count.times { |i| hash[option_names[i]] = option_values[i] } } end def option_lookup(option_name) return option_name if (0..option_count).include?(option_name) option_descriptors.index { |option| option[:name] == option_name.to_s } or raise ArgumentError, "Option not found: #{option_name}" end def describe(option) option_descriptors[option_lookup(option)] end private def ensure_closed! raise "Device is already open" if open? end def ensure_open! raise "Device is closed" if closed? end end end