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
|