ruby-wmi 0.2.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/Gemfile +4 -0
- data/{README.txt → README.md} +21 -24
- data/Thorfile +86 -0
- data/lib/ruby-wmi.rb +85 -8
- data/lib/ruby-wmi/base.rb +355 -119
- data/lib/ruby-wmi/core_ext/time_ext.rb +8 -3
- data/lib/ruby-wmi/errors.rb +21 -0
- data/lib/ruby-wmi/{constants.rb → privilege.rb} +2 -2
- data/lib/ruby-wmi/version.rb +3 -0
- data/ruby-wmi.gemspec +21 -0
- data/spec/time_ext_spec.rb +19 -0
- data/spec/wql_spec.rb +80 -0
- data/web/public/css/active4d.css +114 -0
- data/web/public/css/all_hallows_eve.css +72 -0
- data/web/public/css/amy.css +147 -0
- data/web/public/css/blackboard.css +88 -0
- data/web/public/css/brilliance_black.css +605 -0
- data/web/public/css/brilliance_dull.css +599 -0
- data/web/public/css/cobalt.css +149 -0
- data/web/public/css/dawn.css +121 -0
- data/web/public/css/eiffel.css +121 -0
- data/web/public/css/espresso_libre.css +109 -0
- data/web/public/css/idle.css +62 -0
- data/web/public/css/iplastic.css +80 -0
- data/web/public/css/lazy.css +73 -0
- data/web/public/css/mac_classic.css +123 -0
- data/web/public/css/magicwb_amiga.css +104 -0
- data/web/public/css/pastels_on_dark.css +188 -0
- data/web/public/css/rspec.css +118 -0
- data/web/public/css/slush_poppies.css +85 -0
- data/web/public/css/spacecadet.css +51 -0
- data/web/public/css/sunburst.css +180 -0
- data/web/public/css/toader.css +285 -0
- data/web/public/css/twilight.css +137 -0
- data/web/public/css/zenburnesque.css +91 -0
- data/web/public/images/bg.gif +0 -0
- data/web/public/images/bullet.gif +0 -0
- data/web/public/images/footer_pic.gif +0 -0
- data/web/public/images/green_vr.gif +0 -0
- data/web/public/images/hr.gif +0 -0
- data/web/public/images/main.gif +0 -0
- data/web/public/images/nav_over.gif +0 -0
- data/web/public/images/nav_over_left.gif +0 -0
- data/web/public/images/nav_over_right.gif +0 -0
- data/web/public/images/nav_under.gif +0 -0
- data/web/public/images/your-face.gif +0 -0
- data/web/public/images/your-picture.gif +0 -0
- data/web/templates/index.html.erb +71 -0
- data/web/templates/template-1109.zip +0 -0
- metadata +109 -64
- data/History.txt +0 -25
- data/Manifest.txt +0 -14
- data/Rakefile +0 -62
- data/test/test_ruby-wmi.rb +0 -2
    
        data/.gitignore
    ADDED
    
    
    
        data/Gemfile
    ADDED
    
    
    
        data/{README.txt → README.md}
    RENAMED
    
    | @@ -1,26 +1,26 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
                by Gordon Thiesfeld
         | 
| 3 | 
            -
                http://ruby-wmi.rubyforge.org/
         | 
| 4 | 
            -
                gthiesfeld@gmail.com
         | 
| 1 | 
            +
            # Description
         | 
| 5 2 |  | 
| 6 | 
            -
             | 
| 3 | 
            +
            ruby-wmi is an ActiveRecord style interface for Microsoft's Windows
         | 
| 4 | 
            +
            Management Instrumentation provider.
         | 
| 7 5 |  | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 6 | 
            +
            Many of the methods in WMI::Base are borrowed directly, or with some
         | 
| 7 | 
            +
            modification from ActiveRecord.
         | 
| 8 | 
            +
              http://api.rubyonrails.org/classes/ActiveRecord/Base.html
         | 
| 10 9 |  | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
                http://api.rubyonrails.org/classes/ActiveRecord/Base.html
         | 
| 10 | 
            +
            The major tool in this library is the #find method.  For more
         | 
| 11 | 
            +
            information, see WMI::Base.
         | 
| 14 12 |  | 
| 15 | 
            -
             | 
| 16 | 
            -
              information, see WMI::Base.
         | 
| 13 | 
            +
            There is also a WMI.sublasses method included for reflection.
         | 
| 17 14 |  | 
| 18 | 
            -
             | 
| 15 | 
            +
            # Requirements
         | 
| 19 16 |  | 
| 20 | 
            -
             | 
| 17 | 
            +
            * Windows 2000 or newer
         | 
| 18 | 
            +
            * Ruby 1.8 / 1.9
         | 
| 21 19 |  | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 20 | 
            +
            # Synopsis
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            The following code sample kills all processes of a given name 
         | 
| 23 | 
            +
            (in this case, Notepad), except the oldest.
         | 
| 24 24 |  | 
| 25 25 | 
             
                require 'ruby-wmi'
         | 
| 26 26 |  | 
| @@ -31,21 +31,18 @@ ruby-wmi | |
| 31 31 | 
             
                morituri.shift
         | 
| 32 32 | 
             
                morituri.each{|p| p.terminate }
         | 
| 33 33 |  | 
| 34 | 
            -
             | 
| 34 | 
            +
            # Installation
         | 
| 35 35 |  | 
| 36 | 
            -
                 | 
| 37 | 
            -
                Ruby 1.8
         | 
| 36 | 
            +
                $ gem install ruby-wmi
         | 
| 38 37 |  | 
| 39 | 
            -
             | 
| 38 | 
            +
            # License and Authors
         | 
| 40 39 |  | 
| 41 | 
            -
             | 
| 40 | 
            +
            Maintainer:: Jamie Winsor (<jamie@vialstudios.com>)
         | 
| 42 41 |  | 
| 43 | 
            -
             | 
| 42 | 
            +
            Original Author:: Gordon Thiesfeld (<gthiesfeld@gmail.com>)
         | 
