Ruby Forum RSpec > WDYT, simple, anonymous story listeners?

Posted by Zach Dennis (Guest)
on 13.08.2008 02:26
(Received via mailing list)
Sometimes I don't have a full need to make a class to do something,
yet I want something readable and concise. This is influenced from the
joys of JavaScript.

Today I made this happen. Love it, like it, hate it, WDYT?

Spec::Story::Runner.register_listener FunctionalStruct.new(
  :run_started => lambda { |*args|
    Generate.user(:login => "normal user")
  },
  :run_ended => lambda { |*args|
      User.destroy_all
  },
  :method_missing => lambda { |*a| }
)


--
Zach Dennis
http://www.continuousthinking.com
http://www.mutuallyhuman.com
Posted by David Chelimsky (Guest)
on 13.08.2008 03:25
(Received via mailing list)
On Tue, Aug 12, 2008 at 7:25 PM, Zach Dennis <zach.dennis@gmail.com> 
wrote:
>  :run_ended => lambda { |*args|
>      User.destroy_all
>  },
>  :method_missing => lambda { |*a| }
> )

Love is a bit strong. Like++
Posted by Tero Tilus (Guest)
on 13.08.2008 06:24
(Received via mailing list)
2008-08-12 20:25, Zach Dennis:
> Sometimes I don't have a full need to make a class to do something,

How's that _essentially_ different from making a class or extending an
existing class?  I am not knowledgeable enough to "just see" it, and
becaus I can't understand the motivation, I'm bound to hate it.  ;)

> This is influenced from the joys of JavaScript.

It even looks like JavaScript.  :D
Posted by Zach Dennis (Guest)
on 13.08.2008 06:55
(Received via mailing list)
On Wed, Aug 13, 2008 at 12:24 AM, Tero Tilus <tero@tilus.net> wrote:
> 2008-08-12 20:25, Zach Dennis:
>> Sometimes I don't have a full need to make a class to do something,
>
> How's that _essentially_ different from making a class or extending an
> existing class?  I am not knowledgeable enough to "just see" it, and
> becaus I can't understand the motivation, I'm bound to hate it.  ;)

The biggest difference is that you can pass the whole thing as an
argument into the method you're calling (reduce noise, increase
clarity). If you used something like OpenStruct you couldn't do it,
because in Ruby you can't force invocation of a Proc object (you have
to call "call" on it).

For example, this wouldn't work using an OpenStruct:

  Spec::Story::Runner.register_listener OpenStruct.new(
   :run_started => lambda { |*args|
     Generate.user(:login => "normal user")
   },
   :run_ended => lambda { |*args|
       User.destroy_all
   },
   :method_missing => lambda { |*a| }
  )

In order for this to work we'd have to create a class for our listener, 
like so:

  class MyStoryListener
    def run_started(*args)
      Generate.user(:login => "normal user")
    end

    def run_ended(*args)
      User.destroy_all
    end

    def method_missing(*args)
    end
  end

   # and register it separately
   Spec::Story::Runner.register_listener MyStoryListener.new

There is nothing wrong with this, but there are times when it feels
dirty and unnecessary to create yet another class with some methods,
just so the thing can be instantiated one time and passed in as an
argument. That's why I decided to throw together a little
FunctionalStruct, so if Proc objects got passed in they would be
invokable.

Here's a more specific example of the different between OpenStruct and
FunctionalStruct:

  # openstruct example
  o = OpenStruct.new :foo => lambda { "foo" }
  o.foo # => <Proc:#asfsblahblahblah>
  o.foo.call # => "foo"

  # functionalstruct example
  f = FunctionalStruct.new :foo => lambda { "foo" }
  f.foo # => "foo"

>> This is influenced from the joys of JavaScript.
>
> It even looks like JavaScript.  :D

One thing I miss from JavaScript is that functions are truly first
class citizens. The beauty of Ruby is that I can mimic that by writing
something like FunctionalStruct.


--
Zach Dennis
http://www.continuousthinking.com
http://www.mutuallyhuman.com
Posted by Mark Wilden (Guest)
on 13.08.2008 07:30
(Received via mailing list)
On Tue, Aug 12, 2008 at 9:54 PM, Zach Dennis <zach.dennis@gmail.com> 
wrote:


> There is nothing wrong with this, but there are times when it feels
> dirty and unnecessary to create yet another class with some methods


The proposed solution looks very nice, but I've never been convinced by 
the
"yet another class" argument. It's not like you're only allowed a 
certain
number.

That hasn't always been the case, however. When I worked at Sierra 
On-Line
in the early '90s, I broke the compiler of our proprietary OOP language
because I exceeded its maximum class count. Those were the days...:)

///ark
Posted by Joseph Wilk (joesniff)
on 13.08.2008 11:26
It looks like a nice shortcut for those times when you are registering 
simple one-off listeners.

While it does provide a nice shortcut I can see reasons why that 
shortcut might be bad(in some cases).

Separating registering and implementation can be a good thing.
*Split the logic.
*Organise files/classes nicely
*Enable inheritance from the listeners

If it was anything that I had to maintain or touch regularly I would be 
happier with a class.

Can/Could you support mixins inside FunctionalStruct? That would help 
overcome some of those points.

I can think of a couple of places that I would be happy using 
FunctionalStruct. Is the source for 'FunctionalStruct' 
written/available?

--
Joseph Wilk
http://www.joesniff.co.uk


