com 0.3.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.
@@ -0,0 +1,38 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Represents an COM method invocation error.
4
+ class COM::MethodInvocationError < COM::Error
5
+ extend COM::PatternError
6
+
7
+ pattern %r{^\s*([^\n]*)\n
8
+ \s*OLE\serror\scode:([0-9a-fA-F]+)
9
+ \s+in\s+([^\n]+)\n
10
+ \s*([^\n]+)\n
11
+ \s*HRESULT\serror\scode:0x([0-9a-fA-F]+)[^\n]*\n
12
+ \s*(.+)$}xu
13
+
14
+ class << self
15
+ # This is an internal method used by COM::Error.
16
+ def replace(error)
17
+ m = pattern.match(error.message)
18
+ new(m[4], m[1], m[3], m[2].to_i(16), m[5].to_i(16), m[6])
19
+ end
20
+ end
21
+
22
+ # Creates a new COM::MethodInvocationError with _message_.
23
+ #
24
+ # @param [#to_str] message Error message
25
+ # @param [String] method Method error occurred in
26
+ # @param [String] server Server error occurred in
27
+ # @param [Integer] code COM error code
28
+ # @param [Integer] hresult_code HRESULT error code
29
+ # @param [String] hresult_message HRESULT error message
30
+ def initialize(message, method = '', server = '', code = -1,
31
+ hresult_code = -1, hresult_message = '')
32
+ super message
33
+ @method, @server, @code, @hresult_code, @hresult_message =
34
+ method, server, code, hresult_code, hresult_message
35
+ end
36
+
37
+ attr_reader :method, :server, :code, :hresult_code, :hresult_message
38
+ end
@@ -0,0 +1,79 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ class COM::Object
4
+ # Creates a new instance based on _com_. It is important that subclasses
5
+ # call `super` if they override this method.
6
+ #
7
+ # @param [WIN32OLE] com A WIN32OLE Com object
8
+ def initialize(com)
9
+ self.com = com
10
+ end
11
+
12
+ # Queries whether this COM object responds to _method_.
13
+ #
14
+ # @param [Symbol] method Method name to query for response
15
+ # @return [Boolean] Whether or not this COM object responds to _method_
16
+ def respond_to?(method)
17
+ super or com.respond_to? method
18
+ end
19
+
20
+ # Sets a bunch of properties, yield, and then restore them. If an exception
21
+ # is raised, any set properties are restored.
22
+ #
23
+ # @param [#to_hash] properties properties with values to set
24
+ # @return [COM::Object] `self` */
25
+ def with_properties(properties)
26
+ saved_properties = []
27
+ begin
28
+ properties.to_hash.each do |property, value|
29
+ saved_properties << [property, com[property]]
30
+ com.set_property property, value
31
+ end
32
+ yield
33
+ ensure
34
+ previous_error = $!
35
+ begin
36
+ saved_properties.reverse.each do |property, value|
37
+ begin com.set_property property, value; rescue COM::Error; end
38
+ end
39
+ rescue
40
+ raise if not previous_error
41
+ end
42
+ end
43
+ end
44
+
45
+ def observe(event, observer = COM::Events::ArgumentMissing, &block)
46
+ com.observe event, observer, &block
47
+ self
48
+ end
49
+
50
+ def unobserve(event, observer = nil)
51
+ com.unobserve event, observer
52
+ self
53
+ end
54
+
55
+ def to_com
56
+ com.to_com
57
+ end
58
+
59
+ protected
60
+
61
+ attr_reader :com
62
+
63
+ def com=(com)
64
+ @com = WIN32OLE === com ? COM::Wrapper.new(com) : com
65
+ end
66
+
67
+ private
68
+
69
+ def method_missing(method, *args)
70
+ case method.to_s
71
+ when /=\z/
72
+ com.set_property($`.encode(COM.charset), *args)
73
+ else
74
+ com.invoke(method.to_s.encode(COM.charset), *args)
75
+ end
76
+ rescue NoMethodError => e
77
+ raise e, "undefined method `%s' for %p" % [method, self], e.backtrace
78
+ end
79
+ end
@@ -0,0 +1,10 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ class Pathname
4
+ # Returns a String representation of this Pathname in the COM character set.
5
+ #
6
+ # @see COM.charset
7
+ def to_com
8
+ to_path.encode(COM.charset)
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module COM::PatternError
4
+ # @private method used by {COM::Error.find}.
5
+ def replaces?(error)
6
+ pattern =~ error.message
7
+ end
8
+
9
+ protected
10
+
11
+ def replace(error)
12
+ new(*pattern.match(error.message).captures)
13
+ end
14
+
15
+ private
16
+
17
+ def pattern(pattern = nil)
18
+ (pattern or not defined? @pattern) ? @pattern = pattern : @pattern
19
+ end
20
+ end
@@ -0,0 +1,61 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Sets up mappings between COM errors and Ruby errors.
4
+ #
5
+ # @private
6
+ module COM::StandardError
7
+ class << self
8
+ private
9
+ def define(code, errorclass, message = nil, &block)
10
+ block = proc{ |m| m[1] } unless message or block
11
+ Class.new(COM::Error) do
12
+ extend COM::PatternError
13
+
14
+ pattern %r{^\s*(.*)\n\s*HRESULT\serror\scode:(0x(?i:#{code.to_s(16)}))}xu
15
+
16
+ (class << self; self; end).class_eval do
17
+ define_method :replace do |error|
18
+ m = pattern.match(error.message)
19
+ errorclass.new(*Array(message ? message : block.call(m)))
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ define(0x80004001, ::NotImplementedError){ |m| '%s: not implemented' % m[1] }
27
+ define 0x80020005, ::TypeError
28
+ define 0x80020006, ::NoMethodError
29
+ define 0x8002000e, ::ArgumentError, 'wrong number of arguments'
30
+ define 0x800401e4, ::ArgumentError
31
+ define 0x800401f3, ::NameError do |m|
32
+ name = m[1].sub(/.*`(.*)'\z/, '\\1')
33
+ ['unknown COM server: %s' % name, name]
34
+ end
35
+ end
36
+
37
+ # Sets up mappings between HRESULT errors and COM errors.
38
+ #
39
+ # @private
40
+ module COM::HResultError
41
+ class << self
42
+ def define(code, error, message = nil, &block)
43
+ block = proc{ |m| m[1] } unless message or block
44
+ COM.const_set error, Class.new(COM::Error){
45
+ extend COM::PatternError
46
+
47
+ pattern %r{^\s*(.*)\n\s*HRESULT\serror\scode:(0x(?i:#{code.to_s(16)}))}xu
48
+
49
+ (class << self; self; end).class_eval do
50
+ define_method :replace do |e|
51
+ m = pattern.match(e.message)
52
+ new(message ? message : block.call(m))
53
+ end
54
+ end
55
+ }
56
+ end
57
+ end
58
+
59
+ define 0x80020003, :MemberNotFoundError
60
+ define 0x800401e3, :OperationUnavailableError
61
+ end
@@ -0,0 +1,5 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module COM
4
+ Version = '0.3.0'
5
+ end
@@ -0,0 +1,19 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ class WIN32OLE_TYPE
4
+ class << self
5
+ def enums(id)
6
+ ole_classes(id).select{ |c| c.visible? and c.ole_type == 'Enum' }
7
+ end
8
+ end
9
+
10
+ def constants
11
+ variables.select{ |v| v.visible? and v.variable_kind == 'CONSTANT' }
12
+ end
13
+ end
14
+
15
+ class WIN32OLE_VARIABLE
16
+ def const_name
17
+ name.sub(/^./){ |l| l.upcase }
18
+ end
19
+ end
@@ -0,0 +1,96 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # This module provides a wrapper around WIN32OLE’s instance methods. This
4
+ # wrapper deals with converting errors to the appropriate type.
5
+ #
6
+ # @private
7
+ class COM::Wrapper
8
+ BacktraceFilter = File.dirname(File.dirname(__FILE__)) + File::SEPARATOR
9
+
10
+ class << self
11
+ def raise_in(method, e)
12
+ clean = e.backtrace.reject{ |s| s.start_with? BacktraceFilter }
13
+ raise COM::Error.from(e, clean.unshift(clean.first.sub(/:in `.*'$/, ":in `#{method}'")))
14
+ end
15
+ end
16
+
17
+ def initialize(ole)
18
+ @ole = ole
19
+ end
20
+
21
+ def invoke(method, *args)
22
+ @ole.invoke(method, *args.map{ |e| e.respond_to?(:to_com) ? e.to_com : e })
23
+ rescue NoMethodError => e
24
+ error = NoMethodError.new("undefined method `%s' for %p" % [method, self],
25
+ method,
26
+ args)
27
+ error.set_backtrace e.backtrace.reject{ |s| s.start_with? BacktraceFilter }
28
+ raise error
29
+ rescue WIN32OLERuntimeError => e
30
+ raise COM::Wrapper.raise_in(method, e)
31
+ end
32
+
33
+ def set_property(property, *args)
34
+ @ole.setproperty(property, *args.map{ |e| e.respond_to?(:to_com) ? e.to_com : e })
35
+ rescue NoMethodError => e
36
+ error = NoMethodError.new("undefined property `%s' for %p" % [property, self],
37
+ property,
38
+ args)
39
+ error.set_backtrace e.backtrace.reject{ |s| s.start_with? BacktraceFilter }
40
+ raise error
41
+ rescue WIN32OLERuntimeError => e
42
+ raise COM::Wrapper.raise_in('%s=' % property, e)
43
+ end
44
+
45
+ def load_constants(into)
46
+ saved_verbose, $VERBOSE = $VERBOSE, nil
47
+ begin
48
+ begin
49
+ WIN32OLE.const_load @ole, into
50
+ rescue RuntimeError
51
+ WIN32OLE_TYPE.enums(program_id).each do |enum|
52
+ enum.constants.each do |constant|
53
+ into.const_set constant.const_name, constant.value
54
+ end
55
+ end
56
+ end
57
+ ensure
58
+ $VERBOSE = saved_verbose
59
+ end
60
+ end
61
+
62
+ def observe(event, observer = COM::Events::ArgumentMissing, &block)
63
+ events.observe event, observer, &block
64
+ self
65
+ end
66
+
67
+ def unobserve(event, observer = nil)
68
+ events.unobserve event, observer
69
+ self
70
+ end
71
+
72
+ def to_com
73
+ @ole
74
+ end
75
+
76
+ protected
77
+
78
+ def events
79
+ @events ||= COM::Events.new(@ole)
80
+ end
81
+
82
+ private
83
+
84
+ def method_missing(method, *args)
85
+ case method.to_s
86
+ when /=\z/
87
+ begin
88
+ set_property($`.encode(COM.charset), *args)
89
+ rescue NoMethodError => e
90
+ raise e, "undefined method `%s' for %p" % [method, self], e.backtrace
91
+ end
92
+ else
93
+ invoke(method.to_s.encode(COM.charset), *args)
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,23 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ Expectations do
4
+ expect 'UTF-8' do
5
+ stub(WIN32OLE).codepage{ WIN32OLE::CP_UTF8 }
6
+ COM.charset
7
+ end
8
+
9
+ expect RuntimeError do
10
+ stub(WIN32OLE).codepage{ :unknown_encoding }
11
+ COM.charset
12
+ end
13
+
14
+ expect COM::Error do
15
+ stub(WIN32OLE).connect{ raise WIN32OLERuntimeError }
16
+ COM.connect(stub)
17
+ end
18
+
19
+ expect COM::Error do
20
+ stub(WIN32OLE).new{ raise WIN32OLERuntimeError }
21
+ COM.new(stub)
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ Expectations do
4
+ expect COM::Error do
5
+ COM::Error.from(stub(:message => '<some random message>'))
6
+ end
7
+
8
+ expect '<some random message>' do
9
+ COM::Error.from(stub(:message => '<some random message>')).message
10
+ end
11
+ end
@@ -0,0 +1,28 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ Expectations do
4
+ expect WIN32OLE_EVENT.to.receive.new(:com, :interface) do
5
+ COM::Events.new(:com, :interface)
6
+ end
7
+
8
+ expect mock.to.receive.on_event(:on_something) do |o|
9
+ stub(WIN32OLE_EVENT).new{ o }
10
+ COM::Events.new(:ole, :interface).observe :on_something do end
11
+ end
12
+
13
+ expect mock.to.receive.call do |o|
14
+ events = stub
15
+ class << events
16
+ def on_event(event, &block)
17
+ @block = block
18
+ end
19
+
20
+ def trigger
21
+ @block.call
22
+ end
23
+ end
24
+ stub(WIN32OLE_EVENT).new{ events }
25
+ e = COM::Events.new(:ole, :interface)
26
+ e.observe(:on_something, proc{ events.trigger }){ o.call }
27
+ end
28
+ end
@@ -0,0 +1,94 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ Expectations do
4
+ expect 'Program.Class' do
5
+ Class.new(COM::Instantiable).program_id('Program.Class')
6
+ end
7
+
8
+ expect 'Program.Class' do
9
+ Class.new(COM::Instantiable){
10
+ program_id 'Program.Class'
11
+ }.program_id
12
+ end
13
+
14
+ expect ArgumentError do
15
+ Class.new(COM::Instantiable).tap{ |c| stub(c).name{ 'Class' } }.program_id
16
+ end
17
+
18
+ expect 'Program.Class' do
19
+ Class.new(COM::Instantiable).tap{ |c| stub(c).name{ 'Program::Class' } }.program_id
20
+ end
21
+
22
+ expect 'Program.Class' do
23
+ Class.new(COM::Instantiable).tap{ |c| stub(c).name{ 'Vendor::Program::Class' } }.program_id
24
+ end
25
+
26
+ expect false do
27
+ Class.new(COM::Instantiable).connect?
28
+ end
29
+
30
+ expect true do
31
+ Class.new(COM::Instantiable).connect
32
+ end
33
+
34
+ expect true do
35
+ Class.new(COM::Instantiable){ connect }.connect?
36
+ end
37
+
38
+ expect true do
39
+ Class.new(COM::Instantiable).constants?
40
+ end
41
+
42
+ expect true do
43
+ Class.new(COM::Instantiable).constants(true)
44
+ end
45
+
46
+ expect true do
47
+ Class.new(COM::Instantiable){ constants true }.constants?
48
+ end
49
+
50
+ expect COM.to.receive.connect('A.B'){ stub } do
51
+ Class.new(COM::Instantiable){ program_id 'A.B' }.new(:connect => true, :constants => false)
52
+ end
53
+
54
+ expect COM.not.to.receive.new do
55
+ stub(COM).connect{ stub }
56
+ Class.new(COM::Instantiable){ program_id 'A.B' }.new(:connect => true, :constants => false)
57
+ end
58
+
59
+ expect COM.to.receive.new('A.B'){ stub } do
60
+ stub(COM).connect{ raise COM::OperationUnavailableError }
61
+ Class.new(COM::Instantiable){ program_id 'A.B' }.new(:connect => true, :constants => false)
62
+ end
63
+
64
+ expect COM.to.receive.new('A.B'){ stub } do
65
+ stub(COM).connect{ raise COM::OperationUnavailableError }
66
+ Class.new(COM::Instantiable){ program_id 'A.B' }.new(:constants => false)
67
+ end
68
+
69
+ expect true do
70
+ stub(COM).connect{ stub }
71
+ Class.new(COM::Instantiable){ program_id 'A.B' }.new(:connect => true, :constants => false).connected?
72
+ end
73
+
74
+ expect false do
75
+ stub(COM).connect{ raise COM::OperationUnavailableError }
76
+ stub(COM).new{ stub }
77
+ Class.new(COM::Instantiable){ program_id 'A.B' }.new(:connect => true, :constants => false).connected?
78
+ end
79
+
80
+ expect false do
81
+ stub(COM).new{ stub }
82
+ Class.new(COM::Instantiable){ program_id 'A.B' }.new(:constants => false).connected?
83
+ end
84
+
85
+ expect(Class.new(COM::Instantiable){ program_id 'A.B' }.to.receive.load_constants) do |o|
86
+ stub(COM).new{ stub }
87
+ o.new
88
+ end
89
+
90
+ expect(Class.new(COM::Instantiable){ program_id 'A.B' }.not.to.receive.load_constants) do |o|
91
+ stub(COM).new{ stub }
92
+ o.new(:constants => false)
93
+ end
94
+ end