| 44 43 |  | 
| 45 44 | 
             
            (The MIT License)
         | 
| 46 45 |  | 
| 47 | 
            -
            Copyright (c) 2007 Gordon Thiesfeld
         | 
| 48 | 
            -
             | 
| 49 46 | 
             
            Permission is hereby granted, free of charge, to any person obtaining
         | 
| 50 47 | 
             
            a copy of this software and associated documentation files (the
         | 
| 51 48 | 
             
            'Software'), to deal in the Software without restriction, including
         | 
    
        data/Thorfile
    ADDED
    
    | @@ -0,0 +1,86 @@ | |
| 1 | 
            +
            $:.push File.expand_path("../lib", __FILE__)
         | 
| 2 | 
            +
            require 'ruby-wmi/version'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class Default < Thor
         | 
| 5 | 
            +
              class_option :verbose, 
         | 
| 6 | 
            +
                :type => :boolean, 
         | 
| 7 | 
            +
                :aliases => "-v", 
         | 
| 8 | 
            +
                :default => false
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              desc "build", "Build the ruby-wmi gem"
         | 
| 11 | 
            +
              def build
         | 
| 12 | 
            +
                invoke :clean
         | 
| 13 | 
            +
                
         | 
| 14 | 
            +
                sh("gem build -V '#{source_root.join("ruby-wmi.gemspec")}'", source_root)
         | 
| 15 | 
            +
                FileUtils.mv(Dir.glob("*.gem"), "pkg/")
         | 
| 16 | 
            +
              rescue => e
         | 
| 17 | 
            +
                say e, :red
         | 
| 18 | 
            +
                exit 1
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              desc "clean", "Clean the project"
         | 
| 22 | 
            +
              def clean
         | 
| 23 | 
            +
                FileUtils.mkdir_p(pkg_path)
         | 
| 24 | 
            +
                Dir[pkg_path.join("*")].each { |f| FileUtils.rm(f, :force => true) }
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              desc "release", "Create a tag from the gem version, build, and push the ruby-wmi gem to rubygems"
         | 
| 28 | 
            +
              def release
         | 
| 29 | 
            +
                unless clean?
         | 
| 30 | 
            +
                  say "There are files that need to be committed first.", :red
         | 
| 31 | 
            +
                  exit 1
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                invoke :clean
         | 
