diff options
author | Eric Davis <edavis@littlestreamsoftware.com> | 2009-05-30 23:30:36 +0000 |
---|---|---|
committer | Eric Davis <edavis@littlestreamsoftware.com> | 2009-05-30 23:30:36 +0000 |
commit | 62e58f26b0c6905fc0b2d79ed278ac0e4f55d793 (patch) | |
tree | 6989cbc655e9ffee0db9b7558985cd77d28b0edb | |
parent | fbfb34949677c41734531a552547fb216537999f (diff) | |
download | redmine-62e58f26b0c6905fc0b2d79ed278ac0e4f55d793.tar.gz redmine-62e58f26b0c6905fc0b2d79ed278ac0e4f55d793.zip |
Changed Enumerations to use a Single Table Inheritance
* Added migrations to change Enumerations to an STI relationship
* Added TimeEntryActivity model (STI)
* Added DocumentCategory model (STI)
* Added IssuePriority model (STI)
* Added Enumeration#get_subclasses to get a list of the subclasses of Enumeration
* Changed Enumeration to use the STI type field instead of the opt field
* Changed Enumeration#opt to return the old opt values but with a deprecation warning.
* Removed Enumeration::OPTIONS
* Removed the dynamic named_scopes in favor of specific named_scopes. Kept for
compatibility reasons.
* Added Enumeration#default so each subclass can easily find it's default record.
* Fixed Enumeration#default to use the STI scoping with a fake default scope for finding Enumeration's default.
* Added a 'all' named scope for getting all records in order by position.
* Added Deprecation warnings to the old named_scopes in Enumerations.
* Moved various methods off of Enumeration and onto the concrete classes
* Changed the EnumerationsController to use types
* Updated the Enumeration list template
* Added has_many relationships to the Enumeration STI classes.
* Fixes for tests.
#3007
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2777 e93f8b46-1217-0410-a6f0-8f06a7374b81
32 files changed, 391 insertions, 86 deletions
diff --git a/app/controllers/documents_controller.rb b/app/controllers/documents_controller.rb index f5825e7c3..c2e08768f 100644 --- a/app/controllers/documents_controller.rb +++ b/app/controllers/documents_controller.rb @@ -53,7 +53,7 @@ class DocumentsController < ApplicationController end def edit - @categories = Enumeration.document_categories + @categories = DocumentCategory.all if request.post? and @document.update_attributes(params[:document]) flash[:notice] = l(:notice_successful_update) redirect_to :action => 'show', :id => @document diff --git a/app/controllers/enumerations_controller.rb b/app/controllers/enumerations_controller.rb index ac55c421e..63965fae1 100644 --- a/app/controllers/enumerations_controller.rb +++ b/app/controllers/enumerations_controller.rb @@ -31,14 +31,19 @@ class EnumerationsController < ApplicationController end def new - @enumeration = Enumeration.new(:opt => params[:opt]) + begin + @enumeration = params[:type].constantize.new + rescue NameError + @enumeration = Enumeration.new + end end def create @enumeration = Enumeration.new(params[:enumeration]) + @enumeration.type = params[:enumeration][:type] if @enumeration.save flash[:notice] = l(:notice_successful_create) - redirect_to :action => 'list', :opt => @enumeration.opt + redirect_to :action => 'list', :type => @enumeration.type else render :action => 'new' end @@ -50,9 +55,10 @@ class EnumerationsController < ApplicationController def update @enumeration = Enumeration.find(params[:id]) + @enumeration.type = params[:enumeration][:type] if params[:enumeration][:type] if @enumeration.update_attributes(params[:enumeration]) flash[:notice] = l(:notice_successful_update) - redirect_to :action => 'list', :opt => @enumeration.opt + redirect_to :action => 'list', :type => @enumeration.type else render :action => 'edit' end @@ -65,12 +71,12 @@ class EnumerationsController < ApplicationController @enumeration.destroy redirect_to :action => 'index' elsif params[:reassign_to_id] - if reassign_to = Enumeration.find_by_opt_and_id(@enumeration.opt, params[:reassign_to_id]) + if reassign_to = Enumeration.find_by_type_and_id(@enumeration.type, params[:reassign_to_id]) @enumeration.destroy(reassign_to) redirect_to :action => 'index' end end - @enumerations = Enumeration.values(@enumeration.opt) - [@enumeration] + @enumerations = Enumeration.find(:all, :conditions => ['type = (?)', @enumeration.type]) - [@enumeration] #rescue # flash[:error] = 'Unable to delete enumeration' # redirect_to :action => 'index' diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 7fcf56e84..de5167651 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -113,7 +113,7 @@ class IssuesController < ApplicationController @changesets.reverse! if User.current.wants_comments_in_reverse_order? @allowed_statuses = @issue.new_statuses_allowed_to(User.current) @edit_allowed = User.current.allowed_to?(:edit_issues, @project) - @priorities = Enumeration.priorities + @priorities = IssuePriority.all @time_entry = TimeEntry.new respond_to do |format| format.html { render :template => 'issues/show.rhtml' } @@ -163,7 +163,7 @@ class IssuesController < ApplicationController return end end - @priorities = Enumeration.priorities + @priorities = IssuePriority.all render :layout => !request.xhr? end @@ -173,7 +173,7 @@ class IssuesController < ApplicationController def edit @allowed_statuses = @issue.new_statuses_allowed_to(User.current) - @priorities = Enumeration.priorities + @priorities = IssuePriority.all @edit_allowed = User.current.allowed_to?(:edit_issues, @project) @time_entry = TimeEntry.new @@ -237,7 +237,7 @@ class IssuesController < ApplicationController def bulk_edit if request.post? status = params[:status_id].blank? ? nil : IssueStatus.find_by_id(params[:status_id]) - priority = params[:priority_id].blank? ? nil : Enumeration.find_by_id(params[:priority_id]) + priority = params[:priority_id].blank? ? nil : IssuePriority.find_by_id(params[:priority_id]) assigned_to = (params[:assigned_to_id].blank? || params[:assigned_to_id] == 'none') ? nil : User.find_by_id(params[:assigned_to_id]) category = (params[:category_id].blank? || params[:category_id] == 'none') ? nil : @project.issue_categories.find_by_id(params[:category_id]) fixed_version = (params[:fixed_version_id].blank? || params[:fixed_version_id] == 'none') ? nil : @project.versions.find_by_id(params[:fixed_version_id]) @@ -415,7 +415,7 @@ class IssuesController < ApplicationController @assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to) end - @priorities = Enumeration.priorities.reverse + @priorities = IssuePriority.all.reverse @statuses = IssueStatus.find(:all, :order => 'position') @back = request.env['HTTP_REFERER'] diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb index 92bf530f0..d192a1955 100644 --- a/app/controllers/reports_controller.rb +++ b/app/controllers/reports_controller.rb @@ -37,7 +37,7 @@ class ReportsController < ApplicationController render :template => "reports/issue_report_details" when "priority" @field = "priority_id" - @rows = Enumeration.priorities + @rows = IssuePriority.all @data = issues_by_priority @report_title = l(:field_priority) render :template => "reports/issue_report_details" @@ -68,7 +68,7 @@ class ReportsController < ApplicationController else @trackers = @project.trackers @versions = @project.versions.sort - @priorities = Enumeration.priorities + @priorities = IssuePriority.all @categories = @project.issue_categories @assignees = @project.members.collect { |m| m.user } @authors = @project.members.collect { |m| m.user } @@ -130,7 +130,7 @@ private p.id as priority_id, count(i.id) as total from - #{Issue.table_name} i, #{IssueStatus.table_name} s, #{Enumeration.table_name} p + #{Issue.table_name} i, #{IssueStatus.table_name} s, #{IssuePriority.table_name} p where i.status_id=s.id and i.priority_id=p.id diff --git a/app/controllers/timelog_controller.rb b/app/controllers/timelog_controller.rb index 60cc3916b..29ba49938 100644 --- a/app/controllers/timelog_controller.rb +++ b/app/controllers/timelog_controller.rb @@ -46,7 +46,7 @@ class TimelogController < ApplicationController :klass => Tracker, :label => :label_tracker}, 'activity' => {:sql => "#{TimeEntry.table_name}.activity_id", - :klass => Enumeration, + :klass => TimeEntryActivity, :label => :label_activity}, 'issue' => {:sql => "#{TimeEntry.table_name}.issue_id", :klass => Issue, diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index a85a83a23..73ac4be95 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -68,8 +68,8 @@ module IssuesHelper u = User.find_by_id(detail.value) and value = u.name if detail.value u = User.find_by_id(detail.old_value) and old_value = u.name if detail.old_value when 'priority_id' - e = Enumeration.find_by_id(detail.value) and value = e.name if detail.value - e = Enumeration.find_by_id(detail.old_value) and old_value = e.name if detail.old_value + e = IssuePriority.find_by_id(detail.value) and value = e.name if detail.value + e = IssuePriority.find_by_id(detail.old_value) and old_value = e.name if detail.old_value when 'category_id' c = IssueCategory.find_by_id(detail.value) and value = c.name if detail.value c = IssueCategory.find_by_id(detail.old_value) and old_value = c.name if detail.old_value diff --git a/app/helpers/timelog_helper.rb b/app/helpers/timelog_helper.rb index d34694f1b..861c2acc7 100644 --- a/app/helpers/timelog_helper.rb +++ b/app/helpers/timelog_helper.rb @@ -27,7 +27,7 @@ module TimelogHelper end def activity_collection_for_select_options - activities = Enumeration.activities + activities = TimeEntryActivity.all collection = [] collection << [ "--- #{l(:actionview_instancetag_blank_option)} ---", '' ] unless activities.detect(&:is_default) activities.each { |a| collection << [a.name, a.id] } diff --git a/app/models/document.rb b/app/models/document.rb index f78c15e98..a96c278d0 100644 --- a/app/models/document.rb +++ b/app/models/document.rb @@ -17,7 +17,7 @@ class Document < ActiveRecord::Base belongs_to :project - belongs_to :category, :class_name => "Enumeration", :foreign_key => "category_id" + belongs_to :category, :class_name => "DocumentCategory", :foreign_key => "category_id" acts_as_attachable :delete_permission => :manage_documents acts_as_searchable :columns => ['title', "#{table_name}.description"], :include => :project @@ -31,7 +31,7 @@ class Document < ActiveRecord::Base def after_initialize if new_record? - self.category ||= Enumeration.document_categories.default + self.category ||= DocumentCategory.default end end end diff --git a/app/models/document_category.rb b/app/models/document_category.rb new file mode 100644 index 000000000..e04db7d16 --- /dev/null +++ b/app/models/document_category.rb @@ -0,0 +1,36 @@ +# redMine - project management software +# Copyright (C) 2006 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +class DocumentCategory < Enumeration + has_many :documents, :foreign_key => 'category_id' + + OptionName = :enumeration_doc_categories + # Backwards compatiblity. Can be removed post-0.9 + OptName = 'DCAT' + + def option_name + OptionName + end + + def objects_count + documents.count + end + + def transfer_relations(to) + documents.update_all("category_id = #{to.id}") + end +end diff --git a/app/models/enumeration.rb b/app/models/enumeration.rb index d466940b7..380a4d3d2 100644 --- a/app/models/enumeration.rb +++ b/app/models/enumeration.rb @@ -16,53 +16,77 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class Enumeration < ActiveRecord::Base - acts_as_list :scope => 'opt = \'#{opt}\'' + acts_as_list :scope => 'type = \'#{type}\'' before_destroy :check_integrity - validates_presence_of :opt, :name - validates_uniqueness_of :name, :scope => [:opt] + validates_presence_of :name + validates_uniqueness_of :name, :scope => [:type] validates_length_of :name, :maximum => 30 - - # Single table inheritance would be an option - OPTIONS = { - "IPRI" => {:label => :enumeration_issue_priorities, :model => Issue, :foreign_key => :priority_id, :scope => :priorities}, - "DCAT" => {:label => :enumeration_doc_categories, :model => Document, :foreign_key => :category_id, :scope => :document_categories}, - "ACTI" => {:label => :enumeration_activities, :model => TimeEntry, :foreign_key => :activity_id, :scope => :activities} - }.freeze - # Creates a named scope for each type of value. The scope has a +default+ method - # that returns the default value, or nil if no value is set as default. - # Example: - # Enumeration.priorities - # Enumeration.priorities.default - OPTIONS.each do |k, v| - next unless v[:scope] - named_scope v[:scope], :conditions => { :opt => k }, :order => 'position' do - def default - find(:first, :conditions => { :is_default => true }) - end + # Backwards compatiblity named_scopes. + # Can be removed post-0.9 + named_scope :priorities, :conditions => { :type => "IssuePriority" }, :order => 'position' do + ActiveSupport::Deprecation.warn("Enumeration#priorities is deprecated, use the IssuePriority class. (#{Redmine::Info.issue(3007)})") + def default + find(:first, :conditions => { :is_default => true }) + end + end + + named_scope :document_categories, :conditions => { :type => "DocumentCategory" }, :order => 'position' do + ActiveSupport::Deprecation.warn("Enumeration#document_categories is deprecated, use the DocumentCategories class. (#{Redmine::Info.issue(3007)})") + def default + find(:first, :conditions => { :is_default => true }) + end + end + + named_scope :activities, :conditions => { :type => "TimeEntryActivity" }, :order => 'position' do + ActiveSupport::Deprecation.warn("Enumeration#activities is deprecated, use the TimeEntryActivity class. (#{Redmine::Info.issue(3007)})") + def default + find(:first, :conditions => { :is_default => true }) end end - named_scope :values, lambda {|opt| { :conditions => { :opt => opt }, :order => 'position' } } do + named_scope :values, lambda {|type| { :conditions => { :type => type }, :order => 'position' } } do def default find(:first, :conditions => { :is_default => true }) end end + named_scope :all, :order => 'position' + + def self.default + # Creates a fake default scope so Enumeration.default will check + # it's type. STI subclasses will automatically add their own + # types to the finder. + if self.descends_from_active_record? + find(:first, :conditions => { :is_default => true, :type => 'Enumeration' }) + else + # STI classes are + find(:first, :conditions => { :is_default => true }) + end + end + + # Overloaded on concrete classes def option_name - OPTIONS[self.opt][:label] + nil + end + + # Backwards compatiblity. Can be removed post-0.9 + def opt + ActiveSupport::Deprecation.warn("Enumeration#opt is deprecated, use the STI classes now. (#{Redmine::Info.issue(3007)})") + return OptName end def before_save if is_default? && is_default_changed? - Enumeration.update_all("is_default = #{connection.quoted_false}", {:opt => opt}) + Enumeration.update_all("is_default = #{connection.quoted_false}", {:type => type}) end end + # Overloaded on concrete classes def objects_count - OPTIONS[self.opt][:model].count(:conditions => "#{OPTIONS[self.opt][:foreign_key]} = #{id}") + 0 end def in_use? @@ -75,7 +99,7 @@ class Enumeration < ActiveRecord::Base # If a enumeration is specified, objects are reassigned def destroy(reassign_to = nil) if reassign_to && reassign_to.is_a?(Enumeration) - OPTIONS[self.opt][:model].update_all("#{OPTIONS[self.opt][:foreign_key]} = #{reassign_to.id}", "#{OPTIONS[self.opt][:foreign_key]} = #{id}") + self.transfer_relations(reassign_to) end destroy_without_reassign end @@ -85,9 +109,23 @@ class Enumeration < ActiveRecord::Base end def to_s; name end + + # Returns the Subclasses of Enumeration. Each Subclass needs to be + # required in development mode. + # + # Note: subclasses is protected in ActiveRecord + def self.get_subclasses + @@subclasses[Enumeration] + end private def check_integrity raise "Can't delete enumeration" if self.in_use? end + end + +# Force load the subclasses in development mode +require_dependency 'time_entry_activity' +require_dependency 'document_category' +require_dependency 'issue_priority' diff --git a/app/models/issue.rb b/app/models/issue.rb index da49577e5..861d898e0 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -22,7 +22,7 @@ class Issue < ActiveRecord::Base belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' belongs_to :assigned_to, :class_name => 'User', :foreign_key => 'assigned_to_id' belongs_to :fixed_version, :class_name => 'Version', :foreign_key => 'fixed_version_id' - belongs_to :priority, :class_name => 'Enumeration', :foreign_key => 'priority_id' + belongs_to :priority, :class_name => 'IssuePriority', :foreign_key => 'priority_id' belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id' has_many :journals, :as => :journalized, :dependent => :destroy @@ -67,7 +67,7 @@ class Issue < ActiveRecord::Base if new_record? # set default values for new records only self.status ||= IssueStatus.default - self.priority ||= Enumeration.priorities.default + self.priority ||= IssuePriority.default end end diff --git a/app/models/issue_priority.rb b/app/models/issue_priority.rb new file mode 100644 index 000000000..cea0f0635 --- /dev/null +++ b/app/models/issue_priority.rb @@ -0,0 +1,36 @@ +# redMine - project management software +# Copyright (C) 2006 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +class IssuePriority < Enumeration + has_many :issues, :foreign_key => 'priority_id' + + OptionName = :enumeration_issue_priorities + # Backwards compatiblity. Can be removed post-0.9 + OptName = 'IPRI' + + def option_name + OptionName + end + + def objects_count + issues.count + end + + def transfer_relations(to) + issues.update_all("priority_id = #{to.id}") + end +end diff --git a/app/models/mail_handler.rb b/app/models/mail_handler.rb index 023e1d63c..027665329 100644 --- a/app/models/mail_handler.rb +++ b/app/models/mail_handler.rb @@ -91,7 +91,7 @@ class MailHandler < ActionMailer::Base project = target_project tracker = (get_keyword(:tracker) && project.trackers.find_by_name(get_keyword(:tracker))) || project.trackers.find(:first) category = (get_keyword(:category) && project.issue_categories.find_by_name(get_keyword(:category))) - priority = (get_keyword(:priority) && Enumeration.find_by_opt_and_name('IPRI', get_keyword(:priority))) + priority = (get_keyword(:priority) && IssuePriority.find_by_name(get_keyword(:priority))) status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status))) # check permission diff --git a/app/models/query.rb b/app/models/query.rb index 355a410b7..4e5799fbb 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -102,7 +102,7 @@ class Query < ActiveRecord::Base QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true), QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position", :groupable => true), QueryColumn.new(:status, :sortable => "#{IssueStatus.table_name}.position", :groupable => true), - QueryColumn.new(:priority, :sortable => "#{Enumeration.table_name}.position", :default_order => 'desc', :groupable => true), + QueryColumn.new(:priority, :sortable => "#{IssuePriority.table_name}.position", :default_order => 'desc', :groupable => true), QueryColumn.new(:subject, :sortable => "#{Issue.table_name}.subject"), QueryColumn.new(:author), QueryColumn.new(:assigned_to, :sortable => ["#{User.table_name}.lastname", "#{User.table_name}.firstname", "#{User.table_name}.id"], :groupable => true), @@ -152,7 +152,7 @@ class Query < ActiveRecord::Base @available_filters = { "status_id" => { :type => :list_status, :order => 1, :values => IssueStatus.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] } }, "tracker_id" => { :type => :list, :order => 2, :values => trackers.collect{|s| [s.name, s.id.to_s] } }, - "priority_id" => { :type => :list, :order => 3, :values => Enumeration.find(:all, :conditions => ['opt=?','IPRI'], :order => 'position').collect{|s| [s.name, s.id.to_s] } }, + "priority_id" => { :type => :list, :order => 3, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] } }, "subject" => { :type => :text, :order => 8 }, "created_on" => { :type => :date_past, :order => 9 }, "updated_on" => { :type => :date_past, :order => 10 }, diff --git a/app/models/time_entry.rb b/app/models/time_entry.rb index 7d165492a..e6cbdfee9 100644 --- a/app/models/time_entry.rb +++ b/app/models/time_entry.rb @@ -21,7 +21,7 @@ class TimeEntry < ActiveRecord::Base belongs_to :project belongs_to :issue belongs_to :user - belongs_to :activity, :class_name => 'Enumeration', :foreign_key => :activity_id + belongs_to :activity, :class_name => 'TimeEntryActivity', :foreign_key => 'activity_id' attr_protected :project_id, :user_id, :tyear, :tmonth, :tweek @@ -37,7 +37,7 @@ class TimeEntry < ActiveRecord::Base def after_initialize if new_record? && self.activity.nil? - if default_activity = Enumeration.activities.default + if default_activity = TimeEntryActivity.default self.activity_id = default_activity.id end end diff --git a/app/models/time_entry_activity.rb b/app/models/time_entry_activity.rb new file mode 100644 index 000000000..8e3567156 --- /dev/null +++ b/app/models/time_entry_activity.rb @@ -0,0 +1,36 @@ +# redMine - project management software +# Copyright (C) 2006 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +class TimeEntryActivity < Enumeration + has_many :time_entries, :foreign_key => 'activity_id' + + OptionName = :enumeration_activities + # Backwards compatiblity. Can be removed post-0.9 + OptName = 'ACTI' + + def option_name + OptionName + end + + def objects_count + time_entries.count + end + + def transfer_relations(to) + time_entries.update_all("activity_id = #{to.id}") + end +end diff --git a/app/views/documents/_form.rhtml b/app/views/documents/_form.rhtml index 12a16f5d7..b118ebdcf 100644 --- a/app/views/documents/_form.rhtml +++ b/app/views/documents/_form.rhtml @@ -2,7 +2,7 @@ <div class="box"> <!--[form:document]--> <p><label for="document_category_id"><%=l(:field_category)%></label> -<%= select('document', 'category_id', Enumeration.document_categories.collect {|c| [c.name, c.id]}) %></p> +<%= select('document', 'category_id', DocumentCategory.all.collect {|c| [c.name, c.id]}) %></p> <p><label for="document_title"><%=l(:field_title)%> <span class="required">*</span></label> <%= text_field 'document', 'title', :size => 60 %></p> diff --git a/app/views/enumerations/_form.rhtml b/app/views/enumerations/_form.rhtml index 3f98f5213..dac0c9331 100644 --- a/app/views/enumerations/_form.rhtml +++ b/app/views/enumerations/_form.rhtml @@ -1,7 +1,7 @@ <%= error_messages_for 'enumeration' %> <div class="box"> <!--[form:optvalue]--> -<%= hidden_field 'enumeration', 'opt' %> +<%= hidden_field 'enumeration', 'type' %> <p><label for="enumeration_name"><%=l(:field_name)%></label> <%= text_field 'enumeration', 'name' %></p> diff --git a/app/views/enumerations/list.rhtml b/app/views/enumerations/list.rhtml index 485dec71c..8143ad0f3 100644 --- a/app/views/enumerations/list.rhtml +++ b/app/views/enumerations/list.rhtml @@ -1,9 +1,9 @@ <h2><%=l(:label_enumerations)%></h2> -<% Enumeration::OPTIONS.each do |option, params| %> -<h3><%= l(params[:label]) %></h3> +<% Enumeration.get_subclasses.each do |klass| %> +<h3><%= l(klass::OptionName) %></h3> -<% enumerations = Enumeration.values(option) %> +<% enumerations = klass.all %> <% if enumerations.any? %> <table class="list"> <% enumerations.each do |enumeration| %> @@ -20,7 +20,7 @@ <% reset_cycle %> <% end %> -<p><%= link_to l(:label_enumeration_new), { :action => 'new', :opt => option } %></p> +<p><%= link_to l(:label_enumeration_new), { :action => 'new', :type => klass.name } %></p> <% end %> <% html_title(l(:label_enumerations)) -%> diff --git a/app/views/issues/bulk_edit.rhtml b/app/views/issues/bulk_edit.rhtml index 214116868..4ea9ffd32 100644 --- a/app/views/issues/bulk_edit.rhtml +++ b/app/views/issues/bulk_edit.rhtml @@ -13,7 +13,7 @@ <%= select_tag('status_id', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@available_statuses, :id, :name)) %></label> <% end %> <label><%= l(:field_priority) %>: -<%= select_tag('priority_id', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(Enumeration.priorities, :id, :name)) %></label> +<%= select_tag('priority_id', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(IssuePriority.all, :id, :name)) %></label> <label><%= l(:field_category) %>: <%= select_tag('category_id', content_tag('option', l(:label_no_change_option), :value => '') + content_tag('option', l(:label_none), :value => 'none') + diff --git a/app/views/my/blocks/_issuesassignedtome.rhtml b/app/views/my/blocks/_issuesassignedtome.rhtml index 69814bb82..0666a473b 100644 --- a/app/views/my/blocks/_issuesassignedtome.rhtml +++ b/app/views/my/blocks/_issuesassignedtome.rhtml @@ -4,7 +4,7 @@ :conditions => {:assigned_to_id => User.current.id}, :limit => 10, :include => [ :status, :project, :tracker, :priority ], - :order => "#{Enumeration.table_name}.position DESC, #{Issue.table_name}.updated_on DESC") %> + :order => "#{IssuePriority.table_name}.position DESC, #{Issue.table_name}.updated_on DESC") %> <%= render :partial => 'issues/list_simple', :locals => { :issues => assigned_issues } %> <% if assigned_issues.length > 0 %> <p class="small"><%= link_to l(:label_issue_view_all), :controller => 'issues', diff --git a/db/migrate/20090323224724_add_type_to_enumerations.rb b/db/migrate/20090323224724_add_type_to_enumerations.rb new file mode 100644 index 000000000..c2aef5e4a --- /dev/null +++ b/db/migrate/20090323224724_add_type_to_enumerations.rb @@ -0,0 +1,9 @@ +class AddTypeToEnumerations < ActiveRecord::Migration + def self.up + add_column :enumerations, :type, :string + end + + def self.down + remove_column :enumerations, :type + end +end diff --git a/db/migrate/20090401221305_update_enumerations_to_sti.rb b/db/migrate/20090401221305_update_enumerations_to_sti.rb new file mode 100644 index 000000000..50bd52010 --- /dev/null +++ b/db/migrate/20090401221305_update_enumerations_to_sti.rb @@ -0,0 +1,11 @@ +class UpdateEnumerationsToSti < ActiveRecord::Migration + def self.up + Enumeration.update_all("type = 'IssuePriority'", "opt = 'IPRI'") + Enumeration.update_all("type = 'DocumentCategory'", "opt = 'DCAT'") + Enumeration.update_all("type = 'TimeEntryActivity'", "opt = 'ACTI'") + end + + def self.down + # no-op + end +end diff --git a/test/fixtures/enumerations.yml b/test/fixtures/enumerations.yml index 22a581ab9..6e7983e8a 100644 --- a/test/fixtures/enumerations.yml +++ b/test/fixtures/enumerations.yml @@ -3,46 +3,67 @@ enumerations_001: name: Uncategorized
id: 1
opt: DCAT
+ type: DocumentCategory
enumerations_002:
name: User documentation
id: 2
opt: DCAT
+ type: DocumentCategory
enumerations_003:
name: Technical documentation
id: 3
opt: DCAT
+ type: DocumentCategory
enumerations_004:
name: Low
id: 4
opt: IPRI
+ type: IssuePriority
enumerations_005:
name: Normal
id: 5
opt: IPRI
+ type: IssuePriority
is_default: true
enumerations_006:
name: High
id: 6
opt: IPRI
+ type: IssuePriority
enumerations_007:
name: Urgent
id: 7
opt: IPRI
+ type: IssuePriority
enumerations_008:
name: Immediate
id: 8
opt: IPRI
+ type: IssuePriority
enumerations_009:
name: Design
id: 9
opt: ACTI
+ type: TimeEntryActivity
enumerations_010:
name: Development
id: 10
opt: ACTI
+ type: TimeEntryActivity
is_default: true
enumerations_011:
name: QA
id: 11
opt: ACTI
-
\ No newline at end of file + type: TimeEntryActivity
+enumerations_012:
+ name: Default Enumeration
+ id: 12
+ opt: ''
+ type: Enumeration
+ is_default: true
+enumerations_013:
+ name: Another Enumeration
+ id: 13
+ opt: ''
+ type: Enumeration
diff --git a/test/fixtures/issues.yml b/test/fixtures/issues.yml index 856f80289..b66f2eb41 100644 --- a/test/fixtures/issues.yml +++ b/test/fixtures/issues.yml @@ -95,7 +95,7 @@ issues_007: created_on: <%= 10.days.ago.to_date.to_s(:db) %>
project_id: 1
updated_on: <%= 10.days.ago.to_date.to_s(:db) %>
- priority_id: 3
+ priority_id: 5
subject: Issue due today
id: 7
fixed_version_id:
@@ -112,7 +112,7 @@ issues_008: created_on: <%= 10.days.ago.to_date.to_s(:db) %>
project_id: 1
updated_on: <%= 10.days.ago.to_date.to_s(:db) %>
- priority_id: 3
+ priority_id: 5
subject: Closed issue
id: 8
fixed_version_id:
diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb index 16c1a0804..95e75b43b 100644 --- a/test/functional/issues_controller_test.rb +++ b/test/functional/issues_controller_test.rb @@ -715,7 +715,7 @@ class IssuesControllerTest < Test::Unit::TestCase :id => 1, :issue => { :status_id => 2, :assigned_to_id => 3 }, :notes => 'Assigned to dlopper', - :time_entry => { :hours => '', :comments => '', :activity_id => Enumeration.activities.first } + :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first } end assert_redirected_to :action => 'show', :id => '1' issue.reload @@ -753,7 +753,7 @@ class IssuesControllerTest < Test::Unit::TestCase post :edit, :id => 1, :notes => '2.5 hours added', - :time_entry => { :hours => '2.5', :comments => '', :activity_id => Enumeration.activities.first } + :time_entry => { :hours => '2.5', :comments => '', :activity_id => TimeEntryActivity.first } end assert_redirected_to :action => 'show', :id => '1' diff --git a/test/integration/issues_test.rb b/test/integration/issues_test.rb index 710fa5f76..9a73252b4 100644 --- a/test/integration/issues_test.rb +++ b/test/integration/issues_test.rb @@ -41,7 +41,7 @@ class IssuesTest < ActionController::IntegrationTest post 'projects/1/issues', :tracker_id => "1", :issue => { :start_date => "2006-12-26", - :priority_id => "3", + :priority_id => "4", :subject => "new test issue", :category_id => "", :description => "new issue", diff --git a/test/unit/document_category_test.rb b/test/unit/document_category_test.rb new file mode 100644 index 000000000..6fa93a371 --- /dev/null +++ b/test/unit/document_category_test.rb @@ -0,0 +1,36 @@ +# redMine - project management software +# Copyright (C) 2006-2008 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require File.dirname(__FILE__) + '/../test_helper' + +class DocumentCategoryTest < Test::Unit::TestCase + fixtures :enumerations, :documents + + def test_should_be_an_enumeration + assert DocumentCategory.ancestors.include?(Enumeration) + end + + def test_objects_count + assert_equal 1, DocumentCategory.find_by_name("Uncategorized").objects_count + assert_equal 0, DocumentCategory.find_by_name("User documentation").objects_count + end + + def test_option_name + assert_equal :enumeration_doc_categories, DocumentCategory.new.option_name + end +end + diff --git a/test/unit/enumeration_test.rb b/test/unit/enumeration_test.rb index c192cee8f..14ea5e25c 100644 --- a/test/unit/enumeration_test.rb +++ b/test/unit/enumeration_test.rb @@ -38,40 +38,42 @@ class EnumerationTest < Test::Unit::TestCase end def test_default - e = Enumeration.priorities.default + e = Enumeration.default assert e.is_a?(Enumeration) assert e.is_default? - assert_equal 'Normal', e.name + assert_equal 'Default Enumeration', e.name end def test_create - e = Enumeration.new(:opt => 'IPRI', :name => 'Very urgent', :is_default => false) + e = Enumeration.new(:name => 'Not default', :is_default => false) + e.type = 'Enumeration' assert e.save - assert_equal 'Normal', Enumeration.priorities.default.name + assert_equal 'Default Enumeration', Enumeration.default.name end def test_create_as_default - e = Enumeration.new(:opt => 'IPRI', :name => 'Very urgent', :is_default => true) + e = Enumeration.new(:name => 'Very urgent', :is_default => true) + e.type = 'Enumeration' assert e.save - assert_equal e, Enumeration.priorities.default + assert_equal e, Enumeration.default end def test_update_default - e = Enumeration.priorities.default + e = Enumeration.default e.update_attributes(:name => 'Changed', :is_default => true) - assert_equal e, Enumeration.priorities.default + assert_equal e, Enumeration.default end def test_update_default_to_non_default - e = Enumeration.priorities.default + e = Enumeration.default e.update_attributes(:name => 'Changed', :is_default => false) - assert_nil Enumeration.priorities.default + assert_nil Enumeration.default end def test_change_default - e = Enumeration.find_by_name('Urgent') - e.update_attributes(:name => 'Urgent', :is_default => true) - assert_equal e, Enumeration.priorities.default + e = Enumeration.find_by_name('Default Enumeration') + e.update_attributes(:name => 'Changed Enumeration', :is_default => true) + assert_equal e, Enumeration.default end def test_destroy_with_reassign diff --git a/test/unit/issue_priority_test.rb b/test/unit/issue_priority_test.rb new file mode 100644 index 000000000..e2da1e82d --- /dev/null +++ b/test/unit/issue_priority_test.rb @@ -0,0 +1,38 @@ +# redMine - project management software +# Copyright (C) 2006-2008 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require File.dirname(__FILE__) + '/../test_helper' + +class IssuePriorityTest < Test::Unit::TestCase + fixtures :enumerations, :issues + + def test_should_be_an_enumeration + assert IssuePriority.ancestors.include?(Enumeration) + end + + def test_objects_count + # low priority + assert_equal 5, IssuePriority.find(4).objects_count + # urgent + assert_equal 0, IssuePriority.find(7).objects_count + end + + def test_option_name + assert_equal :enumeration_issue_priorities, IssuePriority.new.option_name + end +end + diff --git a/test/unit/issue_test.rb b/test/unit/issue_test.rb index d836f2bb5..b2eee22b9 100644 --- a/test/unit/issue_test.rb +++ b/test/unit/issue_test.rb @@ -27,14 +27,14 @@ class IssueTest < Test::Unit::TestCase :time_entries def test_create - issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => Enumeration.priorities.first, :subject => 'test_create', :description => 'IssueTest#test_create', :estimated_hours => '1:30') + issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'test_create', :description => 'IssueTest#test_create', :estimated_hours => '1:30') assert issue.save issue.reload assert_equal 1.5, issue.estimated_hours end def test_create_minimal - issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => Enumeration.priorities.first, :subject => 'test_create') + issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'test_create') assert issue.save assert issue.description.nil? end @@ -123,7 +123,7 @@ class IssueTest < Test::Unit::TestCase end def test_category_based_assignment - issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => Enumeration.priorities.first, :subject => 'Assignment test', :description => 'Assignment test', :category_id => 1) + issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'Assignment test', :description => 'Assignment test', :category_id => 1) assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to end @@ -139,7 +139,7 @@ class IssueTest < Test::Unit::TestCase def test_should_close_duplicates # Create 3 issues - issue1 = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :priority => Enumeration.priorities.first, :subject => 'Duplicates test', :description => 'Duplicates test') + issue1 = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'Duplicates test', :description => 'Duplicates test') assert issue1.save issue2 = issue1.clone assert issue2.save @@ -166,7 +166,7 @@ class IssueTest < Test::Unit::TestCase def test_should_not_close_duplicated_issue # Create 3 issues - issue1 = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :priority => Enumeration.priorities.first, :subject => 'Duplicates test', :description => 'Duplicates test') + issue1 = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'Duplicates test', :description => 'Duplicates test') assert issue1.save issue2 = issue1.clone assert issue2.save @@ -248,7 +248,7 @@ class IssueTest < Test::Unit::TestCase def test_create_should_send_email_notification ActionMailer::Base.deliveries.clear - issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => Enumeration.priorities.first, :subject => 'test_create', :estimated_hours => '1:30') + issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'test_create', :estimated_hours => '1:30') assert issue.save assert_equal 1, ActionMailer::Base.deliveries.size diff --git a/test/unit/time_entry_activity_test.rb b/test/unit/time_entry_activity_test.rb new file mode 100644 index 000000000..f99c8ab4e --- /dev/null +++ b/test/unit/time_entry_activity_test.rb @@ -0,0 +1,36 @@ +# redMine - project management software +# Copyright (C) 2006-2008 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require File.dirname(__FILE__) + '/../test_helper' + +class TimeEntryActivityTest < Test::Unit::TestCase + fixtures :enumerations, :time_entries + + def test_should_be_an_enumeration + assert TimeEntryActivity.ancestors.include?(Enumeration) + end + + def test_objects_count + assert_equal 3, TimeEntryActivity.find_by_name("Design").objects_count + assert_equal 1, TimeEntryActivity.find_by_name("Development").objects_count + end + + def test_option_name + assert_equal :enumeration_activities, TimeEntryActivity.new.option_name + end +end + |