Mark Wilden wrote:
> On Tue, Aug 12, 2008 at 9:54 PM, Zach Dennis <zach.dennis@gmail.com> 
> wrote:
> 
> 
>> There is nothing wrong with this, but there are times when it feels
>> dirty and unnecessary to create yet another class with some methods
> 
> 
> The proposed solution looks very nice, but I've never been convinced by 
> the
> "yet another class" argument. It's not like you're only allowed a 
> certain
> number.
> 
> That hasn't always been the case, however. When I worked at Sierra 
> On-Line
> in the early '90s, I broke the compiler of our proprietary OOP language
> because I exceeded its maximum class count. Those were the days...:)
> 
> ///ark
Posted by aslak hellesoy (Guest)
on 13.08.2008 14:36
(Received via mailing list)
On Wed, Aug 13, 2008 at 2:25 AM, Zach Dennis <zach.dennis@gmail.com> 
wrote:
> Sometimes I don't have a full need to make a class to do something,
> yet I want something readable and concise. This is influenced from the
> joys of JavaScript.
>
> Today I made this happen. Love it, like it, hate it, WDYT?
>

As mentioned earlier on the RSpec development list, we're considering
replacing the Story runner with a new implementation:

http://github.com/aslakhellesoy/cucumber
http://gojko.net/2008/08/06/cucumber-next-generation-ruby-bdd-tool/
http://www.nabble.com/-ANN--Cucumber-td18876816.html

I'd rather see a similar construct for Cucumber, which already is
(IMHO) much better than the Story Runner.

Before(:all) do
end

After(:all) do
end

(per-scenarion Before/After is already implemented).

Aslak
Posted by Zach Dennis (Guest)
on 13.08.2008 16:42
(Received via mailing list)
On Wed, Aug 13, 2008 at 5:26 AM, Joseph Wilk <lists@ruby-forum.com> 
wrote:
>
> If it was anything that I had to maintain or touch regularly I would be
> happier with a class.
>
> Can/Could you support mixins inside FunctionalStruct? That would help
> overcome some of those points.

Yeah you definitely could, perhaps something like:

FunctionalStruct.new( SomeModule,
   :foo => ...,
   :bar => ...,
)



> I can think of a couple of places that I would be happy using
> FunctionalStruct. Is the source for 'FunctionalStruct'
> written/available?

http://github.com/zdennis/functional_struct/tree/master


--
Zach Dennis
http://www.continuousthinking.com
http://www.mutuallyhuman.com
Posted by Zach Dennis (Guest)
on 13.08.2008 16:51
(Received via mailing list)
On Wed, Aug 13, 2008 at 8:29 AM, aslak hellesoy
<aslak.hellesoy@gmail.com> wrote:
>
> end
>
> After(:all) do
> end
>
> (per-scenarion Before/After is already implemented).
>

When you say "rather see a similar construct" -- are you referring to
having the Before(:all) and After(:all) capability that you posted, or
something similar to what I posted with using a FuncionalStruct as an
argument to register a listener on the existing StoryRunner (a
clear/concise way to hook-in with those one off listeners) ?

--
Zach Dennis
http://www.continuousthinking.com
http://www.mutuallyhuman.com
Posted by aslak hellesoy (Guest)
on 13.08.2008 17:00
(Received via mailing list)
On Wed, Aug 13, 2008 at 4:50 PM, Zach Dennis <zach.dennis@gmail.com> 
wrote:
>> As mentioned earlier on the RSpec development list, we're considering
>>
> having the Before(:all) and After(:all) capability that you posted, or
> something similar to what I posted with using a FuncionalStruct as an
> argument to register a listener on the existing StoryRunner (a
> clear/concise way to hook-in with those one off listeners) ?
>

By "similar construct" I meant something that achievs the same goal
(running pieces of code at different times during the run).

I suggested Before(:all) and After(:all) because:

* It's a familiar concept from RSpec examples
* It's much easier to read/write

Compare these:

# Your suggestion
Spec::Story::Runner.register_listener FunctionalStruct.new(
  :run_started => lambda { |*args|
    Generate.user(:login => "normal user")
  }
)

# My suggestion:
Before(:all) do # We can pass args to the block if we want to
  Generate.user(:login => "normal user")
end

Aslak
Posted by Zach Dennis (Guest)
on 13.08.2008 17:18
(Received via mailing list)
On Wed, Aug 13, 2008 at 11:00 AM, aslak hellesoy
<aslak.hellesoy@gmail.com> wrote:
>>>
>>>
>> When you say "rather see a similar construct" -- are you referring to
>
> )
>
> # My suggestion:
> Before(:all) do # We can pass args to the block if we want to
>  Generate.user(:login => "normal user")
> end
>

Before/After is definitely much cleaner, although I'm not a fan of
more global namespace pollution, but it may not be a problem in
practice (and/or maybe it's scoped in to another namespace like
Feature::Before(:all))

Are you thinking of allowing for multiple Before/After(:all) blocks?

--
Zach Dennis
http://www.continuousthinking.com
http://www.mutuallyhuman.com
Posted by aslak hellesoy (Guest)
on 13.08.2008 17:27
(Received via mailing list)
On Wed, Aug 13, 2008 at 5:18 PM, Zach Dennis <zach.dennis@gmail.com> 
wrote:
>>>>> Today I made this happen. Love it, like it, hate it, WDYT?
>>>> I'd rather see a similar construct for Cucumber, which already is
>>>>
>>
>>    Generate.user(:login => "normal user")
> more global namespace pollution, but it may not be a problem in
> practice (and/or maybe it's scoped in to another namespace like
> Feature::Before(:all))
>

I've thought about that a bit, and decided that I want to keep it
global (because it means less typing and looks nicer) until it poses a
problem to someone.

> Are you thinking of allowing for multiple Before/After(:all) blocks?
>

Definitely. Before/After (without :all) already supports multiple.

Aslak