contract_acceptance_framework 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in contract_acceptance_framework.gemspec
4
+ gemspec
data/README ADDED
@@ -0,0 +1,53 @@
1
+ == ContractAcceptanceFramework
2
+
3
+ This gem is meant to be used to allow ActiveRecord objects to signify agreement with versioned contracts. It requires some database tables.
4
+
5
+ To that end, you need a migration that looks something like this:
6
+
7
+ class CreateContractAcceptanceFrameworkTables < ActiveRecord::Migration
8
+ def self.up
9
+ create_table :contracts do |t|
10
+ t.string :key
11
+ t.text :content
12
+ t.integer :major_version
13
+ t.integer :minor_version
14
+ t.integer :maintenance_version
15
+ t.timestamps
16
+ end
17
+ add_index :contracts, [:major_version, :minor_version, :maintenance_version, :key], :name => 'contracts_version_index'
18
+ add_index :contracts, [:key]
19
+
20
+ create_table :contract_acceptances do |t|
21
+ t.integer :contract_id
22
+ t.timestamps
23
+ t.integer :acceptable_id
24
+ t.string :acceptable_type
25
+ end
26
+ add_index :contract_acceptances, [:acceptable_id, :acceptable_type]
27
+ add_index :contract_acceptances, :contract_id
28
+ end
29
+
30
+ def self.down
31
+ remove_index :contract_acceptances, :column => :contract_id
32
+ remove_index :contract_acceptances, :column => [:acceptable_id, :acceptable_type]
33
+ drop_table :contract_acceptances
34
+
35
+ remove_index :contracts, :column => [:key]
36
+ remove_index :contracts, 'contracts_version_index'
37
+ drop_table :contracts
38
+ end
39
+ end
40
+
41
+ After that, you just include a module in your class that accepts contracts like so:
42
+
43
+ class SomeClass < ActiveRecord::Base
44
+ include ContractAcceptanceFramework
45
+ end
46
+
47
+ That's it, you're done. Now the class will respond to the following methods:
48
+
49
+ # where version looks like '1.2.3' and will be parsed into the appropriate major, minor, maintenance query
50
+ - has_agreed_to?(contract)
51
+ - agree_to!(contract)
52
+
53
+ If you've already agreed to a contract and try to agree again, it will raise a ContractDuplicateAgreementError
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "contract_acceptance_framework/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "contract_acceptance_framework"
7
+ s.version = ContractAcceptanceFramework::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Josh Adams"]
10
+ s.email = ["josh@isotope11.com"]
11
+ s.homepage = "http://rubygems.org/gems/contract_acceptance_framework"
12
+ s.summary = %q{Allow rails objects to agree to versioned contracts}
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+ s.rubyforge_project = "contract_acceptance_framework"
16
+
17
+ s.add_development_dependency "bundler", ">= 1.0.0.rc.6"
18
+ s.add_development_dependency "rspec", "~> 2.4.0"
19
+ s.add_development_dependency "sqlite3", "~> 1.3.3"
20
+
21
+ s.add_dependency 'activesupport', '~> 3.0.0'
22
+ s.add_dependency 'activerecord', '~> 3.0.0'
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
+ s.require_paths = ["lib"]
28
+ end
@@ -0,0 +1,24 @@
1
+ require 'contract_acceptance_framework/contract'
2
+ require 'contract_acceptance_framework/contract_acceptance'
3
+
4
+ module ContractAcceptanceFramework
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ raise "class must inherit from ActiveRecord::Base" unless self < ActiveRecord::Base
9
+
10
+ has_many :contract_acceptances, :as => :acceptable
11
+ has_many :contracts, :through => :contract_acceptances
12
+
13
+ def has_agreed_to?(contract)
14
+ contracts.include?(contract)
15
+ end
16
+
17
+ def agree_to!(contract)
18
+ raise ContractDuplicateAgreementError if has_agreed_to?(contract)
19
+ contract_acceptances.create(:contract => contract)
20
+ end
21
+ end
22
+ end
23
+
24
+ class ContractDuplicateAgreementError < StandardError; end
@@ -0,0 +1,26 @@
1
+ class Contract < ActiveRecord::Base
2
+ has_many :contract_acceptances
3
+
4
+ validates :maintenance_version, :uniqueness => { :scope => [:minor_version, :major_version, :key] }
5
+ validates :maintenance_version, :minor_version, :major_version, :key, :content, :presence => true
6
+
7
+ scope :accepted_by, lambda{|acceptable| joins(:contract_acceptances).where({ :contract_acceptances => { :acceptable_id => acceptable.id, :acceptable_type => acceptable.type } }) }
8
+ scope :for_key, lambda{|key| where("`key` = ?", key) }
9
+ scope :by_version, order("major_version DESC, minor_version DESC, maintenance_version DESC")
10
+
11
+ # Find the latest contract for a given key, by version number
12
+ def self.latest(key)
13
+ for_key(key).by_version.find(:first)
14
+ end
15
+
16
+ # Find the latest accepted contract for a given acceptable, by key
17
+ def self.latest_accepted(key, acceptable)
18
+ accepted_by(acceptable).for_key(key).by_version.find(:first)
19
+ end
20
+
21
+ def version=(version_string) # Expects "1.0.2"
22
+ versions = version_string.split(".")
23
+ raise "invalid version_string" unless versions.size == 3
24
+ self.major_version, self.minor_version, self.maintenance_version = versions
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ class ContractAcceptance < ActiveRecord::Base
2
+ belongs_to :acceptable, :polymorphic => true
3
+ belongs_to :contract
4
+
5
+ validates_presence_of :acceptable_id
6
+ validates_presence_of :acceptable_type
7
+ validates_presence_of :contract_id
8
+ end
@@ -0,0 +1,3 @@
1
+ module ContractAcceptanceFramework
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Contract Acceptance Framework' do
4
+ let(:person) { Person.create }
5
+ let(:contract) do
6
+ Contract.create(:major_version => 0, :minor_version => 0,
7
+ :maintenance_version => 0, :key => 'unique string',
8
+ :content => 'content')
9
+ end
10
+
11
+ describe '.agree_to!' do
12
+ it 'should agree to the contract' do
13
+ person.agree_to!(contract)
14
+ person.contracts.include?(contract).should be_true
15
+ end
16
+
17
+ context 'when the contract has been previously agreed to' do
18
+ before { person.agree_to!(contract) }
19
+
20
+ it 'should raise a contract duplicate agreement error' do
21
+ lambda { person.agree_to!(contract) }.should raise_error ContractDuplicateAgreementError
22
+ end
23
+ end
24
+ end
25
+
26
+ describe '.has_agreed_to?' do
27
+ subject { person.has_agreed_to?(contract) }
28
+
29
+ context 'when the contract has been agreed to' do
30
+ before { person.agree_to!(contract) }
31
+ it { should be_true }
32
+ end
33
+
34
+ context 'when the contract has not been agreed to' do
35
+ it { should be_false }
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,26 @@
1
+ require 'active_record'
2
+ require 'contract_acceptance_framework'
3
+
4
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
5
+
6
+ ActiveRecord::Base.connection.create_table(:contracts) do |t|
7
+ t.string :key
8
+ t.text :content
9
+ t.integer :major_version
10
+ t.integer :minor_version
11
+ t.integer :maintenance_version
12
+ t.timestamps
13
+ end
14
+
15
+ ActiveRecord::Base.connection.create_table(:contract_acceptances) do |t|
16
+ t.integer :contract_id
17
+ t.integer :acceptable_id
18
+ t.string :acceptable_type
19
+ end
20
+
21
+ ActiveRecord::Base.connection.create_table(:people)
22
+
23
+ class Person < ActiveRecord::Base
24
+ include ContractAcceptanceFramework
25
+ end
26
+
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: contract_acceptance_framework
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Josh Adams
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-02-11 00:00:00 -06:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: bundler
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 15424057
30
+ segments:
31
+ - 1
32
+ - 0
33
+ - 0
34
+ - rc
35
+ - 6
36
+ version: 1.0.0.rc.6
37
+ type: :development
38
+ version_requirements: *id001
39
+ - !ruby/object:Gem::Dependency
40
+ name: rspec
41
+ prerelease: false
42
+ requirement: &id002 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ hash: 31
48
+ segments:
49
+ - 2
50
+ - 4
51
+ - 0
52
+ version: 2.4.0
53
+ type: :development
54
+ version_requirements: *id002
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ prerelease: false
58
+ requirement: &id003 !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ~>
62
+ - !ruby/object:Gem::Version
63
+ hash: 29
64
+ segments:
65
+ - 1
66
+ - 3
67
+ - 3
68
+ version: 1.3.3
69
+ type: :development
70
+ version_requirements: *id003
71
+ - !ruby/object:Gem::Dependency
72
+ name: activesupport
73
+ prerelease: false
74
+ requirement: &id004 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ~>
78
+ - !ruby/object:Gem::Version
79
+ hash: 7
80
+ segments:
81
+ - 3
82
+ - 0
83
+ - 0
84
+ version: 3.0.0
85
+ type: :runtime
86
+ version_requirements: *id004
87
+ - !ruby/object:Gem::Dependency
88
+ name: activerecord
89
+ prerelease: false
90
+ requirement: &id005 !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ~>
94
+ - !ruby/object:Gem::Version
95
+ hash: 7
96
+ segments:
97
+ - 3
98
+ - 0
99
+ - 0
100
+ version: 3.0.0
101
+ type: :runtime
102
+ version_requirements: *id005
103
+ description:
104
+ email:
105
+ - josh@isotope11.com
106
+ executables: []
107
+
108
+ extensions: []
109
+
110
+ extra_rdoc_files: []
111
+
112
+ files:
113
+ - .gitignore
114
+ - Gemfile
115
+ - README
116
+ - Rakefile
117
+ - contract_acceptance_framework.gemspec
118
+ - lib/contract_acceptance_framework.rb
119
+ - lib/contract_acceptance_framework/contract.rb
120
+ - lib/contract_acceptance_framework/contract_acceptance.rb
121
+ - lib/contract_acceptance_framework/version.rb
122
+ - spec/contract_acceptance_framework_spec.rb
123
+ - spec/spec_helper.rb
124
+ has_rdoc: true
125
+ homepage: http://rubygems.org/gems/contract_acceptance_framework
126
+ licenses: []
127
+
128
+ post_install_message:
129
+ rdoc_options: []
130
+
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ none: false
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ hash: 3
139
+ segments:
140
+ - 0
141
+ version: "0"
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ none: false
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ hash: 23
148
+ segments:
149
+ - 1
150
+ - 3
151
+ - 6
152
+ version: 1.3.6
153
+ requirements: []
154
+
155
+ rubyforge_project: contract_acceptance_framework
156
+ rubygems_version: 1.4.2
157
+ signing_key:
158
+ specification_version: 3
159
+ summary: Allow rails objects to agree to versioned contracts
160
+ test_files: []
161
+