| 35 | 
            +
                invoke :build
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                tag_version {
         | 
| 38 | 
            +
                  sh("gem push #{pkg_path.join("ruby-wmi-#{RubyWMI::VERSION}.gem")}")
         | 
| 39 | 
            +
                }
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              private
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                def clean?
         | 
| 45 | 
            +
                  sh_with_excode("git diff --exit-code")[1] == 0
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                def pkg_path
         | 
| 49 | 
            +
                  source_root.join("pkg")
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def sh(cmd, dir = source_root, &block)
         | 
| 53 | 
            +
                  out, code = sh_with_excode(cmd, dir, &block)
         | 
| 54 | 
            +
                  code == 0 ? out : raise(out.empty? ? "Running `#{cmd}` failed. Run this command directly for more detailed output." : out)
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                def sh_with_excode(cmd, dir = source_root, &block)
         | 
| 58 | 
            +
                  cmd << " 2>&1"
         | 
| 59 | 
            +
                  outbuf = ''
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  Dir.chdir(dir) {
         | 
| 62 | 
            +
                    outbuf = `#{cmd}`
         | 
| 63 | 
            +
                    if $? == 0
         | 
| 64 | 
            +
                      block.call(outbuf) if block
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
                  }
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  [ outbuf, $? ]
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                def source_root
         | 
| 72 | 
            +
                  Pathname.new File.dirname(File.expand_path(__FILE__))
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
                
         | 
| 75 | 
            +
                def tag_version
         | 
| 76 | 
            +
                  sh "git tag -a -m \"Version #{RubyWMI::VERSION}\" #{RubyWMI::VERSION}"
         | 
| 77 | 
            +
                  say "Tagged: #{RubyWMI::VERSION}", :green
         | 
| 78 | 
            +
                  yield if block_given?
         | 
| 79 | 
            +
                  sh "git push --tags"
         | 
| 80 | 
            +
                rescue => e
         | 
| 81 | 
            +
                  say "Untagging: #{RubyWMI::VERSION} due to error", :red
         | 
| 82 | 
            +
                  sh_with_excode "git tag -d #{RubyWMI::VERSION}"
         | 
| 83 | 
            +
                  say e, :red
         | 
| 84 | 
            +
                  exit 1
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
            end
         | 
    
        data/lib/ruby-wmi.rb
    CHANGED
    
    | @@ -1,10 +1,87 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 1 | 
            +
            require 'ruby-wmi/version'
         | 
| 2 | 
            +
            require 'ruby-wmi/core_ext'
         | 
| 3 | 
            +
            require 'ruby-wmi/errors'
         | 
| 4 | 
            +
            require 'win32ole'
         | 
| 4 5 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
              $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
         | 
| 6 | 
            +
            WIN32OLE.codepage = WIN32OLE::CP_UTF8
         | 
| 7 7 |  | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 8 | 
            +
            module RubyWMI
         | 
| 9 | 
            +
              autoload :Privilege, 'ruby-wmi/privilege'
         | 
| 10 | 
            +
              autoload :Base, 'ruby-wmi/base'
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              # Returns an array containing all the WMI subclasses
         | 
| 13 | 
            +
              # on a sytem.  Defaults to localhost
         | 
| 14 | 
            +
              #
         | 
| 15 | 
            +
              #   WMI.subclasses
         | 
| 16 | 
            +
              #   => ["Win32_PrivilegesStatus", "Win32_TSNetworkAdapterSettingError", ...]
         | 
| 17 | 
            +
              #
         | 
| 18 | 
            +
              #  For a more human readable version of subclasses when using options:
         | 
| 19 | 
            +
              #
         | 
| 20 | 
            +
              #   WMI.subclasses_of(:host => 'some_computer')
         | 
| 21 | 
            +
              #   => ["Win32_PrivilegesStatus", "Win32_TSNetworkAdapterSettingError", ...]
         | 
| 22 | 
            +
              #
         | 
| 23 | 
            +
              #   WMI.subclasses_of(
         | 
| 24 | 
            +
              #      :host => :some_computer,
         | 
| 25 | 
            +
              #      :namespace => "root\\Microsoft\\SqlServer\\ComputerManagement"
         | 
| 26 | 
            +
              #    )
         | 
| 27 | 
            +
              def subclasses(options ={})
         | 
| 28 | 
            +
                options.merge!(:method => :SubclassesOf)
         | 
| 29 | 
            +
                instances_of(options).
         | 
| 30 | 
            +
                map{ |subclass| subclass.Path_.Class }
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
              alias :subclasses_of :subclasses
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              # Returns an array containing all the WMI providers
         | 
| 35 | 
            +
              # on a sytem.  Defaults to localhost
         | 
| 36 | 
            +
              #
         | 
| 37 | 
            +
              #   WMI.providers
         | 
| 38 | 
            +
              #
         | 
| 39 | 
            +
              #  For a more human readable version of providers when using options:
         | 
| 40 | 
            +
              #
         | 
| 41 | 
            +
              #   WMI.providers_of(:host => :some_computer)
         | 
| 42 | 
            +
              def providers(options ={})
         | 
| 43 | 
            +
                options.merge!(:method => :InstancesOf, :instance => "__Win32Provider")
         | 
| 44 | 
            +
                instances_of(options).
         | 
| 45 | 
            +
                map{ |provider| provider.name }.compact
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
              alias :providers_of :providers
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              # Returns an array containing all the WMI namespaces
         | 
| 50 | 
            +
              # on a sytem.  Defaults to localhost
         | 
| 51 | 
            +
              #
         | 
| 52 | 
            +
              #   WMI.namespaces
         | 
| 53 | 
            +
              #
         | 
| 54 | 
            +
              #  For a more human readable version of namespaces when using options:
         | 
| 55 | 
            +
              #
         | 
| 56 | 
            +
              #   WMI.namespaces_of(:host => :some_computer)
         | 
| 57 | 
            +
              #
         | 
| 58 | 
            +
              #   WMI.namespaces_of(:namespace => "root\\Microsoft")
         | 
| 59 | 
            +
              def namespaces(options ={})
         | 
| 60 | 
            +
                options.merge!(:method => :InstancesOf, :instance => "__NAMESPACE")
         | 
| 61 | 
            +
                options[:namespace] ||= 'root'
         | 
| 62 | 
            +
                instances_of(options).
         | 
| 63 | 
            +
                map{ |namespace|
         | 
| 64 | 
            +
                  namespace = "#{options[:namespace]}\\#{namespace.name}"
         | 
| 65 | 
            +
                  ns = namespaces(options.merge(:namespace => namespace)) rescue nil
         | 
| 66 | 
            +
                  [namespace, ns]
         | 
| 67 | 
            +
                }.flatten
         | 
| 68 | 
            +
              end
         | 
| 69 | 
            +
              alias :namespaces_of :namespaces
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              def instances_of(options)
         | 
| 72 | 
            +
                Base.set_connection(options)
         | 
| 73 | 
            +
                conn = Base.send(:connection)
         | 
| 74 | 
            +
                items = namespaces = conn.send(options[:method], options[:instance])
         | 
| 75 | 
            +
                Base.send(:clear_connection_options)
         | 
| 76 | 
            +
                items
         | 
| 77 | 
            +
              end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              extend self
         | 
| 80 | 
            +
             | 
| 81 | 
            +
              def const_missing(name)
         | 
| 82 | 
            +
                self.const_set(name, Class.new(self::Base))
         | 
| 83 | 
            +
              end
         | 
| 84 | 
            +
            end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
            # Alias for {RubyWMI}
         | 
| 87 | 
            +
            WMI = RubyWMI
         | 
    
        data/lib/ruby-wmi/base.rb
    CHANGED
    
    | @@ -1,66 +1,38 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
            module WMI
         | 
| 4 | 
            -
             | 
| 5 | 
            -
              # Generic WMI exception class.
         | 
| 6 | 
            -
              class WMIError < StandardError
         | 
| 7 | 
            -
              end
         | 
| 8 | 
            -
             | 
| 9 | 
            -
              # Invalid Class exception class.
         | 
| 10 | 
            -
              class InvalidClass < WMIError
         | 
| 11 | 
            -
              end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
              # Invalid Query exception class.
         | 
| 14 | 
            -
              class InvalidQuery < WMIError
         | 
| 15 | 
            -
              end
         | 
| 16 | 
            -
             | 
| 17 | 
            -
              # Returns an array conating all the WMI subclasses
         | 
| 18 | 
            -
              # on a sytem.  Defaults to localhost
         | 
| 19 | 
            -
              #
         | 
| 20 | 
            -
              #   WMI.subclasses
         | 
| 21 | 
            -
              #   => ["Win32_PrivilegesStatus", "Win32_TSNetworkAdapterSettingError", ...]
         | 
| 22 | 
            -
              #
         | 
| 23 | 
            -
              #  For a more human readable version of subclasses when using options:
         | 
| 24 | 
            -
              #
         | 
| 25 | 
            -
              #   WMI.subclasses_of(:host => some_computer)
         | 
| 26 | 
            -
              #   => ["Win32_PrivilegesStatus", "Win32_TSNetworkAdapterSettingError", ...]
         | 
| 27 | 
            -
              def subclasses(options ={})
         | 
| 28 | 
            -
                Base.set_connection(options)
         | 
| 29 | 
            -
                b = Base.send(:connection)
         | 
| 30 | 
            -
                b.SubclassesOf.map { |subclass| class_name = subclass.Path_.Class }
         | 
| 31 | 
            -
              end
         | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
              alias :subclasses_of :subclasses
         | 
| 35 | 
            -
             | 
| 36 | 
            -
              extend self
         | 
| 37 | 
            -
             | 
| 38 | 
            -
              class Base
         | 
| 1 | 
            +
            module RubyWMI
         | 
| 39 2 | 
             
              # Many of the methods in Base are borrowed directly, or with some modification from ActiveRecord
         | 
| 40 3 | 
             
              #   http://api.rubyonrails.org/classes/ActiveRecord/Base.html
         | 
| 41 | 
            -
             | 
| 42 | 
            -
                class << self
         | 
| 43 | 
            -
             | 
| 4 | 
            +
              class Base
         | 
| 5 | 
            +
                class << self    
         | 
| 44 6 | 
             
                  # #find_by_wql currently only works when called through #find
         | 
| 45 7 | 
             
                  # it may stay like that too.  I haven't decided.
         | 
| 46 8 | 
             
                  def find_by_wql(query)
         | 
| 9 | 
            +
                    # TODO: add logging, ie:
         | 
| 10 | 
            +
                    logger.debug query if logger
         | 
| 47 11 | 
             
                    d = connection.ExecQuery(query)
         | 
| 48 12 | 
             
                    begin
         | 
| 49 13 | 
             
                      d.count # needed to check for errors.  Weird, but it works.
         | 
| 50 14 | 
             
                    rescue => error
         | 
| 51 15 | 
             
                      case error.to_s
         | 
| 52 | 
            -
                      when /Invalid class/i | 
| 53 | 
            -
             | 
| 16 | 
            +
                      when /Invalid class/i
         | 
| 17 | 
            +
                        raise InvalidClass
         | 
| 18 | 
            +
                      when /Invalid query/i
         | 
| 19 | 
            +
                        raise InvalidQuery
         | 
| 20 | 
            +
                      when /Invalid namespace/i
         | 
| 21 | 
            +
                        raise InvalidNameSpace            
         | 
| 54 22 | 
             
                      end
         | 
| 55 23 | 
             
                    end
         | 
| 56 | 
            -
                     | 
| 24 | 
            +
                    clear_connection_options
         | 
| 25 | 
            +
                    d.map{|wmi_object| new(wmi_object) }
         | 
| 57 26 | 
             
                  end
         | 
| 58 27 |  | 
| 59 | 
            -
                  #  WMI:: | 
| 28 | 
            +
                  #  WMI::Win32_ComputerSystem.find(:all)
         | 
| 60 29 | 
             
                  #    returns an array of Win32ComputerSystem objects
         | 
| 61 30 | 
             
                  #
         | 
| 62 | 
            -
                  #  WMI:: | 
| 63 | 
            -
                  #    returns  | 
| 31 | 
            +
                  #  WMI::Win32_ComputerSystem.find(:first)
         | 
| 32 | 
            +
                  #    returns the first Win32_ComputerSystem object
         | 
| 33 | 
            +
                  #
         | 
| 34 | 
            +
                  #  WMI::Win32_ComputerSystem.find(:last)
         | 
| 35 | 
            +
                  #    returns the last Win32_ComputerSystem object
         | 
| 64 36 | 
             
                  #
         | 
| 65 37 | 
             
                  #  options:
         | 
| 66 38 | 
             
                  #
         | 
| @@ -68,113 +40,377 @@ module WMI | |
| 68 40 | 
             
                  #
         | 
| 69 41 | 
             
                  #     Conditions can either be specified as a string, array, or hash representing the WHERE-part of an SQL statement.
         | 
| 70 42 | 
             
                  #     The array form is to be used when the condition input is tainted and requires sanitization. The string form can
         | 
| 71 | 
            -
                  #     be used for statements that don't involve tainted data. The hash form works much like the array form | 
| 72 | 
            -
                  # | 
| 43 | 
            +
                  #     be used for statements that don't involve tainted data. The hash form works much like the array form.
         | 
| 44 | 
            +
                  #
         | 
| 45 | 
            +
                  #       Hash examples:
         | 
| 46 | 
            +
                  #
         | 
| 47 | 
            +
                  #         WMI::Win32_ComputerSystem.find(:all, :conditions => {:drive_type => 3} )
         | 
| 48 | 
            +
                  #         WMI::Win32_NTLogEvent.find(:all, :conditions => {:time_written => time_range})
         | 
| 49 | 
            +
                  #
         | 
| 50 | 
            +
                  #       Array examples:
         | 
| 73 51 | 
             
                  #
         | 
| 74 | 
            -
                  # | 
| 75 | 
            -
                  # | 
| 76 | 
            -
                  # | 
| 52 | 
            +
                  #         WMI::Win32_ComputerSystem.find(:all, :conditions => ['DriveType = ?', 3] )
         | 
| 53 | 
            +
                  #         WMI::Win32_Service.all(:conditions => ["StartMode = ? AND Started = ?", 'manual', true])
         | 
| 54 | 
            +
                  #         WMI::Win32_Process.all(:conditions => ["Name LIKE  %s", 'G%'])
         | 
| 55 | 
            +
                  #
         | 
| 56 | 
            +
                  #       String example:
         | 
| 57 | 
            +
                  #
         | 
| 58 | 
            +
                  #         WMI::Win32_ComputerSystem.find(:all, :conditions => 'DriveType = 3' )
         | 
| 59 | 
            +
                  #
         | 
| 60 | 
            +
                  #    :select
         | 
| 61 | 
            +
                  #      By default, this is "*" as in "SELECT * FROM", but can be changed.
         | 
| 62 | 
            +
                  #      Takes a string or symbol with a single column (e.g. "id"), or an array with 
         | 
| 63 | 
            +
                  #      multiple columns, (e.g. [:start_mode, :start_name, :status] )
         | 
| 64 | 
            +
                  #      Column names are converted from underscore to camelcase, so :start_mode
         | 
| 65 | 
            +
                  #      becomes StartMode
         | 
| 77 66 | 
             
                  #
         | 
| 78 67 | 
             
                  #    :host       - computername, defaults to localhost
         | 
| 79 | 
            -
                  #    : | 
| 68 | 
            +
                  #    :namespace  - swebm namespace , defaults to 'root\\cimv2'
         | 
| 80 69 | 
             
                  #    :privileges - see WMI::Privilege for a list of privileges
         | 
| 81 70 | 
             
                  #    :user       - username (domain\\username)
         | 
| 82 71 | 
             
                  #    :password   - password
         | 
| 83 72 | 
             
                  def find(arg=:all, options={})
         | 
| 84 73 | 
             
                    set_connection options
         | 
| 85 74 | 
             
                    case arg
         | 
| 86 | 
            -
             | 
| 87 | 
            -
                       | 
| 75 | 
            +
                    when :all
         | 
| 76 | 
            +
                      find_all(options)
         | 
| 77 | 
            +
                    when :first
         | 
| 78 | 
            +
                      find_first(options)
         | 
| 79 | 
            +
                    when :last
         | 
| 80 | 
            +
                      find_last(options)
         | 
| 81 | 
            +
                    when String
         | 
| 82 | 
            +
                      options.merge!(:conditions => { :name => arg })
         | 
| 83 | 
            +
                      find_first(options)
         | 
| 88 84 | 
             
                    end
         | 
| 89 85 | 
             
                  end
         | 
| 90 86 |  | 
| 91 | 
            -
                   | 
| 92 | 
            -
             | 
| 87 | 
            +
                  # an alias for find(:last)      
         | 
| 88 | 
            +
                  def last(options={})
         | 
| 89 | 
            +
                    find(:last, options)
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
                  
         | 
| 92 | 
            +
                  # an alias for find(:first)      
         | 
| 93 | 
            +
                  def first(options={})
         | 
| 94 | 
            +
                    find(:first, options)
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
                  
         | 
| 97 | 
            +
                  # an alias for find(:all)
         | 
| 98 | 
            +
                  def all(options={})
         | 
| 99 | 
            +
                    find(:all, options)
         | 
| 93 100 | 
             
                  end
         | 
| 94 101 |  | 
| 95 | 
            -
                  def  | 
| 96 | 
            -
                     | 
| 102 | 
            +
                  def count(options={})
         | 
| 103 | 
            +
                    find(:all, options).size
         | 
| 97 104 | 
             
                  end
         | 
| 105 | 
            +
                  
         | 
| 106 | 
            +
                  def method_missing(method_id, *arguments)
         | 
| 107 | 
            +
                    if match = /find_(all_by|by)_([_a-zA-Z]\w*)/.match(method_id.to_s)
         | 
| 108 | 
            +
                      case match[1]
         | 
| 109 | 
            +
                      when 'all_by'
         | 
| 110 | 
            +
                        conditions = match[2].split('_and_').zip(arguments)
         | 
| 111 | 
            +
                        conditions = Hash[*conditions.flatten]
         | 
| 112 | 
            +
                        all(:conditions => conditions)
         | 
| 113 | 
            +
                      when 'by'
         | 
| 114 | 
            +
                        first(:conditions => {match[2] => arguments.first})
         | 
| 115 | 
            +
                      end          
         | 
| 116 | 
            +
                    else
         | 
| 117 | 
            +
                      super
         | 
| 118 | 
            +
                    end
         | 
| 119 | 
            +
                  end
         | 
| 120 | 
            +
             | 
| 98 121 |  | 
| 99 | 
            -
                  def set_connection(options)
         | 
| 100 | 
            -
                    @host | 
| 101 | 
            -
                    @ | 
| 102 | 
            -
                    @ | 
| 122 | 
            +
                  def set_connection(options={})
         | 
| 123 | 
            +
                    @host       = options[:host].to_s    || connection_options[:host].to_s
         | 
| 124 | 
            +
                    @user       = options[:user]         || connection_options[:user]
         | 
| 125 | 
            +
                    @password   = options[:password]     || connection_options[:password]
         | 
| 126 | 
            +
                    @namespace  = options[:namespace]    || self.namespace 
         | 
| 103 127 | 
             
                    @privileges = options[:privileges]
         | 
| 104 128 | 
             
                  end
         | 
| 129 | 
            +
                  
         | 
| 130 | 
            +
                  def clear_connection_options
         | 
| 131 | 
            +
                    @host       = nil
         | 
| 132 | 
            +
                    @user       = nil
         | 
| 133 | 
            +
                    @password   = nil
         | 
| 134 | 
            +
                    @namespace  = nil
         | 
| 135 | 
            +
                    @privileges = nil
         | 
| 136 | 
            +
                    connection_options.clear
         | 
| 137 | 
            +
                  end
         | 
| 138 | 
            +
                  
         | 
| 139 | 
            +
                  def set_wmi_class_name(name)
         | 
| 140 | 
            +
                    @subclass_name = name
         | 
| 141 | 
            +
                  end
         | 
| 142 | 
            +
                  
         | 
| 143 | 
            +
                  def set_wmi_namespace(namespace)
         | 
| 144 | 
            +
                    @namespace_ = namespace
         | 
| 145 | 
            +
                  end
         | 
| 105 146 |  | 
| 106 | 
            -
             | 
| 147 | 
            +
                  def namespace
         | 
| 148 | 
            +
                    @namespace_ ||= 'root\\cimv2'
         | 
| 149 | 
            +
                  end
         | 
| 107 150 |  | 
| 108 151 | 
             
                  def subclass_name
         | 
| 109 | 
            -
                    self.name.split('::').last
         | 
| 152 | 
            +
                    @subclass_name ||= self.name.split('::').last
         | 
| 110 153 | 
             
                  end
         | 
| 111 | 
            -
             | 
| 112 | 
            -
                  def  | 
| 113 | 
            -
                     | 
| 114 | 
            -
                     | 
| 115 | 
            -
             | 
| 154 | 
            +
                  
         | 
| 155 | 
            +
                  def host(hostname)
         | 
| 156 | 
            +
                    connection_options[:host] = hostname.to_s
         | 
| 157 | 
            +
                    self
         | 
| 158 | 
            +
                  end
         | 
| 159 | 
            +
                  
         | 
| 160 | 
            +
                  def connection_options
         | 
| 161 | 
            +
                    @connection_options ||= {}
         | 
| 162 | 
            +
                  end
         | 
| 163 | 
            +
                
         | 
| 164 | 
            +
                  def columns
         | 
| 165 | 
            +
                    @columns ||= first(:from => 'meta_class', :conditions => "__this ISA '#{subclass_name}'").attribute_names
         | 
| 166 | 
            +
                  end
         | 
| 167 | 
            +
                    
         | 
| 168 | 
            +
                  def writable?(key)
         | 
| 169 | 
            +
                    obj_def = connection.get(subclass_name)
         | 
| 170 | 
            +
                    key = camelize(key)
         | 
| 171 | 
            +
                    key_prop = obj_def.properties_(key)
         | 
| 172 | 
            +
                    key_prop.qualifiers_.each do |q|
         | 
| 173 | 
            +
                      return true if q.name == 'write'
         | 
| 174 | 
            +
                    end
         | 
| 175 | 
            +
                    false
         | 
| 176 | 
            +
                  end
         | 
| 177 | 
            +
                  
         | 
| 178 | 
            +
                  def logger
         | 
| 179 | 
            +
                    @@logger ||= nil
         | 
| 180 | 
            +
                  end
         | 
| 181 | 
            +
                  
         | 
| 182 | 
            +
                  def logger=(logger)
         | 
| 183 | 
            +
                    @@logger = logger
         | 
| 116 184 | 
             
                  end
         | 
| 117 185 |  | 
| 118 | 
            -
                   | 
| 119 | 
            -
                    #~ scope = scope(:find)
         | 
| 120 | 
            -
                    sql  = "SELECT #{options[:select] || '*'} "
         | 
| 121 | 
            -
                    sql << "FROM #{options[:from] || subclass_name} "
         | 
| 186 | 
            +
                  private
         | 
| 122 187 |  | 
| 123 | 
            -
                     | 
| 124 | 
            -
             | 
| 188 | 
            +
                    def connection
         | 
| 189 | 
            +
                      @c ||= WIN32OLE.new("WbemScripting.SWbemLocator")
         | 
| 190 | 
            +
                      @privileges.each { |priv| @c.security_.privileges.add(priv, true) } if @privileges
         | 
| 191 | 
            +
                      log_connection if logger
         | 
| 192 | 
            +
                      @c.ConnectServer(@host,@namespace,@user,@password)
         | 
| 193 | 
            +
                    end
         | 
| 194 | 
            +
                    
         | 
| 195 | 
            +
                    # logs SWbemLocator.ConnectServer parameters
         | 
| 196 | 
            +
                    # default parameters aren't logged.
         | 
| 197 | 
            +
                    def log_connection
         | 
| 198 | 
            +
                      msg =  ""
         | 
| 199 | 
            +
                      msg <<  "Host: #{@host.inspect}, "              unless @host.empty?
         | 
| 200 | 
            +
                      msg << "Namespace: #{@namespace.inspect}, "     unless @namespace == "root\\cimv2" && @host.empty?
         | 
| 201 | 
            +
                      msg << "User: #{@user.inspect}, "               if @user
         | 
| 202 | 
            +
                      msg << "Password: #{@password.gsub(/./,'#')}, " if @password
         | 
| 203 | 
            +
                      msg << "Privileges: #{@privileges.inspect}"     if @privileges
         | 
| 204 | 
            +
                      logger.debug msg unless msg.empty?
         | 
| 205 | 
            +
                    end
         | 
| 125 206 |  | 
| 126 | 
            -
                     | 
| 207 | 
            +
                    def find_first(options={})
         | 
| 208 | 
            +
                      find_all(options).first
         | 
| 209 | 
            +
                    end
         | 
| 210 | 
            +
                    
         | 
| 211 | 
            +
                    def find_last(options={})
         | 
| 212 | 
            +
                      find_all(options).last
         | 
| 213 | 
            +
                    end
         | 
| 127 214 |  | 
| 128 | 
            -
                     | 
| 129 | 
            -
             | 
| 130 | 
            -
                     | 
| 215 | 
            +
                    def find_all(options={})
         | 
| 216 | 
            +
                      find_by_wql(construct_finder_sql(options))
         | 
| 217 | 
            +
                    end
         | 
| 131 218 |  | 
| 132 | 
            -
                     | 
| 133 | 
            -
             | 
| 219 | 
            +
                    def construct_finder_sql(options)
         | 
| 220 | 
            +
                      [
         | 
| 221 | 
            +
                        select(options[:select]),
         | 
| 222 | 
            +
                        from(options[:from]),
         | 
| 223 | 
            +
                        conditions(options[:conditions], nil),
         | 
| 224 | 
            +
                        group(options[:group])
         | 
| 225 | 
            +
                       ].compact.join(' ')
         | 
| 226 | 
            +
                    end
         | 
| 227 | 
            +
                    
         | 
| 228 | 
            +
                    def select(*selectors)
         | 
| 229 | 
            +
                      selectors = selectors.compact.empty? ? '*' : selectors.flatten.map{|i| camelize(i) }.join(', ')
         | 
| 230 | 
            +
                      "SELECT #{selectors}"
         | 
| 231 | 
            +
                    end
         | 
| 232 | 
            +
                    
         | 
| 233 | 
            +
                    def from(wmi_class)
         | 
| 234 | 
            +
                      "FROM #{wmi_class || subclass_name}"
         | 
| 235 | 
            +
                    end
         | 
| 134 236 |  | 
| 135 | 
            -
             | 
| 136 | 
            -
             | 
| 137 | 
            -
             | 
| 138 | 
            -
             | 
| 139 | 
            -
             | 
| 140 | 
            -
                     | 
| 141 | 
            -
                     | 
| 142 | 
            -
                     | 
| 143 | 
            -
             | 
| 144 | 
            -
             | 
| 237 | 
            +
                    def conditions(conditions, scope = :auto)
         | 
| 238 | 
            +
                      segments = []
         | 
| 239 | 
            +
                      segments << sanitize_sql(conditions) unless conditions.nil?
         | 
| 240 | 
            +
                      segments.compact!
         | 
| 241 | 
            +
                      "WHERE #{segments.join(") AND (")}".gsub(/\\/, '\&\&') unless segments.empty?
         | 
| 242 | 
            +
                    end
         | 
| 243 | 
            +
                    
         | 
| 244 | 
            +
                    def group(items)
         | 
| 245 | 
            +
                      "GROUP BY #{items}" if items
         | 
| 246 | 
            +
                    end
         | 
| 145 247 |  | 
| 146 | 
            -
             | 
| 147 | 
            -
             | 
| 148 | 
            -
             | 
| 149 | 
            -
             | 
| 150 | 
            -
             | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
             | 
| 155 | 
            -
             | 
| 248 | 
            +
                    # Accepts an array, hash, or string of sql conditions and sanitizes
         | 
| 249 | 
            +
                    # them into a valid SQL fragment.
         | 
| 250 | 
            +
                    #   ["name='%s' and device_id='%s'", "foo'bar", 4]  returns  "name='foo''bar' AND DeviceId='4'"
         | 
| 251 | 
            +
                    #   { :name => "foo'bar", :device_id => 4 }  returns "name='foo''bar' AND DeviceId='4'"
         | 
| 252 | 
            +
                    #   "name='foo''bar' AND DeviceId='4'" returns "name='foo''bar' AND DeviceId='4'"
         | 
| 253 | 
            +
                    def sanitize_sql(condition)
         | 
| 254 | 
            +
                      case condition
         | 
| 255 | 
            +
                        when Array
         | 
| 256 | 
            +
                          sanitize_sql_array(condition)
         | 
| 257 | 
            +
                        when Hash
         | 
| 258 | 
            +
                          sanitize_sql_hash(condition)
         | 
| 259 | 
            +
                        else        condition
         | 
| 260 | 
            +
                      end
         | 
| 156 261 | 
             
                    end
         | 
| 157 | 
            -
                  end
         | 
| 158 262 |  | 
| 159 | 
            -
             | 
| 160 | 
            -
             | 
| 161 | 
            -
             | 
| 162 | 
            -
             | 
| 163 | 
            -
             | 
| 164 | 
            -
             | 
| 165 | 
            -
             | 
| 166 | 
            -
             | 
| 167 | 
            -
                       | 
| 168 | 
            -
             | 
| 263 | 
            +
                    # Sanitizes a hash of attribute/value pairs into SQL conditions.
         | 
| 264 | 
            +
                    #   { :name => "foo'bar", :device_id => 4 }
         | 
| 265 | 
            +
                    #     # => "name='foo''bar' AND DeviceId= 4"
         | 
| 266 | 
            +
                    #   { :status => nil, :device_id => [1,2,3] }
         | 
| 267 | 
            +
                    #     # => "status IS NULL AND DeviceId = '1' OR DeviceId = '2' OR DeviceId = '3'"
         | 
| 268 | 
            +
                    def sanitize_sql_hash(attrs)
         | 
| 269 | 
            +
                      conditions = attrs.map do |attr, value|
         | 
| 270 | 
            +
                        attribute_condition(camelize(attr), value)
         | 
| 271 | 
            +
                      end.join(' AND ')
         | 
| 272 | 
            +
                      replace_bind_variables(conditions, expand_range_bind_variables(attrs.values))
         | 
| 273 | 
            +
                    end
         | 
| 274 | 
            +
                    
         | 
| 275 | 
            +
                    def sanitize_sql_array(ary)
         | 
| 276 | 
            +
                      statement, *values = ary
         | 
| 277 | 
            +
                      if values.first.is_a?(Hash) and statement =~ /:\w+/
         | 
| 278 | 
            +
                        replace_named_bind_variables(statement, values.first)
         | 
| 279 | 
            +
                      elsif statement.include?('?')
         | 
| 280 | 
            +
                        replace_bind_variables(statement, values)
         | 
| 281 | 
            +
                      else
         | 
| 282 | 
            +
                        statement % values.collect { |value| "'#{value}'" }
         | 
| 283 | 
            +
                      end
         | 
| 284 | 
            +
                    end 
         | 
| 285 | 
            +
                    
         | 
| 286 | 
            +
                    def replace_bind_variables(statement, values) #:nodoc:
         | 
| 287 | 
            +
                      raise WMIError.new("Mismatched arity #{statement}:#{values.inspect}") unless statement.count('?') == values.size
         | 
| 288 | 
            +
                      bound = values.dup
         | 
| 289 | 
            +
                      statement.gsub('?') { quote(bound.shift) }
         | 
| 290 | 
            +
                    end
         | 
| 291 | 
            +
                    
         | 
| 292 | 
            +
                    def attribute_condition(column_name, argument)
         | 
| 293 | 
            +
                      case argument
         | 
| 294 | 
            +
                        when nil   then "#{column_name} IS ?"
         | 
| 295 | 
            +
                        when Array then argument.map{|a| "#{column_name} = ? "}.join(" OR ")
         | 
| 296 | 
            +
                        when Range then if argument.exclude_end?
         | 
| 297 | 
            +
                                          "#{column_name} >= ? AND #{column_name} < ?"
         | 
| 298 | 
            +
                                        else
         | 
| 299 | 
            +
                                          "#{column_name} >= ? AND #{column_name} <= ?"
         | 
| 300 | 
            +
                                        end
         | 
| 301 | 
            +
                        else           
         | 
| 302 | 
            +
                          "#{column_name} = ?"
         | 
| 303 | 
            +
                      end
         | 
| 304 | 
            +
                    end
         | 
| 305 | 
            +
                    
         | 
| 306 | 
            +
                    def expand_range_bind_variables(bind_vars)
         | 
| 307 | 
            +
                      expanded = []
         | 
| 308 | 
            +
             | 
| 309 | 
            +
                      bind_vars.each do |var|
         | 
| 310 | 
            +
                        next if var.is_a?(Hash)
         | 
| 169 311 |  | 
| 170 | 
            -
             | 
| 171 | 
            -
             | 
| 312 | 
            +
                        if var.is_a?(Range)
         | 
| 313 | 
            +
                          expanded << var.first
         | 
| 314 | 
            +
                          expanded << var.last
         | 
| 315 | 
            +
                        elsif var.is_a?(Array)
         | 
| 316 | 
            +
                          expanded = var
         | 
| 317 | 
            +
                        else
         | 
| 318 | 
            +
                          expanded << var
         | 
| 319 | 
            +
                        end
         | 
| 320 | 
            +
                      end
         | 
| 321 | 
            +
             | 
| 322 | 
            +
                      expanded
         | 
| 323 | 
            +
                    end
         | 
| 324 | 
            +
                    
         | 
| 325 | 
            +
                    def quote(item)
         | 
| 326 | 
            +
                      case item
         | 
| 327 | 
            +
                        when NilClass
         | 
| 328 | 
            +
                          "NULL" 
         | 
| 329 | 
            +
                        when Time
         | 
| 330 | 
            +
                          "'#{item.to_swbem_date_time}'"
         | 
| 331 | 
            +
                        else  
         | 
| 332 | 
            +
                          "'#{item}'"
         | 
| 333 | 
            +
                      end
         | 
| 334 | 
            +
                    end
         | 
| 335 | 
            +
                       
         | 
| 336 | 
            +
                    def camelize(string)
         | 
| 337 | 
            +
                      string.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }    
         | 
