Skip to content

Commit

Permalink
contract stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanbrunner committed Feb 7, 2012
1 parent a9704d7 commit 262659e
Show file tree
Hide file tree
Showing 9 changed files with 283 additions and 1 deletion.
2 changes: 1 addition & 1 deletion contract.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ Gem::Specification.new do |s|
s.require_paths = ["lib"]

# specify any dependencies here; for example:
# s.add_development_dependency "rspec"
s.add_development_dependency "rspec"
# s.add_runtime_dependency "rest-client"
end
11 changes: 11 additions & 0 deletions examples/example.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class ExampleClass
define_method :foo do
'bar'
end

def define_method(name, &block)

end
end

ExampleClass.new.foo # bar
2 changes: 2 additions & 0 deletions lib/contract.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
require "contract/version"

autoload :DesignContracts, 'contract/design_contract'
require 'contract/design_contract'
module Contract
# Your code goes here...
end
66 changes: 66 additions & 0 deletions lib/contract/design_contract.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@

module Contract::DesignContracts
def define_contract_method (method, *args, &block)
config = init_config(method)
puts config.to_yaml
config.instance_eval &block


define_method method do |*inst_args|
config.setup_args(args, inst_args, self)
raise "Pre-condition not satisfied" unless config.preconditions.all? { |pre| config.instance_eval &pre }

config.instance_eval &config.execute if config.execute
self.send(method)
end


define_method "can_#{method}?" do |*inst_args|
config.setup_args(args, inst_args, self)
config.preconditions.all? { |pre| config.instance_eval &pre } && send(method, *inst_args)
self.send("super_can_#{method}?")
end
end

private

def init_config(method_name)
puts "here"
@methods ||= {}
return @methods[method_name] || @methods[method_name] = Contract::Config.new
end
end

class Contract::Config
attr_reader :preconditions
attr_reader :execute

def initialize
@preconditions = []
@execute = nil
end

def method_missing(method, *args)
@caller.send(method, *args)
end

def metaclass
class << self; self; end
end

def requires(&block)
@preconditions << block
end

def implementation(&block)
@execute = block
end

def setup_args(args, inst, caller)
@caller = caller

args.each_with_index do |a, i|
metaclass.send(:define_method, a) { inst[i] }
end
end
end
60 changes: 60 additions & 0 deletions spec/example_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
require 'spec_helper'

class ExampleClass
extend Contract::DesignContracts

define_contract_method :divide, :num, :denominator do
requires { denominator > 0 }
implementation do
num / denominator
end
end

end

describe ExampleClass do
subject { ExampleClass }
it { should respond_to :define_contract_method }

describe "#divide" do
specify { ExampleClass.new.should respond_to :divide }

subject { ExampleClass.new.divide(numerator, denominator) }
let(:numerator) { 2 }

context "Denominator = 0" do
let(:denominator) { 0 }

specify { expect { subject }.to raise_error }
end

context "Denominator = -1" do
let(:denominator) { -1 }

specify { expect { subject }.to raise_error }
end

context "Denominator = 1" do
let(:denominator) { 1 }
it { should == 2 }
end
end


describe "#can_divide?" do
specify { ExampleClass.new.should respond_to :can_divide? }

subject { ExampleClass.new.can_divide?(numerator, denominator) }
let(:numerator) { 2 }

context "Denominator = 0" do
let(:denominator) { 0 }
it { should be_false }
end

context "Denominator = 1" do
let(:denominator) { 1 }
it { should be_true }
end
end
end
54 changes: 54 additions & 0 deletions spec/inheritance_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
require 'spec_helper'

class OpenExample
extend Contract::DesignContracts

attr_accessor :is_deleted

define_contract_method :delete_stuff do
implementation do
is_deleted = true
end
end
end

class SecuredExample < OpenExample
attr_accessor :password_provided

define_contract_method :delete_stuff do
requires { password_provided }
end
end

describe OpenExample do
describe "#can_delete_stuff?" do
specify { example_class.should respond_to :can_delete_stuff? }
let (:example_class) { OpenExample.new }
subject { example_class.can_delete_stuff? }

it { should be_true }
end
end

describe SecuredExample do
describe "#can_delete_stuff?" do
let (:example_class) { SecuredExample.new }

specify { example_class.should respond_to :can_delete_stuff? }
subject { example_class.can_delete_stuff? }

context "without password provided" do
before { example_class.password_provided = false }
it { should be_false }
end
context "with password provided" do
before { example_class.password_provided = true }
it { should be_true }
# check that we can actually call the method too (i.e. implementation wasn't wiped out)
specify {
example_class.delete_stuff
example_class.is_deleted.should == true
}
end
end
end
34 changes: 34 additions & 0 deletions spec/instance_variable_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require 'spec_helper'

class ExampleAddClass
extend Contract::DesignContracts

attr_accessor :allow_add

define_contract_method :add, :num, :num2 do
requires { allow_add }
implementation do
num + num2
end
end

end

describe ExampleAddClass do

describe "#can_add?" do
specify { example_class.should respond_to :can_add? }
let (:example_class) { ExampleAddClass.new }
subject { example_class.can_add?(1,1) }

context "Don't allow add" do
before { example_class.allow_add = false }
it { should be_false }
end

context "Allow add" do
before { example_class.allow_add = true }
it { should be_true }
end
end
end
46 changes: 46 additions & 0 deletions spec/multiple_require_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
require 'spec_helper'

class ExampleAddPositivesClass
extend Contract::DesignContracts

define_contract_method :add_positives, :num, :num2 do
requires { num > 0 }
requires { num2 > 0 }

implementation do
num + num2
end
end

end

describe ExampleAddPositivesClass do

describe "#can_add?" do
specify { example_class.should respond_to :can_add_positives? }
let (:example_class) { ExampleAddPositivesClass.new }
let(:num1) { 1 }
let(:num2) { 1 }
subject { example_class.can_add_positives?(num1,num2) }

context "Num1 is negative" do
let(:num1) { -1 }
it { should be_false }
end

context "Num2 is negative" do
let(:num2) { -1 }
it { should be_false }
end

context "Both are negative" do
let(:num1) { -1 }
let(:num2) { -1 }
it { should be_false }
end

context "Both are positive" do
it { should be_true }
end
end
end
9 changes: 9 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require 'bundler/setup'

# require 'rspec'

require 'contract'

RSpec.configure do |config|

end

0 comments on commit 262659e

Please sign in to comment.