Skip to content

Commit

Permalink
Use state machine to manage Organisation lifecycle.
Browse files Browse the repository at this point in the history
  • Loading branch information
chrismear committed May 23, 2011
1 parent 033b825 commit 77466d6
Show file tree
Hide file tree
Showing 20 changed files with 132 additions and 90 deletions.
2 changes: 1 addition & 1 deletion app/controllers/found_organisation_proposals_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def create
found_organisation_proposal = co.found_organisation_proposals.build(found_organisation_proposal_parameters)

if found_organisation_proposal.save
co.proposed!
co.propose!
redirect_to(root_path, :notice => "The founding vote has now begun.")
else
# TODO Render instead of redirect; use error_messages_for.
Expand Down
1 change: 0 additions & 1 deletion app/controllers/organisations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ def create
if !@founder.save
errors << "Cannot create your account: #{@founder.errors.full_messages.to_sentence}."
end
@organisation.pending!
end

# display errors
Expand Down
4 changes: 2 additions & 2 deletions app/models/found_organisation_proposal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def send_email
end

def after_reject(params)
organisation.pending! # Switching back to 'pending' org state.
organisation.fail! # Switching back to 'pending' org state.
# The existence of a failed 'Found Organisation' proposal is the only record we keep of this.
end

Expand All @@ -47,7 +47,7 @@ def enact!
end
end

organisation.active!
organisation.found!
organisation.save
end

Expand Down
51 changes: 18 additions & 33 deletions app/models/organisation.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
class Organisation < ActiveRecord::Base
state_machine :initial => :pending do
event :propose do
transition :pending => :proposed
end

event :found do
transition :proposed => :active
end

event :fail do
transition :proposed => :pending
end

after_transition :proposed => :active, :do => :destroy_pending_state_member_classes
end

has_many :clauses

has_many :members
Expand Down Expand Up @@ -86,39 +102,8 @@ def convener
members.first
end

def under_construction?
clauses.get_text('organisation_state').nil?
end

def under_construction!
clause = clauses.get_current('organisation_state')
clause && clause.destroy
end

def pending?
clauses.get_text('organisation_state') == 'pending'
end

def pending!
clauses.set_text!('organisation_state', 'pending')
end

def proposed?
clauses.get_text('organisation_state') == 'proposed'
end

def proposed!
clauses.set_text!('organisation_state', 'proposed')
end

def active?
clauses.get_text('organisation_state') == 'active'
end

def active!
clauses.set_text!('organisation_state', 'active')

# Delete Founder and Founding Member member classes
# Delete Founder and Founding Member member classes
def destroy_pending_state_member_classes
member_classes.find_by_name('Founder').destroy
member_classes.find_by_name('Founding Member').destroy
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
class ConvertOrganisationsToStateMachine < ActiveRecord::Migration
class Organisation < ActiveRecord::Base;end
class Clause < ActiveRecord::Base; end

def self.up
add_column :organisations, :state, :string

Organisation.all.each do |organisation|
state_clause = Clause.where(
:organisation_id => organisation.id,
:name => 'organisation_state',
:ended_at => nil
).order('started_at DESC').first

if state_clause
organisation.state = state_clause.text_value
else
organisation.state = 'pending'
end

organisation.save!
end
end

def self.down
Organisation.all.each do |organisation|
state_clause = Clause.where(
:organisation_id => organisation.id,
:name => 'organisation_state',
:ended_at => nil
).order('started_at DESC').first

if state_clause
if state_clause.text_value != organisation.state
state_clause.update_attribute(:ended_at, Time.now.utc)
Clause.create!(
:organisation_id => organisation.id,
:name => 'organisation_state',
:started_at => Time.now.utc,
:text_value => organisation.state
)
end
else
Clause.create!(
:organisation_id => organisation.id,
:name => 'organisation_state',
:started_at => Time.now.utc,
:text_value => organisation.state
)
end
end

remove_column :organisations, :state
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended to check this file into your version control system.

ActiveRecord::Schema.define(:version => 20110519150109) do
ActiveRecord::Schema.define(:version => 20110521215633) do

create_table "clauses", :force => true do |t|
t.string "name", :limit => 50, :null => false
Expand Down Expand Up @@ -84,6 +84,7 @@
t.string "subdomain"
t.datetime "created_at"
t.datetime "updated_at"
t.string "state"
end

create_table "proposals", :force => true do |t|
Expand Down
8 changes: 3 additions & 5 deletions features/step_definitions/organisation_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
end