| 338 | 
            +
                    end  
         | 
| 339 | 
            +
                    
         | 
| 340 | 
            +
                    def underscore(string)
         | 
| 341 | 
            +
                      string.to_s.gsub(/::/, '/').gsub('_', '___').gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
         | 
| 342 | 
            +
                      gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
         | 
| 343 | 
            +
                    end
         | 
| 172 344 | 
             
                end
         | 
| 173 | 
            -
              end
         | 
| 174 345 |  | 
| 175 | 
            -
             | 
| 346 | 
            +
                def initialize(win32ole_object)
         | 
| 347 | 
            +
                  @win32ole_object = win32ole_object
         | 
| 348 | 
            +
                end
         | 
| 349 | 
            +
                
         | 
| 350 | 
            +
                def methods(all_methods=true)
         | 
| 351 | 
            +
                  super(all_methods) + @win32ole_object.methods_.map{|method| underscore(method.name) }
         | 
| 352 | 
            +
                end
         | 
| 353 | 
            +
                
         | 
| 354 | 
            +
                def attributes
         | 
| 355 | 
            +
                  if @attributes
         | 
| 356 | 
            +
                    return @attributes
         | 
| 357 | 
            +
                  else
         | 
| 358 | 
            +
                    @attributes = {}
         | 
| 359 | 
            +
                    @win32ole_object.properties_.each{ |prop| 
         | 
| 360 | 
            +
                      name  = prop.name
         | 
| 361 | 
            +
                      value = @win32ole_object.send(name)
         | 
| 362 | 
            +
                      value = if prop.cimtype == 101 && value
         | 
| 363 | 
            +
                        Time.parse_swbem_date_time(value) 
         | 
| 364 | 
            +
                      else
         | 
| 365 | 
            +
                        value
         | 
| 366 | 
            +
                      end
         | 
| 367 | 
            +
                      @attributes[underscore(name)] = value
         | 
| 368 | 
            +
                    }
         | 
