WDYT, simple, anonymous story listeners?

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 D.
http://www.continuousthinking.com

On Tue, Aug 12, 2008 at 7:25 PM, Zach D. [email protected]
wrote:

:run_ended => lambda { |*args|
User.destroy_all
},
:method_missing => lambda { |*a| }
)

Love is a bit strong. Like++

2008-08-12 20:25, Zach D.:

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. :wink:

This is influenced from the joys of JavaScript.

It even looks like JavaScript. :smiley:

On Tue, Aug 12, 2008 at 9:54 PM, Zach D. [email protected]
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…:slight_smile:

///ark

On Wed, Aug 13, 2008 at 12:24 AM, Tero T. [email protected] wrote:

2008-08-12 20:25, Zach D.:

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. :wink:

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. :smiley:

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 D.
http://www.continuousthinking.com

On Wed, Aug 13, 2008 at 2:25 AM, Zach D. [email protected]
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

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 W.
http://www.joesniff.co.uk

Mark W. wrote:

On Tue, Aug 12, 2008 at 9:54 PM, Zach D. [email protected]
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…:slight_smile:

///ark

On Wed, Aug 13, 2008 at 8:29 AM, aslak hellesoy
[email protected] 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 D.
http://www.continuousthinking.com

On Wed, Aug 13, 2008 at 4:50 PM, Zach D. [email protected]
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

On Wed, Aug 13, 2008 at 5:26 AM, Joseph W. [email protected]
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?


Zach D.
http://www.continuousthinking.com

On Wed, Aug 13, 2008 at 11:00 AM, aslak hellesoy
[email protected] 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 D.
http://www.continuousthinking.com

On Wed, Aug 13, 2008 at 5:18 PM, Zach D. [email protected]
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