ruby-wmi 0.2.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|