| 369 | 
            +
                    return @attributes
         | 
| 370 | 
            +
                  end
         | 
| 371 | 
            +
                end
         | 
| 372 | 
            +
                
         | 
| 373 | 
            +
                def attribute_names
         | 
| 374 | 
            +
                  @attribute_names ||= @win32ole_object.properties_.map{ |p| underscore(p.name) }
         | 
| 375 | 
            +
                end
         | 
| 376 | 
            +
                
         | 
| 377 | 
            +
                def [](key)
         | 
| 378 | 
            +
                  key = camelize(key.to_s)
         | 
| 379 | 
            +
                  @win32ole_object[key] 
         | 
| 380 | 
            +
                end
         | 
| 381 | 
            +
                
         | 
| 382 | 
            +
                def []=(key,value)
         | 
| 383 | 
            +
                  key = camelize(key.to_s)
         | 
| 384 | 
            +
                  raise ReadOnlyError unless writable?(key)
         | 
| 385 | 
            +
                  @win32ole_object[key] = value
         | 
| 386 | 
            +
                  @win32ole_object.Put_
         | 
| 387 | 
            +
                end
         | 
| 388 | 
            +
                
         | 
| 389 | 
            +
                def method_missing(name,*args)
         | 
| 390 | 
            +
                  name = camelize(name.to_s)
         | 
