├── meta ├── name ├── title ├── version ├── created ├── organization ├── authors ├── summary ├── copyrights ├── repositories ├── requirements ├── description └── resources ├── Gemfile ├── try ├── .testrb ├── calculator.rb └── feature_example.rb ├── .gitignore ├── lib ├── lime │ ├── world.rb │ ├── advice.rb │ ├── step.rb │ ├── featurette.rb │ ├── scenario.rb │ └── feature.rb └── lime.rb ├── .yardopts ├── work ├── strawberry │ ├── lib │ │ ├── strawberry.rb │ │ └── strawberry │ │ │ ├── step.rb │ │ │ ├── runner.rb │ │ │ ├── scenario.rb │ │ │ ├── clause.rb │ │ │ └── feature.rb │ └── features │ │ └── addition.rb └── PROFILE ├── MANIFEST.txt ├── HISTORY.md ├── Assembly ├── .ruby ├── LICENSE.txt ├── README.md └── .gemspec /meta/name: -------------------------------------------------------------------------------- 1 | lime 2 | -------------------------------------------------------------------------------- /meta/title: -------------------------------------------------------------------------------- 1 | Lime 2 | -------------------------------------------------------------------------------- /meta/version: -------------------------------------------------------------------------------- 1 | 0.3.0 2 | -------------------------------------------------------------------------------- /meta/created: -------------------------------------------------------------------------------- 1 | 2011-08-11 2 | -------------------------------------------------------------------------------- /meta/organization: -------------------------------------------------------------------------------- 1 | Rubyworks 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | gemspec 3 | -------------------------------------------------------------------------------- /meta/authors: -------------------------------------------------------------------------------- 1 | --- 2 | - Trans 3 | -------------------------------------------------------------------------------- /meta/summary: -------------------------------------------------------------------------------- 1 | Pure Ruby Gherkin-style Test Framework 2 | -------------------------------------------------------------------------------- /meta/copyrights: -------------------------------------------------------------------------------- 1 | --- 2 | - 2011 Rubyworks (BSD-2-Clause) 3 | -------------------------------------------------------------------------------- /try/.testrb: -------------------------------------------------------------------------------- 1 | $:.unshift '.' 2 | require 'ae' 3 | require 'lime' 4 | -------------------------------------------------------------------------------- /meta/repositories: -------------------------------------------------------------------------------- 1 | --- 2 | upstream: git://github.com/proutils/lime.git 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .reap/digest 2 | .yardoc 3 | doc 4 | log 5 | pkg 6 | tmp 7 | web 8 | QED.rdoc 9 | -------------------------------------------------------------------------------- /lib/lime/world.rb: -------------------------------------------------------------------------------- 1 | module Lime 2 | 3 | # 4 | class World < Module 5 | end 6 | 7 | end 8 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --output-dir doc 2 | --protected 3 | --readme README.md 4 | lib 5 | - 6 | [A-Z]*.* 7 | 8 | -------------------------------------------------------------------------------- /meta/requirements: -------------------------------------------------------------------------------- 1 | --- 2 | - rubytest 3 | - detroit (build) 4 | - reap (build) 5 | - qed (test) 6 | - ae (test) 7 | -------------------------------------------------------------------------------- /meta/description: -------------------------------------------------------------------------------- 1 | Lime is a pure Ruby variation of Cucumber's Gherkin BDD test system 2 | that runs on top of RubyTest, a Universal Test Harness for Ruby. 3 | -------------------------------------------------------------------------------- /work/strawberry/lib/strawberry.rb: -------------------------------------------------------------------------------- 1 | require 'strawberry/feature' 2 | require 'strawberry/scenario' 3 | require 'strawberry/clause' 4 | require 'strawberry/step' 5 | 6 | -------------------------------------------------------------------------------- /meta/resources: -------------------------------------------------------------------------------- 1 | --- 2 | home: http://rubyworks.github.com/lime 3 | code: http://github.com/rubyworks/lime 4 | bugs: http://github.com/rubyworks/lime/issues 5 | mail: http://groups.google.com/groups/rubyworks-mailinglist 6 | -------------------------------------------------------------------------------- /try/calculator.rb: -------------------------------------------------------------------------------- 1 | class Calculator 2 | 3 | def initialize 4 | @stack = [] 5 | end 6 | 7 | def push(int) 8 | @stack << int 9 | end 10 | 11 | def add 12 | @stack.inject(0){ |sum, val| sum += val; sum } 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /MANIFEST.txt: -------------------------------------------------------------------------------- 1 | #!mast .ruby .yardopts bin lib spec test [A-Z]*.* 2 | .ruby 3 | .yardopts 4 | lib/lime/advice.rb 5 | lib/lime/feature.rb 6 | lib/lime/featurette.rb 7 | lib/lime/scenario.rb 8 | lib/lime/step.rb 9 | lib/lime/world.rb 10 | lib/lime.rb 11 | LICENSE.txt 12 | HISTORY.md 13 | README.md 14 | -------------------------------------------------------------------------------- /work/strawberry/lib/strawberry/step.rb: -------------------------------------------------------------------------------- 1 | module Strawberry 2 | 3 | # 4 | class Step 5 | 6 | # 7 | def intialize(type, description) 8 | @type = type 9 | @description = description 10 | end 11 | 12 | # 13 | def find(feature) 14 | features.clauses[@type].find{ |c| c =~ @description } 15 | end 16 | 17 | end 18 | 19 | end 20 | -------------------------------------------------------------------------------- /lib/lime/advice.rb: -------------------------------------------------------------------------------- 1 | module Lime 2 | 3 | # Test Advice 4 | class Advice 5 | 6 | # 7 | attr :table 8 | 9 | # New case instance. 10 | def initialize 11 | @table = Hash.new{ |h,k| h[k] = {} } 12 | end 13 | 14 | # 15 | def initialize_copy(original) 16 | @table = original.table.clone 17 | end 18 | 19 | # 20 | def [](type) 21 | @table[type.to_sym] 22 | end 23 | 24 | ## Returns the description with newlines removed. 25 | #def to_s 26 | # description.gsub(/\n/, ' ') 27 | #end 28 | 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /work/strawberry/lib/strawberry/runner.rb: -------------------------------------------------------------------------------- 1 | module Strawberry 2 | 3 | # 4 | class Runner 5 | 6 | def initialize 7 | end 8 | 9 | def run 10 | features.each do |feature| 11 | feature.each do |scenario| 12 | scope = Object.new 13 | scenario.steps.each do |step| 14 | clause = step.find(feature) 15 | begin 16 | clause.call(scope) 17 | rescue Exception => error 18 | puts error 19 | end 20 | end 21 | end 22 | end 23 | 24 | end 25 | 26 | end 27 | 28 | end 29 | 30 | -------------------------------------------------------------------------------- /work/strawberry/lib/strawberry/scenario.rb: -------------------------------------------------------------------------------- 1 | module Strawberry 2 | 3 | # 4 | class Scenario 5 | 6 | # 7 | def initialize(description, &block) 8 | @description = description 9 | 10 | @steps = [] 11 | 12 | instance_eval(&block) 13 | end 14 | 15 | attr :steps 16 | 17 | # 18 | def Given(description) 19 | @steps << Step.new(:given, descrption) 20 | end 21 | 22 | # 23 | def When(description) 24 | @steps << Step.new(:when, descrption) 25 | end 26 | 27 | # 28 | def Then(description) 29 | @steps << Step.new(:then, descrption) 30 | end 31 | 32 | end 33 | 34 | end 35 | -------------------------------------------------------------------------------- /work/strawberry/lib/strawberry/clause.rb: -------------------------------------------------------------------------------- 1 | module Strawberry 2 | 3 | class Clause 4 | 5 | # 6 | def initialize(type, match, &block) 7 | @type = type 8 | @match = match 9 | @block = block 10 | 11 | @regexp = calc_regexp(match) 12 | end 13 | 14 | # 15 | attr :match 16 | 17 | # 18 | def =~(description) 19 | @regexp =~ description 20 | end 21 | 22 | # 23 | def calc_regexp(match) 24 | case match 25 | when Regexp 26 | match 27 | when String 28 | Regexp.new(match) 29 | else 30 | # ??? 31 | end 32 | end 33 | 34 | # 35 | def call(scope) 36 | scope.instance_eval(&block) 37 | end 38 | 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /work/PROFILE: -------------------------------------------------------------------------------- 1 | --- 2 | title : Lime 3 | suite : RubyWorks 4 | summary: Gherkin-style Test Framework 5 | authors: 6 | - Thomas Sawyer 7 | 8 | description: 9 | Lime is a pure Ruby variation of Cucumber's Gherkin 10 | BDD test system that runs on top of the Ruby 11 | Universal Test Harness. 12 | 13 | resources: 14 | home: http://rubyworks.github.com/lime 15 | code: http://github.com/rubyworks/lime 16 | mail: http://groups.google.com/group/rubyworks-mailinglist 17 | 18 | repositories: 19 | upstream: git://github.com/proutils/lime.git 20 | 21 | copyrights: 22 | - 2011 Thomas Sawyer (BSD-2-Clause) 23 | 24 | requirements: 25 | - test 26 | - ae 27 | - detroit (build) 28 | - reap (build) 29 | - qed (test) 30 | 31 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # Release History 2 | 3 | ## 0.3.0 / 2012-03-03 4 | 5 | Fix execution scope so that method definitions in Featurettes 6 | are visible to advice procedures. Also will automatically require 7 | any `*.rb` files in `featurettes` directory relative to feature 8 | file. 9 | 10 | Changes: 11 | 12 | * Fix execution scope so that featurettes are included. 13 | * Automatically require featurette files. 14 | * Alias #I to #We. 15 | 16 | 17 | ## 0.2.0 / 2011-08-11 18 | 19 | Version 0.2 is first really usable release. 20 | 21 | Changes: 22 | 23 | * Renamed omit to skip. 24 | * Add World base class for adding test helpers. 25 | 26 | 27 | ## 0.1.0 / 2011-07-29 28 | 29 | Pushed very early release to get name up on RubyGems. 30 | 31 | Changes: 32 | 33 | * Happy Birthday. 34 | -------------------------------------------------------------------------------- /work/strawberry/features/addition.rb: -------------------------------------------------------------------------------- 1 | Feature "Addition" do 2 | So "to avoid silly mistakes" 3 | As "a math idiot" 4 | Be "told the sum of two numbers" 5 | 6 | Scenario "Add two numbers" do 7 | Given "I have a calculator" 8 | Given "I have entered 50 into the calculator" 9 | Given "I have entered 70 into the calculator" 10 | When "I press add" 11 | Then "the result should be 120 on the screen" 12 | end 13 | 14 | Given 'I have a calculator' do 15 | @calculator = Calculator.new 16 | end 17 | 18 | Given 'I have entered /\d+/ into the calculator' do |n| 19 | @calculator.push n.to_i 20 | end 21 | 22 | When 'I press add' do 23 | @result = calculator.add 24 | end 25 | 26 | Then 'the result should be /\d+/ on the screen' do |n| 27 | @result == n.to_i 28 | end 29 | end 30 | 31 | -------------------------------------------------------------------------------- /work/strawberry/lib/strawberry/feature.rb: -------------------------------------------------------------------------------- 1 | module Strawberry 2 | 3 | # 4 | class Feature 5 | 6 | # 7 | def initialize(description, &block) 8 | @description = description 9 | 10 | @clauses = {:given=>[], :when=>[], :then=>[] } 11 | 12 | instance_eval(&block) 13 | end 14 | 15 | # 16 | def Scenario(description, &block) 17 | @scenarios << Scenario.new(description, &block) 18 | end 19 | 20 | # 21 | def Given(match, &block) 22 | @clauses[:given] << Clause.new(:given, match, &block) 23 | end 24 | 25 | # 26 | def When(match, &block) 27 | @clauses[:when] << Clause.new(:when, match, &block) 28 | end 29 | 30 | # 31 | def Then(match, &block) 32 | @clauses[:then] << Clause.new(:then, match, &block) 33 | end 34 | 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /Assembly: -------------------------------------------------------------------------------- 1 | --- 2 | github: 3 | gh_pages: web 4 | 5 | gem: 6 | active: true 7 | 8 | dnote: 9 | title: Source Notes 10 | output: log/notes.html 11 | labels: ~ 12 | 13 | yard: 14 | yardopts: true 15 | 16 | #qed: 17 | # files : ~ 18 | # #exclude : ~ 19 | # #loadpath: ~ 20 | # #requires: ~ 21 | # #live : false 22 | # active : false 23 | 24 | #qedoc: 25 | # files : spec/ 26 | # output: QED.rdoc 27 | # active: false 28 | 29 | vclog: 30 | output: 31 | - log/changes.html 32 | - log/history.html 33 | active: false 34 | 35 | email: 36 | mailto : 37 | - ruby-talk@ruby-lang.org 38 | - rubyworks-mailinglist@googlegroups.com 39 | from : <%= ENV['EMAIL_ACCOUNT'] %> 40 | server : <%= ENV['EMAIL_SERVER'] %> 41 | port : <%= ENV['EMAIL_PORT'] %> 42 | account: <%= ENV['EMAIL_ACCOUNT'] %> 43 | domain : <%= ENV['EMAIL_DOMAIN'] %> 44 | login : <%= ENV['EMAIL_LOGIN'] %> 45 | secure : <%= ENV['EMAIL_SECURE'] %> 46 | 47 | -------------------------------------------------------------------------------- /lib/lime/step.rb: -------------------------------------------------------------------------------- 1 | module Lime 2 | 3 | # Test step. 4 | # 5 | class Step 6 | 7 | # New unit test procedure. 8 | # 9 | def initialize(scenario, label, settings={}) 10 | @scenario = scenario 11 | @label = label 12 | 13 | @type = settings[:type] 14 | 15 | @omit = false 16 | #@tested = false 17 | end 18 | 19 | # The type of step (:given, :when or :then). 20 | attr :type 21 | 22 | # The scenario to which this step belongs. 23 | attr :scenario 24 | 25 | # Label of test. 26 | attr :label 27 | 28 | # 29 | def omit? 30 | @omit 31 | end 32 | 33 | # 34 | def omit=(boolean) 35 | @omit = boolean 36 | end 37 | 38 | # 39 | def to_s 40 | "#{type.to_s.capitalize} #{label}" 41 | end 42 | 43 | # FIXME 44 | def subject 45 | end 46 | 47 | # 48 | def to_proc 49 | lambda{ call } 50 | end 51 | 52 | # 53 | #def match?(match) 54 | # match == target || match === label 55 | #end 56 | 57 | # 58 | def call 59 | scenario.run(self) 60 | end 61 | 62 | end 63 | 64 | end -------------------------------------------------------------------------------- /try/feature_example.rb: -------------------------------------------------------------------------------- 1 | Feature "Addition" do 2 | To "avoid silly mistakes" 3 | As "a math idiot" 4 | We "need to calculate the sum of numbers" 5 | 6 | Scenario "Add two numbers" do 7 | Given "I have a calculator" 8 | Given "I have entered 50 into the calculator" 9 | Given "I have entered 70 into the calculator" 10 | When "I press add" 11 | Then "the result should be 120 on the screen" 12 | end 13 | 14 | Scenario "Add three numbers" do 15 | Given "I have a calculator" 16 | Given "I have entered 50 into the calculator" 17 | Given "I have entered 70 into the calculator" 18 | Given "I have entered 90 into the calculator" 19 | When "I press add" 20 | Then "the result should be 210 on the screen" 21 | end 22 | 23 | Given 'I have a calculator' do 24 | require 'calculator' 25 | @calculator = Calculator.new 26 | end 27 | 28 | Given 'I have entered (\d+) into the calculator' do |n| 29 | @calculator.push n.to_i 30 | end 31 | 32 | When 'I press add' do 33 | @result = @calculator.add 34 | end 35 | 36 | Then 'the result should be (\d+) on the screen' do |n| 37 | @result.assert == n.to_i 38 | end 39 | end 40 | 41 | -------------------------------------------------------------------------------- /.ruby: -------------------------------------------------------------------------------- 1 | --- 2 | source: 3 | - meta 4 | authors: 5 | - name: Trans 6 | email: transfire@gmail.com 7 | copyrights: 8 | - holder: Rubyworks 9 | year: '2011' 10 | license: BSD-2-Clause 11 | requirements: 12 | - name: rubytest 13 | - name: detroit 14 | groups: 15 | - build 16 | development: true 17 | - name: reap 18 | groups: 19 | - build 20 | development: true 21 | - name: qed 22 | groups: 23 | - test 24 | development: true 25 | - name: ae 26 | groups: 27 | - test 28 | development: true 29 | dependencies: [] 30 | alternatives: [] 31 | conflicts: [] 32 | repositories: 33 | - uri: git://github.com/proutils/lime.git 34 | scm: git 35 | name: upstream 36 | resources: 37 | home: http://rubyworks.github.com/lime 38 | code: http://github.com/rubyworks/lime 39 | bugs: http://github.com/rubyworks/lime/issues 40 | mail: http://groups.google.com/groups/rubyworks-mailinglist 41 | extra: {} 42 | load_path: 43 | - lib 44 | revision: 0 45 | created: '2011-08-11' 46 | summary: Pure Ruby Gherkin-style Test Framework 47 | title: Lime 48 | version: 0.3.0 49 | name: lime 50 | description: ! 'Lime is a pure Ruby variation of Cucumber''s Gherkin BDD test system 51 | 52 | that runs on top of RubyTest, a Universal Test Harness for Ruby.' 53 | organization: Rubyworks 54 | date: '2012-03-03' 55 | -------------------------------------------------------------------------------- /lib/lime.rb: -------------------------------------------------------------------------------- 1 | # Ignore lime paths in backtraces 2 | ignore_path = File.expand_path(File.dirname(__FILE__) + '/lime') 3 | ignore_regexp = Regexp.new(Regexp.escape(ignore_path)) 4 | $RUBY_IGNORE_CALLERS ||= [] 5 | $RUBY_IGNORE_CALLERS << ignore_regexp 6 | 7 | # Make sure the global test array is defined. 8 | $TEST_SUITE ||= [] 9 | 10 | module Lime 11 | 12 | require 'lime/advice' 13 | require 'lime/feature' 14 | require 'lime/scenario' 15 | require 'lime/step' 16 | 17 | # Toplevel DSL. 18 | # 19 | module DSL 20 | 21 | # 22 | # Define a feature. 23 | # 24 | def Feature(label, &block) 25 | require_featurettes(File.dirname(caller[0])) 26 | 27 | $TEST_SUITE << Lime::Feature.new(:label=>label, &block) 28 | end 29 | 30 | alias :feature :Feature 31 | 32 | # 33 | # Require any featurettes located in `{feature_dir}/featurettes` directory. 34 | # 35 | def require_featurettes(feature_dir) 36 | featurettes_dir = File.join(feature_dir, 'featurettes') 37 | if File.directory?(featurettes_dir) 38 | featurette_files = Dir[File.join(featurettes_dir, '*.rb')] 39 | featurette_files.each do |file| 40 | require file 41 | end 42 | end 43 | end 44 | 45 | end 46 | 47 | end 48 | 49 | extend Lime::DSL 50 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Lime, Pure-Ruby Gherkin-style BDD Test Framework 2 | 3 | Copyright 2011 Rubyworks. All rights reserved. 4 | 5 | (BSD-2-Clause License) 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in the 15 | documentation and/or other materials provided with the distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 18 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 19 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 20 | COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | -------------------------------------------------------------------------------- /lib/lime/featurette.rb: -------------------------------------------------------------------------------- 1 | module Lime 2 | 3 | # Convenience method for creating a feature mixin. 4 | # 5 | # @example 6 | # 7 | # module MyStepDefinitions 8 | # include Lime::Featurette 9 | # 10 | # Given "customer's name is '(((\s+)))'" do |name| 11 | # @name = name 12 | # end 13 | # end 14 | # 15 | # Feature do 16 | # include MyStepDefinitions 17 | # end 18 | # 19 | module Featurette 20 | 21 | def self.append_features(base) 22 | base.extend(self) 23 | base.module_eval %{ 24 | @_advice = Hash.new{ |h,k| h[k] = {} } 25 | } 26 | end 27 | 28 | # Given ... 29 | # 30 | # @param [String] description 31 | # A brief description of the _given_ criteria. 32 | # 33 | def Given(description, &procedure) 34 | @_advice[:given][description] = procedure 35 | end 36 | 37 | alias :given :Given 38 | 39 | # When ... 40 | # 41 | # @param [String] description 42 | # A brief description of the _when_ criteria. 43 | # 44 | def When(description, &procedure) 45 | @_advice[:when][description] = procedure 46 | end 47 | 48 | alias :wence :When 49 | 50 | # Then ... 51 | # 52 | # @param [String] description 53 | # A brief description of the _then_ criteria. 54 | # 55 | def Then(description, &procedure) 56 | @_advice[:then][description] = procedure 57 | end 58 | 59 | alias :hence :Then 60 | 61 | # Access to advice. 62 | def [](key) 63 | @_advice[key] 64 | end 65 | 66 | end 67 | 68 | end 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lime 2 | 3 | [Website](http://rubyworks.github.com/lime) / 4 | [Source Code](http://github.com/rubyworks/lime) / 5 | [Report Issue](http://github.com/rubyworks/lime/issues) / 6 | [Mailing List](http://groups.google.com/groups/rubyworks-mailinglist) 7 | 8 | 9 | ## Description 10 | 11 | Lime is pure-Ruby Gherkin-style test framework. 12 | 13 | 14 | ## Instruction 15 | 16 | Lime lets you write features scripts using Ruby, yet still do 17 | so with a close approximation to Gherkin domain language. 18 | 19 | ``` ruby 20 | Feature "Addition" do 21 | To "avoid silly mistakes" 22 | As "a math idiot" 23 | We "need to calculate the sum of numbers" 24 | 25 | Scenario "Add two numbers" do 26 | Given "I have a calculator" 27 | Given "I have entered 50 into the calculator" 28 | Given "I have entered 70 into the calculator" 29 | When "I press add" 30 | Then "the result should be 120 on the screen" 31 | end 32 | 33 | Scenario "Add three numbers" do 34 | Given "I have a calculator" 35 | Given "I have entered 50 into the calculator" 36 | Given "I have entered 70 into the calculator" 37 | Given "I have entered 90 into the calculator" 38 | When "I press add" 39 | Then "the result should be 2101 on the screen" 40 | end 41 | 42 | Given 'I have a calculator' do 43 | require 'calculator' 44 | @calculator = Calculator.new 45 | end 46 | 47 | Given 'I have entered (((\d+))) into the calculator' do |n| 48 | @calculator.push n.to_i 49 | end 50 | 51 | When 'I press add' do 52 | @result = @calculator.add 53 | end 54 | 55 | Then 'the result should be (((\d+))) on the screen' do |n| 56 | @result.assert == n.to_i 57 | end 58 | end 59 | ``` 60 | 61 | The last set of `Given` and `When` procedures are called the *advice definitions*. These 62 | can be placed in their own modules and included into the Feature scope like any other module. 63 | They simply need to include the `Lime::Featurette` module to do so. For instance: 64 | 65 | ```ruby 66 | module CalculatorAdvice 67 | include Lime::Featurette 68 | 69 | Given 'I have a calculator' do 70 | require 'calculator' 71 | @calculator = Calculator.new 72 | end 73 | 74 | Given 'I have entered (((\d+))) into the calculator' do |n| 75 | @calculator.push n.to_i 76 | end 77 | 78 | When 'I press add' do 79 | @result = @calculator.add 80 | end 81 | 82 | Then 'the result should be (((\d+))) on the screen' do |n| 83 | @result.assert == n.to_i 84 | end 85 | end 86 | ``` 87 | 88 | If you add such scripts to a subdirectory called `featurettes` relative to the 89 | feature that uses them, then they will be loaded automatically when features 90 | are run. 91 | 92 | Speaking of which, to run features use the `rubytest` command line tool. 93 | 94 | ``` 95 | $ rubytest -Ilib spec/feature_addition.rb 96 | ``` 97 | 98 | See [RubyTest](http://rubyworks.github.com/rubytest) to learn more. 99 | 100 | 101 | ## Copyrights 102 | 103 | Copyright (c) 2011 Rubyworks 104 | 105 | Lime is distributed according to the terms of the *FreeBSD* license. 106 | 107 | See LICENSE.txt for details. 108 | -------------------------------------------------------------------------------- /lib/lime/scenario.rb: -------------------------------------------------------------------------------- 1 | require 'lime/step' 2 | 3 | module Lime 4 | 5 | # 6 | class Scenario 7 | 8 | # 9 | def initialize(feature, settings={}, &block) 10 | @feature = feature 11 | 12 | @label = settings[:label] 13 | @skip = settings[:skip] 14 | 15 | @steps = [] 16 | 17 | @scope = Scope.new(self, &block) 18 | end 19 | 20 | # Parent feature. 21 | attr :feature 22 | 23 | # Description of scenario. 24 | attr :label 25 | 26 | # List scenario steps. 27 | attr :steps 28 | 29 | # Evaluation scope. 30 | attr :scope 31 | 32 | # Skip this scenario from runs. 33 | # 34 | # @return [Boolean,String] reason for skipping 35 | def skip? 36 | @skip 37 | end 38 | 39 | # Scenario steps must be run in order. 40 | def ordered? 41 | true 42 | end 43 | 44 | # Iterate over steps. 45 | def each(&block) 46 | @steps.each(&block) 47 | end 48 | 49 | # Number of steps. 50 | def size(&block) 51 | @steps.size 52 | end 53 | 54 | # The type is "Scenario". This is used by Ruby Test in test output. 55 | def type 56 | "Scenario" 57 | end 58 | 59 | # Provided the scenario label. 60 | def to_s 61 | @label.to_s 62 | end 63 | 64 | # FIXME 65 | def topic 66 | end 67 | 68 | # 69 | # Run a step in the context of this scenario. 70 | # 71 | def run(step) 72 | type = step.type 73 | desc = step.label 74 | feature.advice[type].each do |mask, proc| 75 | if md = match_regexp(mask).match(desc) 76 | scope.instance_exec(*md[1..-1], &proc) 77 | end 78 | end 79 | end 80 | 81 | # 82 | #def find 83 | # features.clauses[@type].find{ |c| c =~ @description } 84 | #end 85 | 86 | # 87 | # Convert matching string into a regular expression. If the string 88 | # contains parentheticals, e.g. `(.*?)`, the text within them is 89 | # treated as a case-insensitve back-referenceing regular expression 90 | # and kept verbatium. 91 | # 92 | # To use a regular expression, but leave the resulting match out of 93 | # the backreferences use `?:`, e.g. `(?:\d+)`. 94 | # 95 | def match_regexp(str) 96 | ## the old way required double and triple parens 97 | #str = str.split(/(\(\(.*?\)\))(?!\))/).map{ |x| 98 | # x =~ /\A\(\((.*)\)\)\Z/ ? $1 : Regexp.escape(x) 99 | #}.join 100 | str = str.split(/(\(.*?\))(?!\))/).map{ |x| 101 | x =~ /\A\((.*)\)\Z/ ? "(#{$1})" : Regexp.escape(x) 102 | }.join 103 | str = str.gsub(/\\\s+/, '\s+') 104 | Regexp.new(str, Regexp::IGNORECASE) 105 | end 106 | 107 | # TODO: Need to ensure the correct order of Given, When, Then. 108 | 109 | # 110 | class Scope < Module 111 | 112 | # 113 | def initialize(scenario, &block) 114 | @scenario = scenario 115 | 116 | #include(scenario.feature.scope) 117 | 118 | module_eval(&block) 119 | end 120 | 121 | # Given ... 122 | # 123 | # @param [String] description 124 | # A matching description of the _given_ procedure. 125 | # 126 | def Given(label) 127 | @scenario.steps << Step.new(@scenario, label, :type=>:given) 128 | end 129 | 130 | alias :given :Given 131 | 132 | # When ... 133 | # 134 | # @param [String] label 135 | # A matching description of the _when_ procedure. 136 | # 137 | def When(label) 138 | @scenario.steps << Step.new(@scenario, label, :type=>:when) 139 | end 140 | 141 | alias :wence :When 142 | 143 | # Then ... 144 | # 145 | # @param [String] label 146 | # A matching description of the _then_ procedure. 147 | # 148 | def Then(label) 149 | @scenario.steps << Step.new(@scenario, label, :type=>:then) 150 | end 151 | 152 | alias :hence :Then 153 | 154 | end 155 | 156 | end 157 | 158 | end 159 | -------------------------------------------------------------------------------- /lib/lime/feature.rb: -------------------------------------------------------------------------------- 1 | module Lime 2 | 3 | #require 'lime/pending' 4 | require 'lime/world' 5 | require 'lime/advice' 6 | require 'lime/scenario' 7 | require 'lime/featurette' 8 | 9 | # Features contain scenarios. 10 | # 11 | # The `advice` are _given_, _when_ and _then_ rules. 12 | # 13 | class Feature 14 | 15 | # Brief description of the feature. 16 | attr :label 17 | 18 | # The descriptive details of the feature, defined using 19 | # the `To`, `As` and `We` methods. 20 | attr :story 21 | 22 | # List of scenarios. 23 | attr :scenarios 24 | 25 | # Advice are labeled procedures, such as before 26 | # and after advice. 27 | attr :advice 28 | 29 | # Module for evaluating tests. 30 | attr :scope 31 | 32 | # 33 | def initialize(settings={}, &block) 34 | @label = settings[:label] 35 | @setup = settings[:setup] 36 | 37 | @advice = Advice.new 38 | 39 | @story = [] 40 | @scenarios = [] 41 | 42 | @scope = Scope.new(self, &block) 43 | 44 | @scenarios.each do |scenario| 45 | scenario.scope.__send__(:extend, @scope) 46 | end 47 | end 48 | 49 | # Convenience method for accessing advice, aka step definitions. 50 | def [](key) 51 | @advice[key] 52 | end 53 | 54 | # Iterate over each scenario. 55 | def each(&block) 56 | scenarios.each(&block) 57 | end 58 | 59 | # Number of scenarios. 60 | def size 61 | scenarios.size 62 | end 63 | 64 | # Subclasses of TestCase can override this to describe 65 | # the type of test case they define. 66 | def type 67 | 'Feature' 68 | end 69 | 70 | # 71 | def to_s 72 | (["#{label}"] + story).join("\n") 73 | end 74 | 75 | # 76 | def update(mixin) 77 | @advice[:given].update( mixin[:given] || {} ) 78 | @advice[:when].update( mixin[:when] || {} ) 79 | @advice[:then].update( mixin[:then] || {} ) 80 | 81 | #@scope.__send__(:include, mixin) if Module === mixin 82 | end 83 | 84 | # 85 | class Scope < World 86 | 87 | # 88 | def initialize(feature, &block) 89 | @_feature = feature 90 | @_skip = false 91 | 92 | module_eval(&block) if block 93 | end 94 | 95 | # 96 | def To(description) 97 | @_feature.story << "To " + description 98 | end 99 | 100 | alias :to :To 101 | 102 | # 103 | def As(description) 104 | @_feature.story << "As " + description 105 | end 106 | 107 | alias :as :As 108 | 109 | # 110 | def We(description) 111 | @_feature.story << "We " + description 112 | end 113 | 114 | alias :we :We 115 | 116 | alias :I :We 117 | alias :i :I 118 | 119 | # 120 | # Define a Scenario. 121 | # 122 | def Scenario(label, &procedure) 123 | scenario = Scenario.new( 124 | @_feature, 125 | :skip => @_skip, 126 | :label => label, 127 | &procedure 128 | ) 129 | @_feature.scenarios << scenario 130 | @_skip = false 131 | scenario 132 | end 133 | 134 | alias :scenario :Scenario 135 | 136 | # Given ... 137 | # 138 | # @param [String] description 139 | # A brief description of the _given_ criteria. 140 | # 141 | def Given(description, &procedure) 142 | @_feature[:given][description] = procedure 143 | end 144 | 145 | alias :given :Given 146 | 147 | # When ... 148 | # 149 | # @param [String] description 150 | # A brief description of the _when_ criteria. 151 | # 152 | def When(description, &procedure) 153 | @_feature[:when][description] = procedure 154 | end 155 | 156 | alias :wence :When 157 | 158 | # Then ... 159 | # 160 | # @param [String] description 161 | # A brief description of the _then_ criteria. 162 | # 163 | def Then(description, &procedure) 164 | @_feature[:then][description] = procedure 165 | end 166 | 167 | alias :hence :Then 168 | 169 | # Skip the next scenario when running feature. 170 | # 171 | # skip "for some reason" 172 | # Scenario "blah blah blah" do 173 | # # ... 174 | # end 175 | # 176 | # @todo Use block form of this instead? 177 | def skip(reason=true) 178 | @_skip = reason 179 | end 180 | 181 | # 182 | # Access to underlying feature instance. 183 | # 184 | def _feature 185 | @_feature 186 | end 187 | 188 | # 189 | # Include module and if a Featurette, also update Feature instance. 190 | # 191 | def include(mixin) 192 | if Featurette === mixin 193 | @_feature.update(mixin) 194 | end 195 | super(mixin) 196 | end 197 | end 198 | 199 | end 200 | 201 | end 202 | -------------------------------------------------------------------------------- /.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'yaml' 4 | 5 | module DotRuby 6 | 7 | # 8 | class GemSpec 9 | 10 | # For which revision of .ruby is this gemspec intended? 11 | REVISION = 0 unless defined?(REVSION) 12 | 13 | # 14 | PATTERNS = { 15 | :bin_files => 'bin/*', 16 | :lib_files => 'lib/{**/}*.rb', 17 | :ext_files => 'ext/{**/}extconf.rb', 18 | :doc_files => '*.{txt,rdoc,md,markdown,tt,textile}', 19 | :test_files => '{test/{**/}*_test.rb,spec/{**/}*_spec.rb}' 20 | } unless defined?(PATTERNS) 21 | 22 | # 23 | def self.instance 24 | new.to_gemspec 25 | end 26 | 27 | attr :metadata 28 | 29 | attr :manifest 30 | 31 | # 32 | def initialize 33 | @metadata = YAML.load_file('.ruby') 34 | @manifest = Dir.glob('manifest{,.txt}', File::FNM_CASEFOLD).first 35 | 36 | if @metadata['revision'].to_i != REVISION 37 | warn "You have the wrong revision. Trying anyway..." 38 | end 39 | end 40 | 41 | # 42 | def scm 43 | @scm ||= \ 44 | case 45 | when File.directory?('.git') 46 | :git 47 | end 48 | end 49 | 50 | # 51 | def files 52 | @files ||= \ 53 | #glob_files[patterns[:files]] 54 | case 55 | when manifest 56 | File.readlines(manifest). 57 | map{ |line| line.strip }. 58 | reject{ |line| line.empty? || line[0,1] == '#' } 59 | when scm == :git 60 | `git ls-files -z`.split("\0") 61 | else 62 | Dir.glob('{**/}{.*,*}') # TODO: be more specific using standard locations ? 63 | end.select{ |path| File.file?(path) } 64 | end 65 | 66 | # 67 | def glob_files(pattern) 68 | Dir.glob(pattern).select { |path| 69 | File.file?(path) && files.include?(path) 70 | } 71 | end 72 | 73 | # 74 | def patterns 75 | PATTERNS 76 | end 77 | 78 | # 79 | def executables 80 | @executables ||= \ 81 | glob_files(patterns[:bin_files]).map do |path| 82 | File.basename(path) 83 | end 84 | end 85 | 86 | def extensions 87 | @extensions ||= \ 88 | glob_files(patterns[:ext_files]).map do |path| 89 | File.basename(path) 90 | end 91 | end 92 | 93 | # 94 | def name 95 | metadata['name'] || metadata['title'].downcase.gsub(/\W+/,'_') 96 | end 97 | 98 | # 99 | def to_gemspec 100 | Gem::Specification.new do |gemspec| 101 | gemspec.name = name 102 | gemspec.version = metadata['version'] 103 | gemspec.summary = metadata['summary'] 104 | gemspec.description = metadata['description'] 105 | 106 | metadata['authors'].each do |author| 107 | gemspec.authors << author['name'] 108 | 109 | if author.has_key?('email') 110 | if gemspec.email 111 | gemspec.email << author['email'] 112 | else 113 | gemspec.email = [author['email']] 114 | end 115 | end 116 | end 117 | 118 | gemspec.licenses = metadata['copyrights'].map{ |c| c['license'] }.compact 119 | 120 | metadata['requirements'].each do |req| 121 | name = req['name'] 122 | version = req['version'] 123 | groups = req['groups'] || [] 124 | 125 | case version 126 | when /^(.*?)\+$/ 127 | version = ">= #{$1}" 128 | when /^(.*?)\-$/ 129 | version = "< #{$1}" 130 | when /^(.*?)\~$/ 131 | version = "~> #{$1}" 132 | end 133 | 134 | if groups.empty? or groups.include?('runtime') 135 | # populate runtime dependencies 136 | if gemspec.respond_to?(:add_runtime_dependency) 137 | gemspec.add_runtime_dependency(name,*version) 138 | else 139 | gemspec.add_dependency(name,*version) 140 | end 141 | else 142 | # populate development dependencies 143 | if gemspec.respond_to?(:add_development_dependency) 144 | gemspec.add_development_dependency(name,*version) 145 | else 146 | gemspec.add_dependency(name,*version) 147 | end 148 | end 149 | end 150 | 151 | # convert external dependencies into a requirements 152 | if metadata['external_dependencies'] 153 | ##gemspec.requirements = [] unless metadata['external_dependencies'].empty? 154 | metadata['external_dependencies'].each do |req| 155 | gemspec.requirements << req.to_s 156 | end 157 | end 158 | 159 | # determine homepage from resources 160 | homepage = metadata['resources'].find{ |key, url| key =~ /^home/ } 161 | gemspec.homepage = homepage.last if homepage 162 | 163 | gemspec.require_paths = metadata['load_path'] || ['lib'] 164 | gemspec.post_install_message = metadata['install_message'] 165 | 166 | # RubyGems specific metadata 167 | gemspec.files = files 168 | gemspec.extensions = extensions 169 | gemspec.executables = executables 170 | 171 | if Gem::VERSION < '1.7.' 172 | gemspec.default_executable = gemspec.executables.first 173 | end 174 | 175 | gemspec.test_files = glob_files(patterns[:test_files]) 176 | 177 | unless gemspec.files.include?('.document') 178 | gemspec.extra_rdoc_files = glob_files(patterns[:doc_files]) 179 | end 180 | end 181 | end 182 | 183 | end #class GemSpec 184 | 185 | end 186 | 187 | DotRuby::GemSpec.instance 188 | --------------------------------------------------------------------------------