Given /^an organisation has been created$/ do
@organisation = Organisation.make
@organisation.pending!
@organisation = Organisation.make(:state => 'pending')
@founder = @organisation.members.make(
:member_class => @organisation.member_classes.find_by_name("Founder"),
:inducted_at => nil
Expand All @@ -34,8 +33,7 @@
end

Given /^an organisation is active$/ do
@organisation = Organisation.make
@organisation.active!
@organisation = Organisation.make(:state => 'active')
3.times do
@organisation.members.make(
:member_class => @organisation.member_classes.find_by_name("Member")
Expand All @@ -49,7 +47,7 @@
end

Then /^the organisation should be active$/ do
@organisation.should be_active
@organisation.reload.should be_active
end

Then /^I should see a list of recent activity$/ do
Expand Down
4 changes: 2 additions & 2 deletions features/step_definitions/proposal_steps.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
Given /^I have started the founding vote$/ do
@organisation.found_organisation_proposals.make(:proposer => @user)
@organisation.proposed!
@organisation.propose!
end

Given /^the founding vote has been started$/ do
founder = @organisation.member_classes.where(:name => "Founder").first.members.first
@organisation.found_organisation_proposals.make(:proposer => founder)
@organisation.proposed!
@organisation.propose!
end

Given /^everyone has voted to support the founding$/ do
Expand Down
7 changes: 6 additions & 1 deletion spec/models/found_organisation_proposal_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
describe "enactment" do
before(:each) do
# Set up mock organisation and member classes
@organisation = mock_model(Organisation, :active! => nil, :save => false)
@organisation = mock_model(Organisation, :found! => nil, :save => false)

@founder_class = mock_model(MemberClass, :name => 'Founder', :description => nil)
@founding_member_class = mock_model(MemberClass, :name => 'Founding Member', :description => nil)
Expand Down Expand Up @@ -75,6 +75,11 @@

@proposal.enact!
end

it "founds the organisation" do
@organisation.should_receive(:found!)
@proposal.enact!
end
end

describe "starting" do
Expand Down
14 changes: 7 additions & 7 deletions spec/models/organisation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@
before(:each) do
@organisation.members.destroy_all
@organisation.members.make_n(3)
@organisation.pending!
organisation_is_pending
end

it "returns false unless the organisation is pending" do
@organisation.can_hold_founding_vote?.should be_true
@organisation.proposed!
@organisation.propose!
@organisation.can_hold_founding_vote?.should be_false
end

Expand All @@ -93,13 +93,11 @@

describe "state changes" do
before(:each) do
@organisation = Organisation.new

@clauses_association = mock("clauses association", :set_text! => nil)
@organisation.stub!(:clauses).and_return(@clauses_association)
end

describe "#active!" do
describe "#found!" do
before(:each) do
@founder_class = mock_model(MemberClass, :name => 'Founder', :description => nil)
@founding_member_class = mock_model(MemberClass, :name => 'Founding Member', :description => nil)
Expand All @@ -109,16 +107,18 @@
@member_classes_association.stub!(:find_by_name).with('Founding Member').and_return(@founding_member_class)

@organisation.stub!(:member_classes).and_return(@member_classes_association)

organisation_is_proposed
end

it "destroys the 'Founder' member class" do
@founder_class.should_receive(:destroy)
@organisation.active!
@organisation.found!
end

it "destroys the 'Founding Member' member class" do
@founding_member_class.should_receive(:destroy)
@organisation.active!
@organisation.found!
end

end
Expand Down
4 changes: 3 additions & 1 deletion spec/models/proposal_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@

describe "closing" do
before(:each) do
@organisation.members.count.should >0
@organisation.members.count.should > 0

@p = @organisation.proposals.make(:proposer => @member)
@p.stub!(:passed?).and_return(true)
Expand All @@ -63,6 +63,8 @@
@p = FoundOrganisationProposal.make(:proposer => @member, :organisation => @organisation)
@p.stub!(:passed?).and_return(true)
@p.stub!(:create_decision).and_return(@decision = mock_model(Decision, :send_email => nil))

organisation_is_proposed
end

it "creates a decision" do
Expand Down
1 change: 0 additions & 1 deletion spec/requests/constitution_proposal_bundles_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

before(:each) do
default_organisation
@organisation.active!
default_constitution

@user = login
Expand Down
2 changes: 1 addition & 1 deletion spec/requests/constitutions_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@

context "when organisation is pending" do
before(:each) do
@organisation.pending!
organisation_is_pending
set_permission!(@user, :founder, true)
get '/constitution/edit'
end
Expand Down
4 changes: 1 addition & 3 deletions spec/requests/found_organisation_proposals_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
describe "found organisation proposals" do

before(:each) do
default_organisation
default_organisation(:state => 'pending')
default_constitution

@user = login
Expand All @@ -10,8 +10,6 @@

# Need at least three members to propose foundation
@organisation.members.make_n(2)

@organisation.pending!
end

describe "proposing the founding of the organisation" do
Expand Down
3 changes: 1 addition & 2 deletions spec/requests/founding_members_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
describe "founding members" do

before(:each) do
default_organisation(:active => false)
@organisation.pending!
default_organisation(:state => 'pending')
set_permission!(default_user, :founder, true)
set_permission!(default_user, :membership_proposal, true)
login
Expand Down
7 changes: 3 additions & 4 deletions spec/requests/founding_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
describe "Founding process" do
describe "Found Organisation Proposal decision email" do
before(:each) do
@organisation = default_organisation(:active => false)
@organisation.pending!
@organisation = default_organisation(:state => 'pending')
@founding_member_member_class = @organisation.member_classes.find_by_name("Founding Member")
@founder_member_class = @organisation.member_classes.find_by_name("Founder")
@founder = @organisation.members.make(:member_class => @founder_member_class)
Expand All @@ -24,8 +23,8 @@
)
@found_organisation_proposal.save

@organisation.proposed!
@organisation.save
@organisation.propose!
@found_organisation_proposal.reload

@affirmative_members.each do |m|
m.cast_vote(:for, @found_organisation_proposal)
Expand Down
4 changes: 2 additions & 2 deletions spec/requests/invitations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

describe "invitations" do
before(:each) do
default_organisation(:active => false)
@member = @organisation.members.make(:active => false, :inducted_at => nil, :password => nil, :password_confirmation => nil)
default_organisation(:state => 'pending')
@member = @organisation.members.make(:inducted_at => nil, :password => nil, :password_confirmation => nil)
end

describe "GET edit" do
Expand Down
Loading

0 comments on commit 77466d6

Please sign in to comment.