| 391 | 
            +
                  @win32ole_object.send(name)
         | 
| 392 | 
            +
                end
         | 
| 393 | 
            +
                
         | 
| 394 | 
            +
                def camelize(string)
         | 
| 395 | 
            +
                  self.class.send(:camelize, string)
         | 
| 396 | 
            +
                end  
         | 
| 397 | 
            +
                
         | 
| 398 | 
            +
                def underscore(string)
         | 
| 399 | 
            +
                  self.class.send(:underscore, string)
         | 
| 400 | 
            +
                end
         | 
| 401 | 
            +
                
         | 
| 402 | 
            +
                def inspect
         | 
| 403 | 
            +
                  "#<#{self.class}:#{name}>"
         | 
| 404 | 
            +
                end
         | 
| 176 405 |  | 
| 177 | 
            -
             | 
| 178 | 
            -
             | 
| 406 | 
            +
                def name
         | 
| 407 | 
            +
                  @win32ole_object.name 
         | 
| 408 | 
            +
                rescue
         | 
| 409 | 
            +
                  begin
         | 
| 410 | 
            +
                    @win32ole_object.description
         | 
| 411 | 
            +
                  rescue
         | 
| 412 | 
            +
                    object_id
         | 
| 413 | 
            +
                  end
         | 
| 414 | 
            +
                end
         | 
| 179 415 | 
             
              end
         | 
| 180 416 | 
             
            end
         |