Forum: Ruby Enumerable and WIN32OLE

68db3bafb0a990bf605c4cf62bf85db0?d=identicon&s=25 bpettichord@gmail.com (Guest)
on 2006-09-26 23:52
(Received via mailing list)
I make a lot of use of the WIN32OLE library, with many thanks to Nobu
Nakada.

One of my common idioms is to take a WIN32OLE object that implements
the IEnumerable interface and extend it with the Ruby Enumerable Mix
in. Thus:

      rows = @ole_worksheet.usedRange.rows.extend Enumerable
      rows.select do |row|
        entire_row = row.entireRow
        entire_row.columns(1).text == '' and # comment
        entire_row.columns(2).text != '' # no action
      end

It strikes me that it might be reasonable to expect the Ruby win32ole
library to do this for me automatically. Do you agree?

Bret
0b561a629b87f0bbf71b45ee5a48febb?d=identicon&s=25 Dave Burt (Guest)
on 2006-09-27 10:22
(Received via mailing list)
bpettichord@gmail.com wrote:
> One of my common idioms is to take a WIN32OLE object that implements
> the IEnumerable interface and extend it with the Ruby Enumerable Mix
> in. Thus:
> ...
> It strikes me that it might be reasonable to expect the Ruby win32ole
> library to do this for me automatically. Do you agree?

Yes, I've expected that on occasion, and I think it would be a nifty
feature indeed. Enumerable's methods don't clash with common COM
collection methods, as far as I can see.

The reason it's not included already is that each is called as
dynamically as any other OLE method; it's not determined before the call
whether or not the object actually implements IEnum. So either you'd
have to do this check on instantiation (probably in ole_set_member(),
the function that associates a WIN32OLE object with its COM object), or
you could include Enumerable in WIN32OLE itself.

I'm not sure how expensive it would be in terms of performance, adding
this check to ole_set_member().

Masaki Suketa?

Cheers,
Dave
E97181961afa48102e3a28daab69b904?d=identicon&s=25 Masaki Suketa (Guest)
on 2006-09-29 14:28
(Received via mailing list)
Hello, sorry for being too late to reply.

In message "Re: Enumerable and WIN32OLE"
    on 06/09/27, Dave Burt <dave@burt.id.au> writes:

> Yes, I've expected that on occasion, and I think it would be a nifty
> feature indeed. Enumerable's methods don't clash with common COM
> collection methods, as far as I can see.

Enumerable#find method clashes the 'find' method of Excel Range object.
This is the (only) reason why WIN32OLE does not include Enumerable.

