From 7906afe6b9ebfa2c9af031503ba5dfb27b0d707f Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Tue, 3 May 2011 16:36:44 -0700 Subject: [PATCH] [#604] Add an IssueDrop with custom field support --- app/drops/issue_drop.rb | 65 +++++++++++++++++++++ app/drops/issue_status_drop.rb | 3 + app/drops/tracker_drop.rb | 3 + app/models/issue.rb | 4 ++ app/models/issue_status.rb | 4 ++ app/models/tracker.rb | 4 ++ test/unit/issue_drop_test.rb | 89 +++++++++++++++++++++++++++++ test/unit/issue_status_drop_test.rb | 21 +++++++ test/unit/tracker_drop_test.rb | 20 +++++++ 9 files changed, 213 insertions(+) create mode 100644 app/drops/issue_drop.rb create mode 100644 app/drops/issue_status_drop.rb create mode 100644 app/drops/tracker_drop.rb create mode 100644 test/unit/issue_drop_test.rb create mode 100644 test/unit/issue_status_drop_test.rb create mode 100644 test/unit/tracker_drop_test.rb diff --git a/app/drops/issue_drop.rb b/app/drops/issue_drop.rb new file mode 100644 index 00000000..40d3f572 --- /dev/null +++ b/app/drops/issue_drop.rb @@ -0,0 +1,65 @@ +class IssueDrop < BaseDrop + allowed_methods :id + allowed_methods :subject + allowed_methods :description + allowed_methods :project + allowed_methods :tracker + allowed_methods :status + allowed_methods :due_date + allowed_methods :category + allowed_methods :assigned_to + allowed_methods :priority + allowed_methods :fixed_version + allowed_methods :author + allowed_methods :created_on + allowed_methods :updated_on + allowed_methods :start_date + allowed_methods :done_ratio + allowed_methods :estimated_hours + allowed_methods :parent + + def custom_field(name) + return '' unless name.present? + custom_field = IssueCustomField.find_by_name(name.strip) + return '' unless custom_field.present? + custom_value = @object.custom_value_for(custom_field) + if custom_value.present? + return custom_value.value + else + return '' + end + end + + # TODO: both required, method_missing for Ruby and before_method for Liquid + + # Allows accessing custom fields by their name: + # + # - issue.the_name_of_player => CustomField(:name => "The name Of Player") + # + def before_method(method_sym) + if custom_field_with_matching_name = has_custom_field_with_matching_name?(method_sym) + custom_field(custom_field_with_matching_name.name) + else + super + end + end + + # Allows accessing custom fields by their name: + # + # - issue.the_name_of_player => CustomField(:name => "The name Of Player") + # + def method_missing(method_sym, *arguments, &block) + if custom_field_with_matching_name = has_custom_field_with_matching_name?(method_sym) + custom_field(custom_field_with_matching_name.name) + else + super + end + end + +private + def has_custom_field_with_matching_name?(method_sym) + custom_field_with_matching_name = @object.available_custom_fields.detect {|custom_field| + custom_field.name.downcase.underscore.gsub(' ','_') == method_sym.to_s + } + end +end diff --git a/app/drops/issue_status_drop.rb b/app/drops/issue_status_drop.rb new file mode 100644 index 00000000..88d1a9dc --- /dev/null +++ b/app/drops/issue_status_drop.rb @@ -0,0 +1,3 @@ +class IssueStatusDrop < BaseDrop + allowed_methods :name +end diff --git a/app/drops/tracker_drop.rb b/app/drops/tracker_drop.rb new file mode 100644 index 00000000..3bcf4a0e --- /dev/null +++ b/app/drops/tracker_drop.rb @@ -0,0 +1,3 @@ +class TrackerDrop < BaseDrop + allowed_methods :name +end diff --git a/app/models/issue.rb b/app/models/issue.rb index cc754014..233e2bb1 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -103,6 +103,10 @@ class Issue < ActiveRecord::Base (usr || User.current).allowed_to?(:view_issues, self.project) end + def to_liquid + IssueDrop.new(self) + end + def after_initialize if new_record? # set default values for new records only diff --git a/app/models/issue_status.rb b/app/models/issue_status.rb index 7078237d..637aaafb 100644 --- a/app/models/issue_status.rb +++ b/app/models/issue_status.rb @@ -28,6 +28,10 @@ class IssueStatus < ActiveRecord::Base IssueStatus.update_all("is_default=#{connection.quoted_false}", ['id <> ?', id]) if self.is_default? end + def to_liquid + IssueStatusDrop.new(self) + end + # Returns the default status for new issues def self.default find(:first, :conditions =>["is_default=?", true]) diff --git a/app/models/tracker.rb b/app/models/tracker.rb index 353645f7..94b75845 100644 --- a/app/models/tracker.rb +++ b/app/models/tracker.rb @@ -35,6 +35,10 @@ class Tracker < ActiveRecord::Base name <=> tracker.name end + def to_liquid + TrackerDrop.new(self) + end + def self.all find(:all, :order => 'position') end diff --git a/test/unit/issue_drop_test.rb b/test/unit/issue_drop_test.rb new file mode 100644 index 00000000..b3c034a2 --- /dev/null +++ b/test/unit/issue_drop_test.rb @@ -0,0 +1,89 @@ +require File.expand_path('../../test_helper', __FILE__) + +class IssueDropTest < ActiveSupport::TestCase + include ApplicationHelper + + def setup + @project = Project.generate! + @issue = Issue.generate_for_project!(@project) + User.current = @user = User.generate! + @role = Role.generate!(:permissions => [:view_issues]) + Member.generate!(:principal => @user, :project => @project, :roles => [@role]) + @drop = @issue.to_liquid + end + + context "drop" do + should "be a IssueDrop" do + assert @drop.is_a?(IssueDrop), "drop is not a IssueDrop" + end + end + + + [ + :tracker, + :project, + :subject, + :description, + :due_date, + :category, + :status, + :assigned_to, + :priority, + :fixed_version, + :author, + :created_on, + :updated_on, + :start_date, + :done_ratio, + :estimated_hours, + :parent + ].each do |attribute| + + should "IssueDrop##{attribute} should return the actual #{attribute} attribute" do + assert @issue.respond_to?(attribute), "Issue does not have an #{attribute} method" + assert @drop.respond_to?(attribute), "IssueDrop does not have an #{attribute} method" + + assert_equal @issue.send(attribute), @drop.send(attribute) + end + end + + context "custom fields" do + setup do + @field = IssueCustomField.generate!(:name => 'The Name', :field_format => 'string', :is_for_all => true, :trackers => @project.trackers) + @field_name_conflict = IssueCustomField.generate!(:name => 'Subject', :field_format => 'string', :is_for_all => true, :trackers => @project.trackers) + @issue.custom_fields = [{'id' => @field.id, 'value' => 'Custom field value'}, + {'id' => @field_name_conflict.id, 'value' => 'Second subject'}] + assert @issue.save + assert_equal "Custom field value", @issue.reload.custom_value_for(@field).value + assert_equal "Second subject", @issue.reload.custom_value_for(@field_name_conflict).value + @drop = @issue.to_liquid + end + + should "be accessible under #custom_field(name)" do + assert_equal @issue.reload.custom_value_for(@field).value, @drop.custom_field('The Name') + end + + should "be accessible under the custom field name (lowercase, underscored)" do + assert_equal @issue.reload.custom_value_for(@field).value, @drop.the_name + + assert textilizable("{{issue.the_name}}").include?("Custom field value") + end + + should "not be accessible under the custom field name if it conflict with an existing drop method" do + assert_equal @issue.subject, @drop.subject # no confict + end + end + + should "only load an object if it's visible to the current user" do + assert User.current.logged? + assert @issue.visible? + + @private_project = Project.generate!(:is_public => false) + @private_issue = Issue.generate_for_project!(@private_project) + + assert !@private_issue.visible?, "Issue is visible" + @private_drop = IssueDrop.new(@private_issue) + assert_equal nil, @private_drop.instance_variable_get("@object") + assert_equal nil, @private_drop.subject + end +end \ No newline at end of file diff --git a/test/unit/issue_status_drop_test.rb b/test/unit/issue_status_drop_test.rb new file mode 100644 index 00000000..83cbe24e --- /dev/null +++ b/test/unit/issue_status_drop_test.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../test_helper', __FILE__) + +class IssueStatusDropTest < ActiveSupport::TestCase + def setup + @issue_status = IssueStatus.generate! + @drop = @issue_status.to_liquid + end + + context "drop" do + should "be a IssueStatusDrop" do + assert @drop.is_a?(IssueStatusDrop), "drop is not a IssueStatusDrop" + end + end + + + context "#name" do + should "return the name" do + assert_equal @issue_status.name, @drop.name + end + end +end diff --git a/test/unit/tracker_drop_test.rb b/test/unit/tracker_drop_test.rb new file mode 100644 index 00000000..ff619d72 --- /dev/null +++ b/test/unit/tracker_drop_test.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../test_helper', __FILE__) + +class TrackerDropTest < ActiveSupport::TestCase + def setup + @tracker = Tracker.generate! + @drop = @tracker.to_liquid + end + + context "drop" do + should "be a TrackerDrop" do + assert @drop.is_a?(TrackerDrop), "drop is not a TrackerDrop" + end + end + + context "#name" do + should "return the name" do + assert_equal @tracker.name, @drop.name + end + end +end