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
on 13.08.2008 02:26
on 13.08.2008 03:25
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++
on 13.08.2008 06:24
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
on 13.08.2008 06:55
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
on 13.08.2008 07:30
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
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
on 13.08.2008 14:36
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
on 13.08.2008 16:42
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
on 13.08.2008 16:51
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
on 13.08.2008 17:00
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
on 13.08.2008 17:18
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
on 13.08.2008 17:27
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