public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-commits] proj/council-webapp:master commit in: site/db/, site/features/support/, site/features/, site/config/, ...
@ 2011-05-24 16:58 Petteri Räty
  0 siblings, 0 replies; only message in thread
From: Petteri Räty @ 2011-05-24 16:58 UTC (permalink / raw
  To: gentoo-commits

commit:     a0925ae845600d7e07a672b2a1a62d749154fabc
Author:     Joachim Filip Ignacy Bartosik <jbartosik <AT> gmail <DOT> com>
AuthorDate: Fri May 13 16:50:23 2011 +0000
Commit:     Petteri Räty <betelgeuse <AT> gentoo <DOT> org>
CommitDate: Tue May 24 16:55:39 2011 +0000
URL:        http://git.overlays.gentoo.org/gitweb/?p=proj/council-webapp.git;a=commit;h=a0925ae8

Agenda lifecycle (semi-automatically managed)

When starting application create new (open) agenda if there are 0
non-archival agendas. When archiving agenda create a new open agenda.
Agenda is valid only if it's archival agenda or if there are no other
non-archival agendas.

---
 site/app/controllers/agendas_controller.rb     |    3 +
 site/app/models/agenda.rb                      |   52 ++++++++++++
 site/app/views/agendas/index.dryml             |    5 +
 site/app/views/agendas/show.dryml              |    9 ++
 site/config/hobo_routes.rb                     |    8 ++
 site/config/initializers/agenda.rb             |   12 +++
 site/db/schema.rb                              |    6 +-
 site/features/agendas.feature                  |   37 ++++++++-
 site/features/step_definitions/agenda_steps.rb |   26 ++++++-
 site/features/step_definitions/within_steps.rb |    3 +
 site/features/support/paths.rb                 |    2 +
 site/spec/factories.rb                         |    2 -
 site/spec/models/agenda_spec.rb                |  104 +++++++++++++++++++-----
 site/spec/spec_helper.rb                       |    1 +
 14 files changed, 241 insertions(+), 29 deletions(-)

diff --git a/site/app/controllers/agendas_controller.rb b/site/app/controllers/agendas_controller.rb
index 438fff7..bfc32a4 100644
--- a/site/app/controllers/agendas_controller.rb
+++ b/site/app/controllers/agendas_controller.rb
@@ -4,4 +4,7 @@ class AgendasController < ApplicationController
 
   auto_actions :all
 
+  def index
+    hobo_index Agenda.state_is(:old)
+  end
 end

diff --git a/site/app/models/agenda.rb b/site/app/models/agenda.rb
index a6fe828..def1643 100644
--- a/site/app/models/agenda.rb
+++ b/site/app/models/agenda.rb
@@ -7,6 +7,19 @@ class Agenda < ActiveRecord::Base
     timestamps
   end
 
+  lifecycle do
+    state :open, :default => true
+    state :submissions_closed, :meeting_ongoing, :old
+
+    transition :close, {:open => :submissions_closed}, :available_to => '::Agenda.transitions_available(acting_user)'
+    transition :reopen, {:submissions_closed=> :open}, :available_to => '::Agenda.transitions_available(acting_user)'
+    transition :archive, {:submissions_closed => :old}, :available_to =>  '::Agenda.transitions_available(acting_user)' do
+      Agenda.new.save!
+    end
+  end
+
+  validate  :there_is_only_one_non_archival_agenda
+
   # --- Permissions --- #
 
   def create_permitted?
@@ -25,4 +38,43 @@ class Agenda < ActiveRecord::Base
     true
   end
 
+  before_create do |a|
+    a.meeting_time ||= Time.now
+  end
+
+  def self.current
+    Agenda.state_is_not(:old).first
+  end
+
+  def self.transitions_available(user)
+    return user if user.council_member?
+    return user if user.administrator?
+    false
+  end
+
+  def possible_transitions
+    transitions = case state
+      when 'open'
+        ['close']
+      when 'submissions_closed'
+        ['reopen', 'archive']
+      else
+        []
+    end
+
+    transitions.collect do |transition|
+      ["#{transition.camelize} this agenda.", "agenda_#{transition}_path"]
+    end
+  end
+
+  protected
+    def there_is_only_one_non_archival_agenda
+      return if(state.to_s == 'old')
+      if id.nil?
+        return if Agenda.state_is_not(:old).count == 0
+      else
+        return if Agenda.state_is_not(:old).id_is_not(id).count == 0
+      end
+      errors.add(:state, 'There can be only one non-archival agenda at time.')
+    end
 end

diff --git a/site/app/views/agendas/index.dryml b/site/app/views/agendas/index.dryml
new file mode 100644
index 0000000..9eff469
--- /dev/null
+++ b/site/app/views/agendas/index.dryml
@@ -0,0 +1,5 @@
+<index-page>
+  <before-collection:>
+    Current agenda: <view with="&Agenda.current" class="current-agenda"/>
+  </before-collection:>
+</index-page>

diff --git a/site/app/views/agendas/show.dryml b/site/app/views/agendas/show.dryml
new file mode 100644
index 0000000..7df2b72
--- /dev/null
+++ b/site/app/views/agendas/show.dryml
@@ -0,0 +1,9 @@
+<show-page>
+  <append-content-body:>
+    <div class="transition" if="&Agenda.transitions_available(current_user)">
+      <collection:possible_transitions>
+        <a href="&send(this.second)"><view:first/></a>
+      </collection>
+    </div>
+  </append-content-body:>
+</show-page>

diff --git a/site/config/hobo_routes.rb b/site/config/hobo_routes.rb
index 7035490..33b292b 100644
--- a/site/config/hobo_routes.rb
+++ b/site/config/hobo_routes.rb
@@ -27,6 +27,14 @@ Council::Application.routes.draw do
   match 'forgot_password(.:format)' => 'users#forgot_password', :as => 'user_forgot_password'
 
 
+  # Lifecycle routes for controller "agendas"
+  put 'agendas/:id/close(.:format)' => 'agendas#do_close', :as => 'do_agenda_close'
+  get 'agendas/:id/close(.:format)' => 'agendas#close', :as => 'agenda_close'
+  put 'agendas/:id/reopen(.:format)' => 'agendas#do_reopen', :as => 'do_agenda_reopen'
+  get 'agendas/:id/reopen(.:format)' => 'agendas#reopen', :as => 'agenda_reopen'
+  put 'agendas/:id/archive(.:format)' => 'agendas#do_archive', :as => 'do_agenda_archive'
+  get 'agendas/:id/archive(.:format)' => 'agendas#archive', :as => 'agenda_archive'
+
   # Resource routes for controller "agendas"
   get 'agendas(.:format)' => 'agendas#index', :as => 'agendas'
   get 'agendas/new(.:format)', :as => 'new_agenda'

diff --git a/site/config/initializers/agenda.rb b/site/config/initializers/agenda.rb
new file mode 100644
index 0000000..dccc29f
--- /dev/null
+++ b/site/config/initializers/agenda.rb
@@ -0,0 +1,12 @@
+# If there are no active agendas create one
+begin
+  return unless ['development', 'production'].include? Rails.env
+  return unless Agenda.state_is_not(:old).count > 0
+  Agenda.create!
+rescue
+  # Just ignore it. It will happen when:
+  #  * Everything is fine, but database is missing (eg. rake db:schema:load)
+  #    * It's safe to ignore this then
+  #  * Something is seriously wrong (like broken db)
+  #    * Users will notice this anyway
+end

diff --git a/site/db/schema.rb b/site/db/schema.rb
index 812d821..b308e67 100644
--- a/site/db/schema.rb
+++ b/site/db/schema.rb
@@ -10,14 +10,18 @@
 #
 # It's strongly recommended to check this file into your version control system.
 
-ActiveRecord::Schema.define(:version => 20110520150527) do
+ActiveRecord::Schema.define(:version => 20110523175740) do
 
   create_table "agendas", :force => true do |t|
     t.datetime "meeting_time"
     t.datetime "created_at"
     t.datetime "updated_at"
+    t.string   "state",         :default => "open"
+    t.datetime "key_timestamp"
   end
 
+  add_index "agendas", ["state"], :name => "index_agendas_on_state"
+
   create_table "users", :force => true do |t|
     t.string   "salt",                      :limit => 40
     t.string   "remember_token"

diff --git a/site/features/agendas.feature b/site/features/agendas.feature
index 77267a5..582f904 100644
--- a/site/features/agendas.feature
+++ b/site/features/agendas.feature
@@ -3,13 +3,42 @@ Feature: Agendas
   I want to have agendas
 
   Scenario: View agendas listing as a guest
+    Given an agenda
+    Given an old agenda
     When I am on the homepage
     And I follow "Agendas"
-    Then I should not see "Agenda" in the content body
-
-    Given an agenda
-    When I follow "Agendas"
     Then I should see "Agenda" in the agendas collection
+    And I should see "Agenda" as current agenda
 
     When I follow link to first agenda
     Then I should see current date as meeting time
+
+  Scenario: Change current agenda state as a council member
+    Given an agenda
+    When I am logged in as a council member
+    And I follow "Agendas"
+    And I follow link to current agenda
+    Then I should see "open" as agenda state
+    And I should see "Close this agenda" as transition
+
+    When I close current agenda
+    Then I should see "submissions_closed" as agenda state
+    And I should see "Reopen this agenda" as transition
+    And I should see "Archive this agenda" as transition
+
+    When I reopen current agenda
+    Then I should see "open" as agenda state
+
+    When I close current agenda
+    When I archive current agenda
+    Then I should see "old" as agenda state
+
+  Scenario: Change current agenda state as a council member
+    Given an closed agenda
+    When I am logged in as a council member
+    And I am on the current agenda page
+    And I archive current agenda
+
+    When I follow "Agendas"
+    And I follow link to current agenda
+    Then I should see "open" as agenda state

diff --git a/site/features/step_definitions/agenda_steps.rb b/site/features/step_definitions/agenda_steps.rb
index 95c7406..8613fb1 100644
--- a/site/features/step_definitions/agenda_steps.rb
+++ b/site/features/step_definitions/agenda_steps.rb
@@ -1,5 +1,8 @@
-Given /^an agenda$/ do
-  Agenda.new(:meeting_time => Time.now).save!
+Given /^an ?(\w*) agenda$/ do |state|
+  a = Agenda.new
+  state = 'submissions_closed' if state == 'closed'
+  a.state = state unless state.empty?
+  a.save! if a.valid?
 end
 
 Then /^I should see current date as meeting time$/ do
@@ -10,3 +13,22 @@ When /^I follow link to first agenda$/ do
   link_text = page.find(:xpath, "//a[contains(@class, 'agenda-link')]").text
   When "I follow \"#{link_text}\""
 end
+
+When /^I follow link to current agenda$/ do
+  a = Agenda.current
+  When "I follow \"Agenda #{a.id}\""
+end
+
+When /^I am logged in as a council member$/ do
+  Given 'example user'
+  user = User.last
+  user.council_member = true
+  user.save
+  When 'I am on the login page'
+  When 'I login as example user'
+end
+
+When /^I (\w+) current agenda$/ do |action|
+    When "I follow \"#{action.camelize} this agenda\""
+    When "I press \"#{action.camelize}\""
+end

diff --git a/site/features/step_definitions/within_steps.rb b/site/features/step_definitions/within_steps.rb
index 3f42f37..80bb8f0 100644
--- a/site/features/step_definitions/within_steps.rb
+++ b/site/features/step_definitions/within_steps.rb
@@ -1,4 +1,7 @@
 {
+  'as current agenda' => '.current-agenda',
+  'as agenda state' => '.state-tag.view.agenda-state',
+  'as transition' => '.transition',
   'in the notices' => '.flash.notice',
   'in the errors' => '.error-messages',
   'in the content body' => '.content-body',

diff --git a/site/features/support/paths.rb b/site/features/support/paths.rb
index 90b611b..515a7ec 100644
--- a/site/features/support/paths.rb
+++ b/site/features/support/paths.rb
@@ -17,6 +17,8 @@ module NavigationHelpers
     when /the signup page/
       user_signup_path
 
+    when /the current agenda page/
+      agenda_path(Agenda.current)
     # Add more mappings here.
     # Here is an example that pulls values out of the Regexp:
     #

diff --git a/site/spec/factories.rb b/site/spec/factories.rb
index a7256c0..b41c25c 100644
--- a/site/spec/factories.rb
+++ b/site/spec/factories.rb
@@ -8,6 +8,4 @@ Factory.define :user, :class => User do |u|
   u.email { |u| "#{u.name}@example.com" }
 end
 
-Factory.define :guest do |g|; end
-
 Factory.define :agenda do |a|; end

diff --git a/site/spec/models/agenda_spec.rb b/site/spec/models/agenda_spec.rb
index 9fd3c68..0a69551 100644
--- a/site/spec/models/agenda_spec.rb
+++ b/site/spec/models/agenda_spec.rb
@@ -1,32 +1,96 @@
 require 'spec_helper'
 
 describe Agenda do
-  it "shouldn't allow anyone to create and destroy" do
-    agendas = [Agenda.new, Factory(:agenda)]
-    for a in agendas
-      for u in users_factory(AllRoles)
-        a.should_not be_creatable_by(u)
-        a.should_not be_destroyable_by(u)
-      end
+  it 'shouldn not allow anyone to create' do
+    a = Agenda.new
+    for u in users_factory(AllRoles)
+      a.should_not be_creatable_by(u)
+      a.should_not be_destroyable_by(u)
     end
   end
 
-  it "shouldn allow everybody to view" do
-    agendas = [Agenda.new, Factory(:agenda)]
-    for a in agendas
-      for u in users_factory(AllRoles)
-        a.should be_viewable_by(u)
-      end
+  it 'shouldn not allow anyone to destory' do
+    a = Factory(:agenda)
+    for u in users_factory(AllRoles)
+      a.should_not be_destroyable_by(u)
     end
   end
 
-  it "should allow only administrators and council members to edit and update" do
-    agendas = [Agenda.new, Factory(:agenda)]
-    for a in agendas
-      for u in users_factory(AllRoles)
-        a.should_not be_editable_by(u)
-        a.should_not be_updatable_by(u)
-      end
+  it 'should allow everybody to view' do
+    a = Factory(:agenda)
+    for u in users_factory(AllRoles)
+      a.should be_viewable_by(u)
     end
   end
+
+  it 'should allow only administrators and council members to edit and update' do
+    a = Factory(:agenda)
+    for u in users_factory(:guest, :user)
+      a.should_not be_editable_by(u)
+      a.should_not be_updatable_by(u)
+    end
+  end
+
+  def test_migration(object, migration, prohibited, allowed, final_state)
+    # object - object to migrate
+    # migration - migration name
+    # prohibited - array of users who can not perform migration
+    # allowed - *one* user who can perform migration
+    # final_state - state of object after migration
+    for user in prohibited
+      lambda { object.lifecycle.send("#{migration}!", user) }.should raise_error
+    end
+
+    object.lifecycle.send("#{migration}!", allowed)
+    object.state.should == final_state
+  end
+
+  it 'should have working transitions, available only to council members and administrators' do
+    Factory(:agenda)
+    prohibited = users_factory(:guest, :user)
+    allowed = users_factory(:council, :admin, :council_admin)
+
+    for user in allowed
+      agenda = Agenda.last
+      test_migration(agenda, :close, prohibited, user, 'submissions_closed')
+      test_migration(agenda, :reopen, prohibited, user, 'open')
+      test_migration(agenda, :close, prohibited, user, 'submissions_closed')
+      test_migration(agenda, :archive, prohibited, user, 'old')
+    end
+
+  end
+
+  it 'that is non-archival should not be valid if there other open agenda' do
+    a = Factory(:agenda)
+    Agenda.new.should_not be_valid
+    Agenda.new(:state => 'submissions_closed').should_not be_valid
+  end
+
+  it 'that is non-archival should not be valid if there other closed agenda' do
+    a = Factory(:agenda, :state => 'submissions_closed')
+    Agenda.new.should_not be_valid
+    Agenda.new(:state => 'submissions_closed').should_not be_valid
+  end
+
+  it 'that is archival should be valid' do
+    a = Factory(:agenda, :state => 'old')
+    a.should be_valid
+  end
+
+  it 'should create new open agenda, when current agenda is archived' do
+    a = Factory(:agenda)
+    u = users_factory(:admin)
+    a.lifecycle.close! u
+    a.lifecycle.archive! u
+    Agenda.last.state.should == 'open'
+  end
+
+  it 'should set meeting time to now by default' do
+    a1 = Factory(:agenda)
+    a2 = Factory(:agenda, :meeting_time => 2.days.ago, :state => 'old')
+    today = Time.now.strftime('%Y-%m-%d')
+
+    a1.meeting_time.strftime('%Y-%m-%d').should == today
+    a2.meeting_time.strftime('%Y-%m-%d').should_not == today
+  end
 end

diff --git a/site/spec/spec_helper.rb b/site/spec/spec_helper.rb
index 53f0ed3..a8b8aea 100644
--- a/site/spec/spec_helper.rb
+++ b/site/spec/spec_helper.rb
@@ -13,4 +13,5 @@ Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
 
 RSpec.configure do |config|
   config.mock_with :rspec
+  config.use_transactional_fixtures = true
 end



^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2011-05-24 16:58 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-05-24 16:58 [gentoo-commits] proj/council-webapp:master commit in: site/db/, site/features/support/, site/features/, site/config/, Petteri Räty

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox