What is this ?

A tool that generates an API documentation using RSpec and some DSL magic. If successful I might use it for my own project (neo4j.rb) This is an early experiment !

How ?

Instead of writing specs like this:

describe Account do
    context "transfering money" do
      it "deposits transfer amount to the other account" do
        source = Account.new(50, :USD)
        target = mock('target account')
        target.should_receive(:deposit).with(Money.new(5, :USD))
        source.transfer(5, :USD).to(target)
      end

      it "reduces its balance by the transfer amount" do
        source = Account.new(50, :USD)
        target = stub('target account')
        source.transfer(5, :USD).to(target)
        source.balance.should == Money.new(45, :USD)
      end
    end
  end

Which generates the following output

$ spec ./spec/account_spec.rb --format nested
Account
  transfering money
    deposits transfer amount to the other account
    reduces its balance by the transfer amount

2 examples, 0 failures

I (also) want to generate a detailed API documentation something like this from a RSpec Macro DSL:

Account
  Public Static Methods
    #new ()
      Given
        no arguments
      Then
        Return Account with 0 USD
          has #balance == 0
          has #currency == 'USD
    #new (amount,currency)
      Scenario account and currency has valid values
        Given
          arguments 50, USD
        Then
          Return an Account with given amount and currency
            should == 50
            should == "USD"
  Public Instance Methods
    #transfer (amount,currency)
      Scenario transfer amount and currency have valid values
        Given
          arguments 5, USD
        Then
          should not change subject
          Return A transfer of 5 USD from Account with 50 USD
            should be a kind of TransferDSL
Finished in 0.00412 seconds
9 examples, 0 failures

TransferDSL
  Public Static Methods
    #new (source_account,amount,currency)
      Given
        arguments Account balance: 50 USD, 5, USD
      Then
        Return An TransferDSL instance with initialized source account, amount and currency
          should == 50
  Public Instance Methods
    #to (target_account)
      Given
        arguments Account balance: 10 USD
      Then it should add money on the target account
        should add money to the target account

Finished in 0.00225 seconds
4 examples, 0 failures

The above is generated from the following RSpec file:

describe Account do

  static_methods do
    new do
      Return("Account with 0 USD") do
        # it_behaves_like "Account with 0 USD" can also be used
        it("has #balance == 0") { subject.balance.should == 0 }
        it("has #currency == 'USD") { subject.currency.should == 'USD' }
      end
    end

    new(arg(:amount), arg(:currency)) do
      Scenario 'account and currency has valid values' do
        Given do
          arg.amount   = 50
          arg.currency = 'USD'
        end

        Return "an Account with given amount and currency" do
          it { subject.balance.should == 50 } #given.amount }
          it { subject.currency.should == 'USD' } # given.currency }
        end
      end
    end
  end

  instance_methods do
    transfer(arg(:amount), arg(:currency)) do
#      Description 'bla bla bla'
      Scenario 'transfer amount and currency have valid values' do
        subject { Account.new(50, 'USD') }
        Given do
          arg.amount = 5
          arg.currency = 'USD'
        end
        Return "A transfer of 5 USD from Account with 50 USD" do
          it { should be_kind_of(TransferDSL) }
        end
        Then do
         it "should not change subject" do
            given.subject.balance.should == subject.balance
          end
        end
      end
    end
  end
end

describe TransferDSL do
  static_methods do
    new(arg(:source_account) do
      Given do
        arg.source_account = Account.new(50, 'USD')
      end
      Returns("A Transfer DSL with the given source account") do
         it {subject.source_account.should  == given.source_account}
      end

  end

  instance_methods do
    to(arg(:target_account, arg(:amount), arg(:currency)) do
      Scenario 'source account has enough money' do
        Given do
          arg.target_account = Account.new(50, 'USD')
          arg.amount = 5
          arg.currency = 'USD'
          subject{TransferDSL.new(Account.new(50, 'USD')}
        end

        # the following line describes the subject's source_account method
        Then "it added money to the target account" do
           it { subject.target_account.amount.should == given.arg.source_account.amount + given.arg.amount }
        end
      end

      Scenario 'source account does NOT have enough money' do
        Given do
          arg.target_account = Account.new(50, 'USD')
          arg.amount = 100
          arg.currency = 'USD'
          subject{TransferDSL.new(Account.new(10, 'USD')}
        end
        Throws(Error)
    end
  end

end

TODO: I will implement a new RSpec HTML formatter which will generate something similar to RDoc. I want to write specification in my rspec code instead of using RDoc.

Example

gem install rspec --prerelease (2.0.0.beta.19)
rspec -f d -c spec