WIN32OLE class has 'each' method (WIN32OLE#each is defined),
and you can use Enumerable's methods by adding the following code.
  class WIN32OLE
    include Enumerable
  end

  Regards,
  Masaki Suketa
E97181961afa48102e3a28daab69b904?d=identicon&s=25 Masaki Suketa (Guest)
on 2006-09-29 15:32
(Received via mailing list)
In message "Re: Enumerable and WIN32OLE"
    on 06/09/29, Masaki Suketa <masaki.suketa@nifty.ne.jp> writes:

> In message "Re: Enumerable and WIN32OLE"
>     on 06/09/27, Dave Burt <dave@burt.id.au> writes:
>
> > Yes, I've expected that on occasion, and I think it would be a nifty
> > feature indeed. Enumerable's methods don't clash with common COM
> > collection methods, as far as I can see.
>
> Enumerable#find method clashes the 'find' method of Excel Range object.
> This is the (only) reason why WIN32OLE does not include Enumerable.

Enumerable#select method clashes the 'select' method of Excel Range
object.
And I think this case is more painful than the 'find' case.

  Regards,
  Masaki Suketa
0b561a629b87f0bbf71b45ee5a48febb?d=identicon&s=25 Dave Burt (Guest)
on 2006-09-29 18:27
(Received via mailing list)
Masaki Suketa wrote:
>> Enumerable#find method clashes the 'find' method of Excel Range object.
>> This is the (only) reason why WIN32OLE does not include Enumerable.
>
> Enumerable#select method clashes the 'select' method of Excel Range object.
> And I think this case is more painful than the 'find' case.

You're right, we don't want to break people's range.find or range.select
calls (at least in Ruby 1.8).

What about something like this, though? For these methods, a block is
always provided. For a COM method call, a block is never provided. Why
not dispatch depending on block_given?

class WIN32OLE
  include Enumerable
  %w( find select ).each do |m|
    alias_method "enum_#{m}", m
    define_method m do |*args|
      if block_given?
        send("enum_#{m}", *args) {|*a| yield *a }
      else
        invoke m, *args
      end
    end
  end
end

My preference for writing code for OLE is to use uppercase method calls
(range.Select) and save lowercase for Ruby methods. But this should
provide compatibility for people who have range.select in their code.
IEnums should have Enumerable.

Cheers,
Dave
E97181961afa48102e3a28daab69b904?d=identicon&s=25 Masaki Suketa (Guest)
on 2006-09-30 03:50
(Received via mailing list)
In message "Re: Enumerable and WIN32OLE"
    on 06/09/30, Dave Burt <dave@burt.id.au> writes:

> What about something like this, though? For these methods, a block is
> always provided. For a COM method call, a block is never provided. Why
> not dispatch depending on block_given?

Some Enumerable's methods does not need a block, for example,
Enumerable#max.
So, I hesitate to implement your idea.
(I am not sure that there are OLE methods whose name is "max".)

> My preference for writing code for OLE is to use uppercase method calls
> (range.Select) and save lowercase for Ruby methods. But this should
> provide compatibility for people who have range.select in their code.

I prefer this approach.
  In Win32OLE, WIN32OLE includes Enumerable.
  If you want to call original method(range.select),
  you should use range.Select.
  (And Win32OLE does not provide the compatibility.
   I have no idea to provide the compatibility.)

Or,
  In Win32OLE, WIN32OLE does not include Enumerable.
  If you want to use Enumerable's method,
  you add the following code at your own risk.
    class WIN32OLE
      include Enumerable
    end

> IEnums should have Enumerable.

I agree with you, but I have no idea that all things work well.

  Regards,
  Masaki Suketa
0b561a629b87f0bbf71b45ee5a48febb?d=identicon&s=25 Dave Burt (Guest)
on 2006-09-30 17:06
(Received via mailing list)
Masaki Suketa wrote:
> In message "Re: Enumerable and WIN32OLE"
>     on 06/09/30, Dave Burt <dave@burt.id.au> writes:
>
>> What about something like this, though? For these methods, a block is
>> always provided. For a COM method call, a block is never provided. Why
>> not dispatch depending on block_given?
>
> Some Enumerable's methods does not need a block, for example, Enumerable#max.
> So, I hesitate to implement your idea.
> (I am not sure that there are OLE methods whose name is "max".)

I had an earlier version of the code I posted that went through
Enumerable.instance_methods but skipped specific methods that don't need
blocks (min, max, to_a, entries, grep, sort, zip, /enum_.*/, /.*\?$/).
You can probably dismiss this option as well as what I wrote, just for
being so inconsistent. I don't prefer this option, but I think it does
strike a valuable balance between POLS and compatibility.

> I prefer this approach.
>   In Win32OLE, WIN32OLE includes Enumerable.
>   If you want to call original method(range.select),
>   you should use range.Select.
>   (And Win32OLE does not provide the compatibility.
>    I have no idea to provide the compatibility.)

I don't think this is a bad idea, but the incompatibility issue needs
considering. I think it's worth it.

> Or,
>   In Win32OLE, WIN32OLE does not include Enumerable.
>   If you want to use Enumerable's method,
>   you add the following code at your own risk.
>     class WIN32OLE
>       include Enumerable
>     end

This (what we currently have) is deficient in the principle of least
surprise and utility. You should not have to explicitly include or
extend Enumerable on a WIN32OLE IEnum object.

Looking at where String's going in Ruby (apparently becoming
non-Enumerable in 1.9) another option could be to leave Enumerable out
of WIN32OLE itself, but provide an enum method to return an enumerator
object:

class WIN32OLE
  def enum
    Enumerable::Enumerator.new(self)
  end
end

Cheers,
Dave
E97181961afa48102e3a28daab69b904?d=identicon&s=25 Masaki Suketa (Guest)
on 2006-09-30 23:59
(Received via mailing list)
Hello,
In message "Re: Enumerable and WIN32OLE"
    on 06/10/01, Dave Burt <dave@burt.id.au> writes:

> > Some Enumerable's methods does not need a block, for example, Enumerable#max.
> > So, I hesitate to implement your idea.
> > (I am not sure that there are OLE methods whose name is "max".)
>
> I had an earlier version of the code I posted that went through
> Enumerable.instance_methods but skipped specific methods that don't need
> blocks (min, max, to_a, entries, grep, sort, zip, /enum_.*/, /.*\?$/).
> You can probably dismiss this option as well as what I wrote, just for
> being so inconsistent. I don't prefer this option, but I think it does
> strike a valuable balance between POLS and compatibility.

I think I understand your code skip the methods that don't need
blocks.
But, for example, if there is an OLE methods whose name is "max",
then, I am afraid that we might have the same problem.
Which method does the following code call (in your posted code)?
OLE method max or Enumerable#max?

  ole = WIN32OLE.new(...)
  ole.max

Do I misunderstand you?

> Looking at where String's going in Ruby (apparently becoming
> non-Enumerable in 1.9) another option could be to leave Enumerable out
> of WIN32OLE itself, but provide an enum method to return an enumerator
> object:
>
> class WIN32OLE
>   def enum
>     Enumerable::Enumerator.new(self)
>   end
> end

Hmm, please give me some time to investigate it.
(If there is OLE "enum" method, I think we might have the same problem
...)

  Regards,
  Masaki Suketa
0b561a629b87f0bbf71b45ee5a48febb?d=identicon&s=25 Dave Burt (Guest)
on 2006-10-01 06:36
(Received via mailing list)
Masaki Suketa wrote:
> Do I misunderstand you?
No, that's right, that is still an issue, only for (min, max, entries,
sort, grep).

> Hmm, please give me some time to investigate it.
> (If there is OLE "enum" method, I think we might have the same problem ...)

I missed something in the Enumerator API: the method Object#to_enum does
what I intended "enum" to do.

Cheers,
Dave
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.