I'm wondering if there is some resource about writing code GOOS style
in Ruby (and Javascript).
I'm not so fluent in Ruby and I never write a big application with it,
I'm wondering how to use TDD in Ruby in the best way for example
without interfaces.
cheers
Uberto
S
The RSpec Book [1] is a great guide, not only in how to use RSpec the tool, but how to use it to help drive good OO Ruby code.
[1] http://www.amazon.com/RSpec-Book-Behaviour-Development-Cucumber
On 27 Jan 2011, at 20:39, Uberto Barbini wrote:
cheers,
Matt
ma...@mattwynne.net
07974 430184
Still it doesn't explain very much how to "grow" your applications.
Maybe it's just me but I'd like some more examples about how to design
your objects in the best way (aka Alan Kay's OOP) in Ruby.
cheers
Uberto
S
On 28 Feb 2011, at 16:50, tcrayford wrote:
Whilst java is very different from ruby, a lot of GOOS still applies
> to ruby. The only major difference that would affect the style
> presented in GOOS is the lack of explicit interfaces. I've never
> really found that a problem though, you just have to take some more
> care when thinking about interfaces.
Steve Freeman
Winner of the Agile Alliance Gordon Pask award 2006
Book: http://www.growing-object-oriented-software.com
+44 (0) 797 179 4105
M3P Limited. http://www.m3p.co.uk
Registered office. 2 Church Street, Burnham, Bucks, SL1 7HZ.
Company registered in England & Wales. Number 03689627
Now I have much clearer... still there is this missing "interface" part.
for example from the page 217 example:
describe Statement do
it "uses the customer's name in the header (with a mock)" do
customer = mock("customer")
customer.should_receive(:name).and_return("Dave Astels")
statement = Statement.new(customer)
statement.header.should == "Statement for Dave Astels"
end
end
that's all and fine, but when I go to implement customer class, I
haven't any clue about needed methods and their parameters other than
go to all the tests and check which one I used.
moreover if in a test I mock customer in a slightly different way like:
customer.should_receive(:fullname).and_return("Dave Astels")
I won't know until my integration tests fail.
I'd like to know if someone already worked in this way on Ruby and can
share his best practices.
On Mon, Feb 28, 2011 at 7:36 PM, Steve Freeman <st...@m3p.co.uk> wrote:
> I would hope that some of the principles apply. If people really wanted, I expect they could do some reflecto-magic to at least make sure that the implicit interfaces were consistent, and maybe cross-reference their implementors.
do you have something specific in mind with "implicit interface"?
cheers
Uberto
I read "the spec book" several months ago only because I'm testing a
java app with cucumber, so I didn't put attention on the ruby testing
and mocking part. ;)
Now I have much clearer... still there is this missing "interface" part.
for example from the page 217 example:
describe Statement do
it "uses the customer's name in the header (with a mock)" do
customer = mock("customer")
customer.should_receive(:name).and_return("Dave Astels")
statement = Statement.new(customer)
statement.header.should == "Statement for Dave Astels"
end
end
that's all and fine, but when I go to implement customer class, I
haven't any clue about needed methods and their parameters other than
go to all the tests and check which one I used.
moreover if in a test I mock customer in a slightly different way like:
customer.should_receive(:fullname).and_return("Dave Astels")
I won't know until my integration tests fail.
I'd like to know if someone already worked in this way on Ruby and can
share his best practices.
I read "the spec book" several months ago only because I'm testing a
java app with cucumber, so I didn't put attention on the ruby testing
and mocking part. ;)
Now I have much clearer... still there is this missing "interface" part.
for example from the page 217 example:
describe Statement do
it "uses the customer's name in the header (with a mock)" do
customer = mock("customer")
customer.should_receive(:name).and_return("Dave Astels")
statement = Statement.new(customer)
statement.header.should == "Statement for Dave Astels"
end
end
that's all and fine, but when I go to implement customer class, I
haven't any clue about needed methods and their parameters other than
go to all the tests and check which one I used.
moreover if in a test I mock customer in a slightly different way like:
customer.should_receive(:fullname).and_return("Dave Astels")
I won't know until my integration tests fail.
I'd like to know if someone already worked in this way on Ruby and can
share his best practices.
I'll check that.
>> I won't know until my integration tests fail.
>>
>> I'd like to know if someone already worked in this way on Ruby and can
>> share his best practices.
>
> Language features and libraries might /help/ you fulfil the contracts
> defined in your test-double collaboration tests but as with any language,
> it's probably best to write things down and be mindful of the fact that
> every time you write a collaboration test, you then have an obligation to
> write the contract tests for the implementors of that interface.
yes of course.
but I'd like to play around with the interface for some tests before
trying to implement it.
With Ruby I could add directly methods to an new Object without
bothering of using Mocks at all, still I see that they're using in
Rspec, so I'd like to "steal" the best practices from masters
shortcutting months of experience with my own mistakes. ;)
cheers
Uberto
> My real query here is how to name classes, and how to show a class'
> responsibilities. In Java, you use interfaces for showing
> responsibilities, and classes are just particular implementations of
> those responsibilities. In Ruby however, I'm wondering wether to name
> classes after the particular implementation, or the abstraction it
> implements.
>
> That was very hard to say in the abstract, so I'll throw up a specific
> example. I have a class that represents an AuctionHouse, but posts
> things to the actual warehouse over XMPP. In Java, I'd likely have a
> AuctionHouse interface, and an XMPPAuctionHouse implementation. In
> Ruby however, I can't name the responsibility like that, so do I name
> my class AuctionHouse or XMPPAuctionHouse?
>
> My current thinking is that you call the class AuctionHouse (because
> that's what external users of the abstraction want to know), but then
> when I read the code for class, it seems weird because it is all about
> XMPP details.
In Ruby, you can do this
class XmppAuctionHouse
...
end
AuctionHouse = XmppAuctionHouse
Now your clients can do `AuctionHouse.new` and they don't know that they're actually getting an instance of the XmppAuctionHouse. I use this mechanism for injecting stubs in test environments.
Alternatively, you can make AuctionHouse a module with the common implementation in a class ActionHouse::Base, then have modules like AuctionHouse::Xmpp that you mix to compose the ActionHouse that you want:
auction_house = AuctionHouse::Base.new.extend(AuctionHouse::Xmpp)
This gives more control to the client, but exposes more of your internals to them.
>
> On Feb 28, 6:36 pm, Steve Freeman <st...@m3p.co.uk> wrote:
>> I would hope that some of the principles apply. If people really wanted, I expect they could do some reflecto-magic to at least make sure that the implicit interfaces were consistent, and maybe cross-reference their implementors.
>>
>> S
>>
>> On 28 Feb 2011, at 16:50, tcrayford wrote:
>> Whilst java is very different from ruby, a lot of GOOS still applies
>>
>>> to ruby. The only major difference that would affect the style
>>> presented in GOOS is the lack of explicit interfaces. I've never
>>> really found that a problem though, you just have to take some more
>>> care when thinking about interfaces.
>>
>> Steve Freeman
>>
>> Winner of the Agile Alliance Gordon Pask award 2006
>> Book:http://www.growing-object-oriented-software.com
>>
>> +44 (0) 797 179 4105
>> M3P Limited. http://www.m3p.co.uk
>> Registered office. 2 Church Street, Burnham, Bucks, SL1 7HZ.
>> Company registered in England & Wales. Number 03689627
cheers,
Matt
ma...@mattwynne.net
07974 430184
The aliasing thing sounds interesting. It certainly fixes the naming
problem, which is the only problem I have with the lack of interfaces
right now.
The modules less so. I dislike inheritance quite a bit (for all the
problems discussed at length). Module inclusion is inheritance in
sheeps clothing.
A friend suggested me this video on SOLID Ruby:
http://confreaks.net/videos/185-rubyconf2009-solid-ruby
I think that what Jim Weirich calls protocols are a very good solution
but I'm not sure about write about them in the documentation is
enough.
But I can easily imaging to have a test on my Furnace class checking
that it implements OnOffDevice protocol using something like
furnace.repond_to?(:on, :off)
cheers
Uberto
Mixing in a module with one method to do "template method based
composition" might be ok, as described here:
http://fabiokung.com/2010/05/06/ruby-and-dependency-injection-in-a-dynamic-world/
Although I still feel like its service location (not inversion of
control).
Unfortunately mixing in a module involves all inheritance related
complications. Starting from coupling you with the entire module
private methods and variable definitions. That means, that lack of
encapsulation and total coupling to the module's internal code.
Therefore, IMHO, using mix-in to add a little bit more than a method
seems like going to the nasty way of composing behavior.
The problem is, including 55 modules and ending up with a class which
> No it isn't. You could just as easily think of module inclusion as
> composition,
has 300+ methods is composing behavior. But composing behavior of half
of your app into one huge object.
> though not perhaps with the current examples that are beingAgreed. But I still dont see how it achieve something that can't be
> given.
achieved without "new" and DI on construction.
> What modules in Ruby give you is far more power to create aJust a joke: the power is 56 modules and 300 methods? :)
> meaningful set of objects than Java's rather restrictive approach. You just
> don't yet know the full extent of that power!
my take is that some parts yes, some parts no, but I don't know which! ;)
anyway much more than in DI and interfaces, Ruby is different for the
metaprogramming abilities.
For example how can you grow a DSL guided by tests?
cheers
Uberto
I grow DSLs by refactoring mostly. If I have tests in the right places, I can refactor a DSL out of code that already works without changing the tests, and then when I want to start using the DSL somewhere else, I write new tests against the DSL's interface to lock down the behaviour that's being shared.
Hmmm... that's a good idea for a book...
--Nat
Yes!!
just to be clear, it's not difficult to test your DSL while it grows,
the hard part (well for me at least) is how to use your tests for
guide its grow.
For example, take somethink like Sinatra and move it till you have
your webapp completely defined as DSL, something like
when requestFor listOf Customer do
fetch first(pagination.size) Customers
display customers in evenOddRows Grid
end
cheers
Uberto
Regards
Guilherme Silveira
Caelum | Ensino e Inovação
http://www.caelum.com.br/