Commit f9109d366f8125612b0bba02b11880a1388f26db

Authored by Nathan Broadbent
1 parent 25d141bd
Exists in master and in 1 other branch production

Finished #59 - Refactored Issue Trackers. Remember to run "rake db:migrate".

app/controllers/apps_controller.rb
@@ -24,6 +24,18 @@ class AppsController < InheritedResources::Base @@ -24,6 +24,18 @@ class AppsController < InheritedResources::Base
24 end 24 end
25 end 25 end
26 26
  27 + def create
  28 + @app = App.new(params[:app])
  29 + initialize_subclassed_issue_tracker
  30 + create!
  31 + end
  32 +
  33 + def update
  34 + @app = resource
  35 + initialize_subclassed_issue_tracker
  36 + update!
  37 + end
  38 +
27 def new 39 def new
28 plug_params build_resource 40 plug_params build_resource
29 new! 41 new!
@@ -34,7 +46,16 @@ class AppsController < InheritedResources::Base @@ -34,7 +46,16 @@ class AppsController < InheritedResources::Base
34 edit! 46 edit!
35 end 47 end
36 48
  49 +
37 protected 50 protected
  51 + def initialize_subclassed_issue_tracker
  52 + if params[:app][:issue_tracker_attributes] && tracker_type = params[:app][:issue_tracker_attributes][:type]
  53 + if IssueTracker.subclasses.map(&:to_s).include?(tracker_type.to_s)
  54 + @app.issue_tracker = tracker_type.constantize.new(params[:app][:issue_tracker_attributes])
  55 + end
  56 + end
  57 + end
  58 +
38 def begin_of_association_chain 59 def begin_of_association_chain
39 current_user unless current_user.admin? 60 current_user unless current_user.admin?
40 end 61 end
@@ -45,7 +66,7 @@ class AppsController < InheritedResources::Base @@ -45,7 +66,7 @@ class AppsController < InheritedResources::Base
45 66
46 def plug_params app 67 def plug_params app
47 app.watchers.build if app.watchers.none? 68 app.watchers.build if app.watchers.none?
48 - app.issue_tracker = IssueTracker.new if app.issue_tracker.nil? 69 + app.issue_tracker = IssueTracker.new unless app.issue_tracker_configured?
49 end 70 end
50 71
51 # email_at_notices is edited as a string, and stored as an array. 72 # email_at_notices is edited as a string, and stored as an array.
app/helpers/application_helper.rb
@@ -29,16 +29,5 @@ module ApplicationHelper @@ -29,16 +29,5 @@ module ApplicationHelper
29 tallies.values.inject(0) {|sum, n| sum + n} 29 tallies.values.inject(0) {|sum, n| sum + n}
30 end 30 end
31 private :total_from_tallies 31 private :total_from_tallies
32 -  
33 - def no_tracker? object  
34 - object.issue_tracker_type == "none"  
35 - end  
36 -  
37 - %w(lighthouseapp redmine pivotal fogbugz mingle).each do |tracker|  
38 - define_method("#{tracker}_tracker?".to_sym) do |object|  
39 - object.issue_tracker_type == tracker  
40 - end  
41 - end  
42 -  
43 end 32 end
44 33
app/models/app.rb
@@ -11,7 +11,7 @@ class App @@ -11,7 +11,7 @@ class App
11 field :notify_on_deploys, :type => Boolean, :default => true 11 field :notify_on_deploys, :type => Boolean, :default => true
12 field :email_at_notices, :type => Array, :default => Errbit::Config.email_at_notices 12 field :email_at_notices, :type => Array, :default => Errbit::Config.email_at_notices
13 13
14 - # Some legacy apps may have sting as key instead of BSON::ObjectID 14 + # Some legacy apps may have string as key instead of BSON::ObjectID
15 identity :type => String 15 identity :type => String
16 # There seems to be a Mongoid bug making it impossible to use String identity with references_many feature: 16 # There seems to be a Mongoid bug making it impossible to use String identity with references_many feature:
17 # https://github.com/mongoid/mongoid/issues/703 17 # https://github.com/mongoid/mongoid/issues/703
@@ -37,7 +37,7 @@ class App @@ -37,7 +37,7 @@ class App
37 accepts_nested_attributes_for :watchers, :allow_destroy => true, 37 accepts_nested_attributes_for :watchers, :allow_destroy => true,
38 :reject_if => proc { |attrs| attrs[:user_id].blank? && attrs[:email].blank? } 38 :reject_if => proc { |attrs| attrs[:user_id].blank? && attrs[:email].blank? }
39 accepts_nested_attributes_for :issue_tracker, :allow_destroy => true, 39 accepts_nested_attributes_for :issue_tracker, :allow_destroy => true,
40 - :reject_if => proc { |attrs| !%w(none lighthouseapp redmine pivotal fogbugz mingle).include?(attrs[:issue_tracker_type]) } 40 + :reject_if => proc { |attrs| !IssueTracker.subclasses.map(&:to_s).include?(attrs[:type].to_s) }
41 41
42 def self.find_by_id!(app_id) 42 def self.find_by_id!(app_id)
43 find app_id 43 find app_id
@@ -71,7 +71,7 @@ class App @@ -71,7 +71,7 @@ class App
71 end 71 end
72 72
73 def issue_tracker_configured? 73 def issue_tracker_configured?
74 - issue_tracker && issue_tracker.issue_tracker_type != "none" && !issue_tracker.project_id.blank? 74 + !!(issue_tracker && issue_tracker.class < IssueTracker && issue_tracker.project_id.present?)
75 end 75 end
76 76
77 def notification_recipients 77 def notification_recipients
app/models/issue_tracker.rb
@@ -5,171 +5,28 @@ class IssueTracker @@ -5,171 +5,28 @@ class IssueTracker
5 include Rails.application.routes.url_helpers 5 include Rails.application.routes.url_helpers
6 default_url_options[:host] = Errbit::Application.config.action_mailer.default_url_options[:host] 6 default_url_options[:host] = Errbit::Application.config.action_mailer.default_url_options[:host]
7 7
8 - validate :check_params  
9 -  
10 embedded_in :app, :inverse_of => :issue_tracker 8 embedded_in :app, :inverse_of => :issue_tracker
11 9
12 - field :account, :type => String  
13 - field :api_token, :type => String  
14 field :project_id, :type => String 10 field :project_id, :type => String
15 - field :ticket_properties, :type => String 11 + field :api_token, :type => String
  12 + field :account, :type => String
16 field :username, :type => String 13 field :username, :type => String
17 field :password, :type => String 14 field :password, :type => String
18 - field :issue_tracker_type, :type => String, :default => 'none'  
19 -  
20 - def create_issue err  
21 - case issue_tracker_type  
22 - when 'lighthouseapp'  
23 - create_lighthouseapp_issue err  
24 - when 'redmine'  
25 - create_redmine_issue err  
26 - when 'pivotal'  
27 - create_pivotal_issue err  
28 - when 'fogbugz'  
29 - create_fogbugz_issue err  
30 - when 'mingle'  
31 - create_mingle_issue err  
32 - end  
33 - end  
34 -  
35 - def ticket_properties_hash  
36 - # Parses 'key=value, key2=value2' from user input into a ruby hash.  
37 - self.ticket_properties.split(",").inject({}) do |hash, pair|  
38 - key, value = pair.split("=").map(&:strip)  
39 - hash[key] = value  
40 - hash  
41 - end  
42 - end  
43 -  
44 - protected  
45 - def create_redmine_issue err  
46 - token = api_token  
47 - acc = account  
48 - RedmineClient::Base.configure do  
49 - self.token = token  
50 - self.site = acc  
51 - end  
52 - issue = RedmineClient::Issue.new(:project_id => project_id)  
53 - issue.subject = issue_title err  
54 - issue.description = self.class.redmine_body_template.result(binding)  
55 - issue.save!  
56 - err.update_attribute :issue_link, "#{RedmineClient::Issue.site.to_s.sub(/#{RedmineClient::Issue.site.path}$/, '')}#{RedmineClient::Issue.element_path(issue.id, :project_id => project_id)}".sub(/\.xml\?project_id=#{project_id}$/, "\?project_id=#{project_id}")  
57 - end  
58 -  
59 - def create_pivotal_issue err  
60 - PivotalTracker::Client.token = api_token  
61 - PivotalTracker::Client.use_ssl = true  
62 - project = PivotalTracker::Project.find project_id.to_i  
63 - story = project.stories.create :name => issue_title(err), :story_type => 'bug', :description => self.class.pivotal_body_template.result(binding)  
64 - err.update_attribute :issue_link, "https://www.pivotaltracker.com/story/show/#{story.id}"  
65 - end  
66 -  
67 - def create_lighthouseapp_issue err  
68 - Lighthouse.account = account  
69 - Lighthouse.token = api_token  
70 -  
71 - # updating lighthouse account  
72 - Lighthouse::Ticket.site  
73 -  
74 - ticket = Lighthouse::Ticket.new(:project_id => project_id)  
75 - ticket.title = issue_title err  
76 -  
77 - ticket.body = self.class.lighthouseapp_body_template.result(binding)  
78 -  
79 - ticket.tags << "errbit"  
80 - ticket.save!  
81 - err.update_attribute :issue_link, "#{Lighthouse::Ticket.site.to_s.sub(/#{Lighthouse::Ticket.site.path}$/, '')}#{Lighthouse::Ticket.element_path(ticket.id, :project_id => project_id)}".sub(/\.xml$/, '')  
82 - end  
83 -  
84 - def create_fogbugz_issue err  
85 - fogbugz = Fogbugz::Interface.new(:email => username, :password => password, :uri => "https://#{account}.fogbugz.com")  
86 - fogbugz.authenticate  
87 -  
88 - issue = {}  
89 - issue['sTitle'] = issue_title err  
90 - issue['sArea'] = project_id  
91 - issue['sEvent'] = self.class.fogbugz_body_template.result(binding)  
92 - issue['sTags'] = ['errbit'].join(',')  
93 - issue['cols'] = ['ixBug'].join(',')  
94 -  
95 - fb_resp = fogbugz.command(:new, issue)  
96 - err.update_attribute :issue_link, "https://#{account}.fogbugz.com/default.asp?#{fb_resp['case']['ixBug']}"  
97 - end  
98 -  
99 - def create_mingle_issue err  
100 - properties = ticket_properties_hash  
101 - basic_auth = account.gsub(/https?:\/\//, "https://#{username}:#{password}@")  
102 - Mingle.set_site "#{basic_auth}/api/v1/projects/#{project_id}/" 15 + field :ticket_properties, :type => String
103 16
104 - card = Mingle::Card.new  
105 - card.card_type_name = properties.delete("card_type")  
106 - card.name = issue_title(err)  
107 - card.description = self.class.mingle_body_template.result(binding)  
108 - properties.each do |property, value|  
109 - card.send("cp_#{property}=", value)  
110 - end 17 + validate :check_params
111 18
112 - card.save!  
113 - err.update_attribute :issue_link, URI.parse("#{account}/projects/#{project_id}/cards/#{card.id}").to_s  
114 - end 19 + # Subclasses are responsible for overwriting this method.
  20 + def check_params; end
115 21
116 def issue_title err 22 def issue_title err
117 "[#{ err.environment }][#{ err.where }] #{err.message.to_s.truncate(100)}" 23 "[#{ err.environment }][#{ err.where }] #{err.message.to_s.truncate(100)}"
118 end 24 end
119 25
120 - def check_params  
121 - blank_flag_fields = %w(project_id)  
122 - if %w(fogbugz mingle).include?(issue_tracker_type)  
123 - blank_flag_fields += %w(username password)  
124 - else  
125 - blank_flag_fields << 'api_token'  
126 - end  
127 - blank_flag_fields << 'account' if(%w(fogbugz lighthouseapp redmine mingle).include?(issue_tracker_type))  
128 - blank_flags = blank_flag_fields.map {|m| self[m].blank? }  
129 -  
130 - if issue_tracker_type == "mingle"  
131 - # Check that mingle was given a 'card_type' in the ticket_properties  
132 - blank_flags << "card_type" unless ticket_properties_hash["card_type"]  
133 - end  
134 -  
135 - if blank_flags.any? && !blank_flags.all?  
136 - message = case issue_tracker_type  
137 - when 'lighthouseapp'  
138 - 'You must specify your Lighthouseapp account, API token and Project ID'  
139 - when 'redmine'  
140 - 'You must specify your Redmine URL, API token and Project ID'  
141 - when 'pivotal'  
142 - 'You must specify your Pivotal Tracker API token and Project ID'  
143 - when 'fogbugz'  
144 - 'You must specify your FogBugz Area Name, Username, and Password'  
145 - when 'mingle'  
146 - 'You must specify your Mingle URL, Project ID, Card Type (in default card properties), Sign-in name, and Password'  
147 - end  
148 - errors.add(:base, message)  
149 - end  
150 - end  
151 -  
152 - class << self  
153 - def lighthouseapp_body_template  
154 - @@lighthouseapp_body_template ||= ERB.new(File.read(Rails.root + "app/views/errs/lighthouseapp_body.txt.erb").gsub(/^\s*/, ''))  
155 - end 26 + # Allows us to set the issue tracker class from a single form.
  27 + def type; self._type; end
  28 + def type=(t); self._type=t; end
156 29
157 - def redmine_body_template  
158 - @@redmine_body_template ||= ERB.new(File.read(Rails.root + "app/views/errs/redmine_body.txt.erb"))  
159 - end  
160 -  
161 - def pivotal_body_template  
162 - @@pivotal_body_template ||= ERB.new(File.read(Rails.root + "app/views/errs/pivotal_body.txt.erb"))  
163 - end  
164 -  
165 - def fogbugz_body_template  
166 - @@fogbugz_body_template ||= ERB.new(File.read(Rails.root + "app/views/errs/fogbugz_body.txt.erb"))  
167 - end  
168 -  
169 - def mingle_body_template  
170 - # Mingle also uses textile markup, so the redmine template is perfect.  
171 - redmine_body_template  
172 - end  
173 - end 30 + def self.label; "(none)"; end
174 end 31 end
175 32
app/models/issue_trackers/fogbugz_tracker.rb 0 → 100644
@@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
  1 +class FogbugzTracker < IssueTracker
  2 + def self.label; "fogbugz"; end
  3 +
  4 + def check_params
  5 + if %w(project_id account username password).detect {|f| self[f].blank? }
  6 + errors.add :base, 'You must specify your FogBugz Area Name, FogBugz URL, Username, and Password'
  7 + end
  8 + end
  9 +
  10 + def create_issue(err)
  11 + fogbugz = Fogbugz::Interface.new(:email => username, :password => password, :uri => "https://#{account}.fogbugz.com")
  12 + fogbugz.authenticate
  13 +
  14 + issue = {}
  15 + issue['sTitle'] = issue_title err
  16 + issue['sArea'] = project_id
  17 + issue['sEvent'] = body_template.result(binding)
  18 + issue['sTags'] = ['errbit'].join(',')
  19 + issue['cols'] = ['ixBug'].join(',')
  20 +
  21 + fb_resp = fogbugz.command(:new, issue)
  22 + err.update_attribute :issue_link, "https://#{account}.fogbugz.com/default.asp?#{fb_resp['case']['ixBug']}"
  23 + end
  24 +
  25 + def body_template
  26 + @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/fogbugz_body.txt.erb"))
  27 + end
  28 +end
  29 +
app/models/issue_trackers/lighthouse_tracker.rb 0 → 100644
@@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
  1 +class LighthouseTracker < IssueTracker
  2 + def self.label; "lighthouseapp"; end
  3 +
  4 + def check_params
  5 + if %w(account api_token project_id).detect {|f| self[f].blank? }
  6 + errors.add :base, 'You must specify your Lighthouseapp account, API token and Project ID'
  7 + end
  8 + end
  9 +
  10 + def create_issue(err)
  11 + Lighthouse.account = account
  12 + Lighthouse.token = api_token
  13 + # updating lighthouse account
  14 + Lighthouse::Ticket.site
  15 +
  16 + ticket = Lighthouse::Ticket.new(:project_id => project_id)
  17 + ticket.title = issue_title err
  18 +
  19 + ticket.body = body_template.result(binding)
  20 +
  21 + ticket.tags << "errbit"
  22 + ticket.save!
  23 + err.update_attribute :issue_link, "#{Lighthouse::Ticket.site.to_s.sub(/#{Lighthouse::Ticket.site.path}$/, '')}#{Lighthouse::Ticket.element_path(ticket.id, :project_id => project_id)}".sub(/\.xml$/, '')
  24 + end
  25 +
  26 + def body_template
  27 + @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/lighthouseapp_body.txt.erb").gsub(/^\s*/, ''))
  28 + end
  29 +end
  30 +
app/models/issue_trackers/mingle_tracker.rb 0 → 100644
@@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
  1 +class MingleTracker < IssueTracker
  2 + def self.label; "mingle"; end
  3 +
  4 + def check_params
  5 + if %w(account project_id username password).detect {|f| self[f].blank? } or !ticket_properties_hash["card_type"]
  6 + errors.add :base, 'You must specify your Mingle URL, Project ID, Card Type (in default card properties), Sign-in name, and Password'
  7 + end
  8 + end
  9 +
  10 + def create_issue(err)
  11 + properties = ticket_properties_hash
  12 + basic_auth = account.gsub(/https?:\/\//, "https://#{username}:#{password}@")
  13 + Mingle.set_site "#{basic_auth}/api/v1/projects/#{project_id}/"
  14 +
  15 + card = Mingle::Card.new
  16 + card.card_type_name = properties.delete("card_type")
  17 + card.name = issue_title(err)
  18 + card.description = body_template.result(binding)
  19 + properties.each do |property, value|
  20 + card.send("cp_#{property}=", value)
  21 + end
  22 +
  23 + card.save!
  24 + err.update_attribute :issue_link, URI.parse("#{account}/projects/#{project_id}/cards/#{card.id}").to_s
  25 + end
  26 +
  27 + def body_template
  28 + # Mingle also uses textile markup, so the redmine template is perfect.
  29 + @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/redmine_body.txt.erb"))
  30 + end
  31 +
  32 + def ticket_properties_hash
  33 + # Parses 'key=value, key2=value2' from ticket_properties into a ruby hash.
  34 + self.ticket_properties.split(",").inject({}) do |hash, pair|
  35 + key, value = pair.split("=").map(&:strip)
  36 + hash[key] = value
  37 + hash
  38 + end
  39 + end
  40 +end
  41 +
app/models/issue_trackers/pivotal_labs_tracker.rb 0 → 100644
@@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
  1 +class PivotalLabsTracker < IssueTracker
  2 + def self.label; "pivotal"; end
  3 +
  4 + def check_params
  5 + if %w(api_token project_id).detect {|f| self[f].blank? }
  6 + errors.add :base, 'You must specify your Pivotal Tracker API token and Project ID'
  7 + end
  8 + end
  9 +
  10 + def create_issue(err)
  11 + PivotalTracker::Client.token = api_token
  12 + PivotalTracker::Client.use_ssl = true
  13 + project = PivotalTracker::Project.find project_id.to_i
  14 + story = project.stories.create :name => issue_title(err), :story_type => 'bug', :description => body_template.result(binding)
  15 + err.update_attribute :issue_link, "https://www.pivotaltracker.com/story/show/#{story.id}"
  16 + end
  17 +
  18 + def body_template
  19 + @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/pivotal_body.txt.erb"))
  20 + end
  21 +end
  22 +
app/models/issue_trackers/redmine_tracker.rb 0 → 100644
@@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
  1 +class RedmineTracker < IssueTracker
  2 + def self.label; "redmine"; end
  3 +
  4 + def check_params
  5 + if %w(account api_token project_id).detect {|f| self[f].blank? }
  6 + errors.add :base, 'You must specify your Redmine URL, API token and Project ID'
  7 + end
  8 + end
  9 +
  10 + def create_issue(err)
  11 + token = api_token
  12 + acc = account
  13 + RedmineClient::Base.configure do
  14 + self.token = token
  15 + self.site = acc
  16 + end
  17 + issue = RedmineClient::Issue.new(:project_id => project_id)
  18 + issue.subject = issue_title err
  19 + issue.description = body_template.result(binding)
  20 + issue.save!
  21 + err.update_attribute :issue_link, "#{RedmineClient::Issue.site.to_s.sub(/#{RedmineClient::Issue.site.path}$/, '')}#{RedmineClient::Issue.element_path(issue.id, :project_id => project_id)}".sub(/\.xml\?project_id=#{project_id}$/, "\?project_id=#{project_id}")
  22 + end
  23 +
  24 + def body_template
  25 + @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/redmine_body.txt.erb"))
  26 + end
  27 +end
  28 +
app/views/apps/_fields.html.haml
@@ -50,40 +50,40 @@ @@ -50,40 +50,40 @@
50 = f.fields_for :issue_tracker do |w| 50 = f.fields_for :issue_tracker do |w|
51 %div.issue_tracker.nested 51 %div.issue_tracker.nested
52 %div.choose 52 %div.choose
53 - = w.radio_button :issue_tracker_type, :none  
54 - = label_tag :issue_tracker_type_none, '(None)', :for => label_for_attr(w, 'issue_tracker_type_none')  
55 - = w.radio_button :issue_tracker_type, :lighthouseapp  
56 - = label_tag :issue_tracker_type_lighthouseapp, 'Lighthouse', :for => label_for_attr(w, 'issue_tracker_type_lighthouseapp')  
57 - = w.radio_button :issue_tracker_type, :redmine  
58 - = label_tag :issue_tracker_type_redmine, 'Redmine', :for => label_for_attr(w, 'issue_tracker_type_redmine')  
59 - = w.radio_button :issue_tracker_type, :pivotal  
60 - = label_tag :issue_tracker_type_pivotal, 'Pivotal Tracker', :for => label_for_attr(w, 'issue_tracker_type_pivotal')  
61 - = w.radio_button :issue_tracker_type, :fogbugz  
62 - = label_tag :issue_tracker_type_fogbugz, 'FogBugz', :for => label_for_attr(w, 'issue_tracker_type_fogbugz')  
63 - = w.radio_button :issue_tracker_type, :mingle  
64 - = label_tag :issue_tracker_type_fogbugz, 'Mingle', :for => label_for_attr(w, 'issue_tracker_type_mingle')  
65 - %div.tracker_params.none{:class => no_tracker?(w.object) ? 'chosen' : nil} 53 + = w.radio_button :type, "IssueTracker", 'data-section' => 'none'
  54 + = label_tag :type_none, '(None)', :for => label_for_attr(w, 'type_none')
  55 + = w.radio_button :type, "LighthouseTracker", 'data-section' => 'lighthouse'
  56 + = label_tag :type_lighthouseapp, 'Lighthouse', :for => label_for_attr(w, 'type_lighthouseapp')
  57 + = w.radio_button :type, "RedmineTracker", 'data-section' => 'redmine'
  58 + = label_tag :type_redmine, 'Redmine', :for => label_for_attr(w, 'type_redmine')
  59 + = w.radio_button :type, "PivotalTracker", 'data-section' => 'pivotal'
  60 + = label_tag :type_pivotal, 'Pivotal Tracker', :for => label_for_attr(w, 'type_pivotal')
  61 + = w.radio_button :type, "FogbugzTracker", 'data-section' => 'fogbugz'
  62 + = label_tag :type_fogbugz, 'FogBugz', :for => label_for_attr(w, 'type_fogbugz')
  63 + = w.radio_button :type, "MingleTracker", 'data-section' => 'mingle'
  64 + = label_tag :type_fogbugz, 'Mingle', :for => label_for_attr(w, 'type_mingle')
  65 + %div.tracker_params.none{:class => (w.object && !(w.object.class < IssueTracker)) ? 'chosen' : nil}
66 %p When no issue tracker has been configured, you will be able to leave comments on errors. 66 %p When no issue tracker has been configured, you will be able to leave comments on errors.
67 - %div.tracker_params.lighthouseapp{:class => lighthouseapp_tracker?(w.object) ? 'chosen' : nil} 67 + %div.tracker_params.lighthouse{:class => w.object.is_a?(LighthouseTracker) ? 'chosen' : nil}
68 = w.label :account, "Account" 68 = w.label :account, "Account"
69 = w.text_field :account, :placeholder => "abc from abc.lighthouseapp.com" 69 = w.text_field :account, :placeholder => "abc from abc.lighthouseapp.com"
70 = w.label :api_token, "API token" 70 = w.label :api_token, "API token"
71 = w.text_field :api_token, :placeholder => "API Token for your account" 71 = w.text_field :api_token, :placeholder => "API Token for your account"
72 = w.label :project_id, "Project ID" 72 = w.label :project_id, "Project ID"
73 = w.text_field :project_id 73 = w.text_field :project_id
74 - %div.tracker_params.redmine{:class => redmine_tracker?(w.object) ? 'chosen' : nil} 74 + %div.tracker_params.redmine{:class => w.object.is_a?(RedmineTracker) ? 'chosen' : nil}
75 = w.label :account, "Redmine URL" 75 = w.label :account, "Redmine URL"
76 = w.text_field :account, :placeholder => "like http://www.redmine.org/" 76 = w.text_field :account, :placeholder => "like http://www.redmine.org/"
77 = w.label :api_token, "API token" 77 = w.label :api_token, "API token"
78 = w.text_field :api_token, :placeholder => "API Token for your account" 78 = w.text_field :api_token, :placeholder => "API Token for your account"
79 = w.label :project_id, "Project ID" 79 = w.label :project_id, "Project ID"
80 = w.text_field :project_id 80 = w.text_field :project_id
81 - %div.tracker_params.pivotal{:class => pivotal_tracker?(w.object) ? 'chosen' : nil} 81 + %div.tracker_params.pivotal{:class => w.object.is_a?(PivotalLabsTracker) ? 'chosen' : nil}
82 = w.label :project_id, "Project ID" 82 = w.label :project_id, "Project ID"
83 = w.text_field :project_id 83 = w.text_field :project_id
84 = w.label :api_token, "API token" 84 = w.label :api_token, "API token"
85 = w.text_field :api_token, :placeholder => "API Token for your account" 85 = w.text_field :api_token, :placeholder => "API Token for your account"
86 - %div.tracker_params.fogbugz{:class => fogbugz_tracker?(w.object) ? 'chosen' : nil} 86 + %div.tracker_params.fogbugz{:class => w.object.is_a?(FogbugzTracker) ? 'chosen' : nil}
87 = w.label :project_id, "Area Name" 87 = w.label :project_id, "Area Name"
88 = w.text_field :project_id 88 = w.text_field :project_id
89 = w.label :account, "FogBugz URL" 89 = w.label :account, "FogBugz URL"
@@ -92,7 +92,7 @@ @@ -92,7 +92,7 @@
92 = w.text_field :username, :placeholder => 'Username/Email for your account' 92 = w.text_field :username, :placeholder => 'Username/Email for your account'
93 = w.label :password, 'Password' 93 = w.label :password, 'Password'
94 = w.password_field :password, :placeholder => 'Password for your account' 94 = w.password_field :password, :placeholder => 'Password for your account'
95 - %div.tracker_params.mingle{:class => mingle_tracker?(w.object) ? 'chosen' : nil} 95 + %div.tracker_params.mingle{:class => w.object.is_a?(MingleTracker) ? 'chosen' : nil}
96 = w.label :account, "Mingle URL" 96 = w.label :account, "Mingle URL"
97 = w.text_field :account, :placeholder => "http://mingle.yoursite.com/" 97 = w.text_field :account, :placeholder => "http://mingle.yoursite.com/"
98 = w.label :project_id, "Project ID" 98 = w.label :project_id, "Project ID"
app/views/errs/fogbugz_body.txt.erb
@@ -1,31 +0,0 @@ @@ -1,31 +0,0 @@
1 -"See this exception on Errbit": <%= app_err_url(err.app, err) %>  
2 -<% if notice = err.notices.first %>  
3 - <%= notice.message %>  
4 -  
5 - Summary  
6 - - Where  
7 - <%= notice.err.where %>  
8 -  
9 - - Occured  
10 - <%= notice.created_at.to_s(:micro) %>  
11 -  
12 - - Similar  
13 - <%= (notice.err.notices_count - 1).to_s %>  
14 -  
15 - Params  
16 - <%= pretty_hash(notice.params) %>  
17 -  
18 - Session  
19 - <%= pretty_hash(notice.session) %>  
20 -  
21 - Backtrace  
22 - <% for line in notice.backtrace %>  
23 - <%= line['number'] %>: <%= line['file'].sub(/^\[PROJECT_ROOT\]/, '') %>  
24 - <% end %>  
25 -  
26 - Environment  
27 - <% for key, val in notice.env_vars %>  
28 - <%= key %>: <%= val %>  
29 - <% end %>  
30 -<% end %>  
31 -  
app/views/errs/lighthouseapp_body.txt.erb
@@ -1,34 +0,0 @@ @@ -1,34 +0,0 @@
1 -[See this exception on Errbit](<%= app_err_url err.app, err %> "See this exception on Errbit")  
2 -<% if notice = err.notices.first %>  
3 - # <%= notice.message %> #  
4 - ## Summary ##  
5 - <% if notice.request['url'].present? %>  
6 - ### URL ###  
7 - [<%= notice.request['url'] %>](<%= notice.request['url'] %>)"  
8 - <% end %>  
9 - ### Where ###  
10 - <%= notice.err.where %>  
11 -  
12 - ### Occured ###  
13 - <%= notice.created_at.to_s(:micro) %>  
14 -  
15 - ### Similar ###  
16 - <%= (notice.err.notices_count - 1).to_s %>  
17 -  
18 - ## Params ##  
19 - <code><%= pretty_hash(notice.params) %></code>  
20 -  
21 - ## Session ##  
22 - <code><%= pretty_hash(notice.session) %></code>  
23 -  
24 - ## Backtrace ##  
25 - <code>  
26 - <% for line in notice.backtrace %><%= line['number'] %>: <%= line['file'].sub(/^\[PROJECT_ROOT\]/, '') %> -> **<%= line['method'] %>**  
27 - <% end %>  
28 - </code>  
29 -  
30 - ## Environment ##  
31 - <% for key, val in notice.env_vars %>  
32 - <%= key %>: <%= val %>  
33 - <% end %>  
34 -<% end %>  
app/views/errs/pivotal_body.txt.erb
@@ -1,16 +0,0 @@ @@ -1,16 +0,0 @@
1 -See this exception on Errbit: <%= app_err_url err.app, err %>  
2 -<% if notice = err.notices.first %>  
3 - <% if notice.request['url'].present? %>URL: <%= notice.request['url'] %><% end %>  
4 - Where: <%= notice.err.where %>  
5 - Occurred: <%= notice.created_at.to_s :micro %>  
6 - Similar: <%= (notice.err.notices.count - 1).to_s %>  
7 -  
8 - Params:  
9 - <%= pretty_hash notice.params %>  
10 -  
11 - Session:  
12 - <%= pretty_hash notice.session %>  
13 -  
14 - Backtrace:  
15 - <%= notice.backtrace[0..4].map { |line| "#{line['number']}: #{line['file'].sub(/^\[PROJECT_ROOT\]/, '')} -> *#{line['method']}*" }.join "\n" %>  
16 -<% end %>  
app/views/errs/redmine_body.txt.erb
@@ -1,44 +0,0 @@ @@ -1,44 +0,0 @@
1 -<% if notice = err.notices.first %>  
2 -h1. <%= notice.message %>  
3 -  
4 -h3. "See this exception on Errbit":<%= app_err_url err.app, err %>  
5 -  
6 -h2. Summary  
7 -<% if notice.request['url'].present? %>  
8 -h3. URL  
9 -  
10 -"<%= notice.request['url'] %>":<%= notice.request['url'] %>  
11 -<% end %>  
12 -h3. Where  
13 -  
14 -<%= notice.err.where %>  
15 -  
16 -h3. Occured  
17 -  
18 -<%= notice.created_at.to_s(:micro) %>  
19 -  
20 -h3. Similar  
21 -  
22 -<%= (notice.err.notices_count - 1).to_s %>  
23 -  
24 -h2. Params  
25 -  
26 -<pre><%= pretty_hash(notice.params) %></pre>  
27 -  
28 -h2. Session  
29 -  
30 -<pre><%= pretty_hash(notice.session) %></pre>  
31 -  
32 -h2. Backtrace  
33 -  
34 -| Line | File | Method |  
35 -<% for line in notice.backtrace %>| <%= line['number'] %> | <%= line['file'].sub(/^\[PROJECT_ROOT\]/, '') %> | *<%= line['method'] %>* |  
36 -<% end %>  
37 -  
38 -h2. Environment  
39 -  
40 -<% for key, val in notice.env_vars %>| <%= key %> | <%= val %> |  
41 -<% end %>  
42 -  
43 -<% end %>  
44 -  
app/views/errs/show.html.haml
@@ -13,9 +13,9 @@ @@ -13,9 +13,9 @@
13 - content_for :action_bar do 13 - content_for :action_bar do
14 - if @err.app.issue_tracker_configured? 14 - if @err.app.issue_tracker_configured?
15 - if @err.issue_link.blank? 15 - if @err.issue_link.blank?
16 - %span= link_to 'create issue', create_issue_app_err_path(@app, @err), :method => :post, :class => "#{@app.issue_tracker.issue_tracker_type}_create create-issue" 16 + %span= link_to 'create issue', create_issue_app_err_path(@app, @err), :method => :post, :class => "#{@app.issue_tracker.class.label}_create create-issue"
17 - else 17 - else
18 - %span= link_to 'go to issue', @err.issue_link, :class => "#{@app.issue_tracker.issue_tracker_type}_goto goto-issue" 18 + %span= link_to 'go to issue', @err.issue_link, :class => "#{@app.issue_tracker.class.label}_goto goto-issue"
19 = link_to 'unlink issue', unlink_issue_app_err_path(@app, @err), :method => :delete, :confirm => "Unlink err issues?", :class => "unlink-issue" 19 = link_to 'unlink issue', unlink_issue_app_err_path(@app, @err), :method => :delete, :confirm => "Unlink err issues?", :class => "unlink-issue"
20 - if @err.unresolved? 20 - if @err.unresolved?
21 %span= link_to 'resolve', resolve_app_err_path(@app, @err), :method => :put, :confirm => err_confirm, :class => 'resolve' 21 %span= link_to 'resolve', resolve_app_err_path(@app, @err), :method => :put, :confirm => err_confirm, :class => 'resolve'
app/views/issue_trackers/fogbugz_body.txt.erb 0 → 100644
@@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
  1 +"See this exception on Errbit": <%= app_err_url(err.app, err) %>
  2 +<% if notice = err.notices.first %>
  3 + <%= notice.message %>
  4 +
  5 + Summary
  6 + - Where
  7 + <%= notice.err.where %>
  8 +
  9 + - Occured
  10 + <%= notice.created_at.to_s(:micro) %>
  11 +
  12 + - Similar
  13 + <%= (notice.err.notices_count - 1).to_s %>
  14 +
  15 + Params
  16 + <%= pretty_hash(notice.params) %>
  17 +
  18 + Session
  19 + <%= pretty_hash(notice.session) %>
  20 +
  21 + Backtrace
  22 + <% for line in notice.backtrace %>
  23 + <%= line['number'] %>: <%= line['file'].sub(/^\[PROJECT_ROOT\]/, '') %>
  24 + <% end %>
  25 +
  26 + Environment
  27 + <% for key, val in notice.env_vars %>
  28 + <%= key %>: <%= val %>
  29 + <% end %>
  30 +<% end %>
  31 +
app/views/issue_trackers/lighthouseapp_body.txt.erb 0 → 100644
@@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
  1 +[See this exception on Errbit](<%= app_err_url err.app, err %> "See this exception on Errbit")
  2 +<% if notice = err.notices.first %>
  3 + # <%= notice.message %> #
  4 + ## Summary ##
  5 + <% if notice.request['url'].present? %>
  6 + ### URL ###
  7 + [<%= notice.request['url'] %>](<%= notice.request['url'] %>)"
  8 + <% end %>
  9 + ### Where ###
  10 + <%= notice.err.where %>
  11 +
  12 + ### Occured ###
  13 + <%= notice.created_at.to_s(:micro) %>
  14 +
  15 + ### Similar ###
  16 + <%= (notice.err.notices_count - 1).to_s %>
  17 +
  18 + ## Params ##
  19 + <code><%= pretty_hash(notice.params) %></code>
  20 +
  21 + ## Session ##
  22 + <code><%= pretty_hash(notice.session) %></code>
  23 +
  24 + ## Backtrace ##
  25 + <code>
  26 + <% for line in notice.backtrace %><%= line['number'] %>: <%= line['file'].sub(/^\[PROJECT_ROOT\]/, '') %> -> **<%= line['method'] %>**
  27 + <% end %>
  28 + </code>
  29 +
  30 + ## Environment ##
  31 + <% for key, val in notice.env_vars %>
  32 + <%= key %>: <%= val %>
  33 + <% end %>
  34 +<% end %>
app/views/issue_trackers/pivotal_body.txt.erb 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +See this exception on Errbit: <%= app_err_url err.app, err %>
  2 +<% if notice = err.notices.first %>
  3 + <% if notice.request['url'].present? %>URL: <%= notice.request['url'] %><% end %>
  4 + Where: <%= notice.err.where %>
  5 + Occurred: <%= notice.created_at.to_s :micro %>
  6 + Similar: <%= (notice.err.notices.count - 1).to_s %>
  7 +
  8 + Params:
  9 + <%= pretty_hash notice.params %>
  10 +
  11 + Session:
  12 + <%= pretty_hash notice.session %>
  13 +
  14 + Backtrace:
  15 + <%= notice.backtrace[0..4].map { |line| "#{line['number']}: #{line['file'].sub(/^\[PROJECT_ROOT\]/, '')} -> *#{line['method']}*" }.join "\n" %>
  16 +<% end %>
app/views/issue_trackers/redmine_body.txt.erb 0 → 100644
@@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
  1 +<% if notice = err.notices.first %>
  2 +h1. <%= notice.message %>
  3 +
  4 +h3. "See this exception on Errbit":<%= app_err_url err.app, err %>
  5 +
  6 +h2. Summary
  7 +<% if notice.request['url'].present? %>
  8 +h3. URL
  9 +
  10 +"<%= notice.request['url'] %>":<%= notice.request['url'] %>
  11 +<% end %>
  12 +h3. Where
  13 +
  14 +<%= notice.err.where %>
  15 +
  16 +h3. Occured
  17 +
  18 +<%= notice.created_at.to_s(:micro) %>
  19 +
  20 +h3. Similar
  21 +
  22 +<%= (notice.err.notices_count - 1).to_s %>
  23 +
  24 +h2. Params
  25 +
  26 +<pre><%= pretty_hash(notice.params) %></pre>
  27 +
  28 +h2. Session
  29 +
  30 +<pre><%= pretty_hash(notice.session) %></pre>
  31 +
  32 +h2. Backtrace
  33 +
  34 +| Line | File | Method |
  35 +<% for line in notice.backtrace %>| <%= line['number'] %> | <%= line['file'].sub(/^\[PROJECT_ROOT\]/, '') %> | *<%= line['method'] %>* |
  36 +<% end %>
  37 +
  38 +h2. Environment
  39 +
  40 +<% for key, val in notice.env_vars %>| <%= key %> | <%= val %> |
  41 +<% end %>
  42 +
  43 +<% end %>
  44 +
config/application.rb
@@ -19,7 +19,7 @@ module Errbit @@ -19,7 +19,7 @@ module Errbit
19 # -- all .rb files in that directory are automatically loaded. 19 # -- all .rb files in that directory are automatically loaded.
20 20
21 # Custom directories with classes and modules you want to be autoloadable. 21 # Custom directories with classes and modules you want to be autoloadable.
22 - # config.autoload_paths += %W(#{config.root}/extras) 22 + config.autoload_paths += %W(app/models/issue_trackers)
23 23
24 # Only load the plugins named here, in the order given (default is alphabetical). 24 # Only load the plugins named here, in the order given (default is alphabetical).
25 # :all can be used as a placeholder for all plugins not explicitly named. 25 # :all can be used as a placeholder for all plugins not explicitly named.
@@ -53,3 +53,4 @@ module Errbit @@ -53,3 +53,4 @@ module Errbit
53 config.filter_parameters += [:password] 53 config.filter_parameters += [:password]
54 end 54 end
55 end 55 end
  56 +
config/initializers/issue_tracker_apis.rb 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +# Require all issue tracker apis in lib/issue_tracker_apis
  2 +Dir.glob(Rails.root.join('lib/issue_tracker_apis/*.rb')).each {|t| require t }
  3 +
config/initializers/issue_trackers.rb
@@ -1,2 +0,0 @@ @@ -1,2 +0,0 @@
1 -require Rails.root.join('lib/issue_trackers/mingle.rb')  
2 -  
db/migrate/20110812135951_move_issue_trackers_to_sti.rb 0 → 100644
@@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
  1 +class MoveIssueTrackersToSti < Mongoid::Migration
  2 + def self.up
  3 + App.all.each do |app|
  4 + # Update all embedded issue trackers to use STI patterns.
  5 + # All issue trackers now subclass the IssueTracker model,
  6 + # and their class is stored in the '_type' field, which is
  7 + # also aliased to 'type'.
  8 + if app.issue_tracker && app.issue_tracker.attributes["issue_tracker_type"]
  9 + app.issue_tracker._type = case app.issue_tracker.issue_tracker_type
  10 + when 'lighthouseapp'; "LighthouseTracker"
  11 + when 'redmine'; "RedmineTracker"
  12 + when 'pivotal'; "PivotalLabsTracker"
  13 + when 'fogbugz'; "FogbugzTracker"
  14 + when 'mingle'; "MingleTracker"
  15 + else; nil
  16 + end
  17 + if app.issue_tracker.issue_tracker_type == "none"
  18 + app.issue_tracker = nil
  19 + else
  20 + app.issue_tracker.issue_tracker_type = nil
  21 + end
  22 + app.save
  23 + end
  24 + end
  25 + end
  26 +
  27 + def self.down
  28 + end
  29 +end
  30 +
lib/issue_tracker_apis/mingle.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +module Mingle
  2 + class Card < ActiveResource::Base
  3 + # site template ~> "https://username:password@mingle.example.com/api/v1/projects/:project_id/"
  4 + end
  5 + def self.set_site(site)
  6 + # ActiveResource seems to clone and freeze the @site variable
  7 + # after the first use. It seems that the only way to change @site
  8 + # is to drop the subclass, and then reload it.
  9 + Mingle.send(:remove_const, :Card)
  10 + load File.join(Rails.root,'lib','issue_tracker_apis','mingle.rb')
  11 + Mingle::Card.site = site
  12 + end
  13 +end
  14 +
lib/issue_trackers/mingle.rb
@@ -1,14 +0,0 @@ @@ -1,14 +0,0 @@
1 -module Mingle  
2 - class Card < ActiveResource::Base  
3 - # site template ~> "https://username:password@mingle.example.com/api/v1/projects/:project_id/"  
4 - end  
5 - def self.set_site(site)  
6 - # ActiveResource seems to clone and freeze the @site variable  
7 - # after the first use. It seems that the only way to change @site  
8 - # is to drop the subclass, and then reload it.  
9 - Mingle.send(:remove_const, :Card)  
10 - load File.join(Rails.root,'lib','issue_trackers','mingle.rb')  
11 - Mingle::Card.site = site  
12 - end  
13 -end  
14 -  
public/javascripts/form.js
@@ -71,8 +71,8 @@ function activateTypeSelector(field_class, section_class) { @@ -71,8 +71,8 @@ function activateTypeSelector(field_class, section_class) {
71 $('div.'+field_class+' > div.'+section_class).not('.chosen').find('input') 71 $('div.'+field_class+' > div.'+section_class).not('.chosen').find('input')
72 .attr('disabled','disabled').val(''); 72 .attr('disabled','disabled').val('');
73 73
74 - $('div.'+field_class+' input[name*='+field_class+'_type]').live('click', function(){  
75 - var chosen = $(this).val(); 74 + $('div.'+field_class+' input[name*=type]').live('click', function(){
  75 + var chosen = $(this).data("section");
76 var wrapper = $(this).closest('.nested'); 76 var wrapper = $(this).closest('.nested');
77 wrapper.find('div.chosen.'+section_class).removeClass('chosen').find('input').attr('disabled','disabled'); 77 wrapper.find('div.chosen.'+section_class).removeClass('chosen').find('input').attr('disabled','disabled');
78 wrapper.find('div.'+section_class+'.'+chosen).addClass('chosen').find('input').removeAttr('disabled'); 78 wrapper.find('div.'+section_class+'.'+chosen).addClass('chosen').find('input').removeAttr('disabled');
spec/controllers/apps_controller_spec.rb
@@ -263,25 +263,25 @@ describe AppsController do @@ -263,25 +263,25 @@ describe AppsController do
263 context "unknown tracker type" do 263 context "unknown tracker type" do
264 before(:each) do 264 before(:each) do
265 put :update, :id => @app.id, :app => { :issue_tracker_attributes => { 265 put :update, :id => @app.id, :app => { :issue_tracker_attributes => {
266 - :issue_tracker_type => 'unknown', :project_id => '1234', :api_token => '123123', :account => 'myapp' 266 + :type => 'unknown', :project_id => '1234', :api_token => '123123', :account => 'myapp'
267 } } 267 } }
268 @app.reload 268 @app.reload
269 end 269 end
270 270
271 it "should not create issue tracker" do 271 it "should not create issue tracker" do
272 - @app.issue_tracker.should be_nil 272 + @app.issue_tracker_configured?.should == false
273 end 273 end
274 end 274 end
275 275
276 context "lighthouseapp" do 276 context "lighthouseapp" do
277 it "should save tracker params" do 277 it "should save tracker params" do
278 put :update, :id => @app.id, :app => { :issue_tracker_attributes => { 278 put :update, :id => @app.id, :app => { :issue_tracker_attributes => {
279 - :issue_tracker_type => 'lighthouseapp', :project_id => '1234', :api_token => '123123', :account => 'myapp' 279 + :type => 'LighthouseTracker', :project_id => '1234', :api_token => '123123', :account => 'myapp'
280 } } 280 } }
281 @app.reload 281 @app.reload
282 282
283 tracker = @app.issue_tracker 283 tracker = @app.issue_tracker
284 - tracker.issue_tracker_type.should == 'lighthouseapp' 284 + tracker.should be_a(LighthouseTracker)
285 tracker.project_id.should == '1234' 285 tracker.project_id.should == '1234'
286 tracker.api_token.should == '123123' 286 tracker.api_token.should == '123123'
287 tracker.account.should == 'myapp' 287 tracker.account.should == 'myapp'
@@ -289,11 +289,11 @@ describe AppsController do @@ -289,11 +289,11 @@ describe AppsController do
289 289
290 it "should show validation notice when sufficient params are not present" do 290 it "should show validation notice when sufficient params are not present" do
291 put :update, :id => @app.id, :app => { :issue_tracker_attributes => { 291 put :update, :id => @app.id, :app => { :issue_tracker_attributes => {
292 - :issue_tracker_type => 'lighthouseapp', :project_id => '1234', :api_token => '123123' 292 + :type => 'LighthouseTracker', :project_id => '1234', :api_token => '123123'
293 } } 293 } }
294 @app.reload 294 @app.reload
295 295
296 - @app.issue_tracker.should be_nil 296 + @app.issue_tracker_configured?.should == false
297 response.body.should match(/You must specify your Lighthouseapp account, API token and Project ID/) 297 response.body.should match(/You must specify your Lighthouseapp account, API token and Project ID/)
298 end 298 end
299 end 299 end
@@ -301,12 +301,12 @@ describe AppsController do @@ -301,12 +301,12 @@ describe AppsController do
301 context "redmine" do 301 context "redmine" do
302 it "should save tracker params" do 302 it "should save tracker params" do
303 put :update, :id => @app.id, :app => { :issue_tracker_attributes => { 303 put :update, :id => @app.id, :app => { :issue_tracker_attributes => {
304 - :issue_tracker_type => 'redmine', :project_id => '1234', :api_token => '123123', :account => 'http://myapp.com' 304 + :type => 'RedmineTracker', :project_id => '1234', :api_token => '123123', :account => 'http://myapp.com'
305 } } 305 } }
306 @app.reload 306 @app.reload
307 307
308 tracker = @app.issue_tracker 308 tracker = @app.issue_tracker
309 - tracker.issue_tracker_type.should == 'redmine' 309 + tracker.should be_a(RedmineTracker)
310 tracker.project_id.should == '1234' 310 tracker.project_id.should == '1234'
311 tracker.api_token.should == '123123' 311 tracker.api_token.should == '123123'
312 tracker.account.should == 'http://myapp.com' 312 tracker.account.should == 'http://myapp.com'
@@ -314,11 +314,11 @@ describe AppsController do @@ -314,11 +314,11 @@ describe AppsController do
314 314
315 it "should show validation notice when sufficient params are not present" do 315 it "should show validation notice when sufficient params are not present" do
316 put :update, :id => @app.id, :app => { :issue_tracker_attributes => { 316 put :update, :id => @app.id, :app => { :issue_tracker_attributes => {
317 - :issue_tracker_type => 'redmine', :project_id => '1234', :api_token => '123123' 317 + :type => 'RedmineTracker', :project_id => '1234', :api_token => '123123'
318 } } 318 } }
319 @app.reload 319 @app.reload
320 320
321 - @app.issue_tracker.should be_nil 321 + @app.issue_tracker_configured?.should == false
322 response.body.should match(/You must specify your Redmine URL, API token and Project ID/) 322 response.body.should match(/You must specify your Redmine URL, API token and Project ID/)
323 end 323 end
324 end 324 end
@@ -326,21 +326,21 @@ describe AppsController do @@ -326,21 +326,21 @@ describe AppsController do
326 context "pivotal" do 326 context "pivotal" do
327 it "should save tracker params" do 327 it "should save tracker params" do
328 put :update, :id => @app.id, :app => { :issue_tracker_attributes => { 328 put :update, :id => @app.id, :app => { :issue_tracker_attributes => {
329 - :issue_tracker_type => 'pivotal', :project_id => '1234', :api_token => '123123' } } 329 + :type => 'PivotalLabsTracker', :project_id => '1234', :api_token => '123123' } }
330 @app.reload 330 @app.reload
331 331
332 tracker = @app.issue_tracker 332 tracker = @app.issue_tracker
333 - tracker.issue_tracker_type.should == 'pivotal' 333 + tracker.should be_a(PivotalLabsTracker)
334 tracker.project_id.should == '1234' 334 tracker.project_id.should == '1234'
335 tracker.api_token.should == '123123' 335 tracker.api_token.should == '123123'
336 end 336 end
337 337
338 it "should show validation notice when sufficient params are not present" do 338 it "should show validation notice when sufficient params are not present" do
339 put :update, :id => @app.id, :app => { :issue_tracker_attributes => { 339 put :update, :id => @app.id, :app => { :issue_tracker_attributes => {
340 - :issue_tracker_type => 'pivotal', :project_id => '1234' } } 340 + :type => 'PivotalLabsTracker', :project_id => '1234' } }
341 @app.reload 341 @app.reload
342 342
343 - @app.issue_tracker.should be_nil 343 + @app.issue_tracker_configured?.should == false
344 response.body.should match(/You must specify your Pivotal Tracker API token and Project ID/) 344 response.body.should match(/You must specify your Pivotal Tracker API token and Project ID/)
345 end 345 end
346 end 346 end
@@ -349,12 +349,12 @@ describe AppsController do @@ -349,12 +349,12 @@ describe AppsController do
349 context 'with correct params' do 349 context 'with correct params' do
350 before do 350 before do
351 put :update, :id => @app.id, :app => { :issue_tracker_attributes => { 351 put :update, :id => @app.id, :app => { :issue_tracker_attributes => {
352 - :issue_tracker_type => 'fogbugz', :account => 'abc', :project_id => 'Service - Peon', :username => '1234', :password => '123123' } } 352 + :type => 'FogbugzTracker', :account => 'abc', :project_id => 'Service - Peon', :username => '1234', :password => '123123' } }
353 @app.reload 353 @app.reload
354 end 354 end
355 355
356 subject {@app.issue_tracker} 356 subject {@app.issue_tracker}
357 - its(:issue_tracker_type) {should == 'fogbugz'} 357 + its(:type) {should == "FogbugzTracker"}
358 its(:account) {should == 'abc'} 358 its(:account) {should == 'abc'}
359 its(:project_id) {should == 'Service - Peon'} 359 its(:project_id) {should == 'Service - Peon'}
360 its(:username) {should == '1234'} 360 its(:username) {should == '1234'}
@@ -364,11 +364,11 @@ describe AppsController do @@ -364,11 +364,11 @@ describe AppsController do
364 context 'insufficient params' do 364 context 'insufficient params' do
365 it 'shows validation notice' do 365 it 'shows validation notice' do
366 put :update, :id => @app.id, :app => { :issue_tracker_attributes => { 366 put :update, :id => @app.id, :app => { :issue_tracker_attributes => {
367 - :issue_tracker_type => 'fogbugz', :project_id => '1234' } } 367 + :type => 'FogbugzTracker', :project_id => '1234' } }
368 @app.reload 368 @app.reload
369 369
370 - @app.issue_tracker.should be_nil  
371 - response.body.should match(/You must specify your FogBugz Area Name, Username, and Password/) 370 + @app.issue_tracker_configured?.should == false
  371 + response.body.should match(/You must specify your FogBugz Area Name, FogBugz URL, Username, and Password/)
372 end 372 end
373 end 373 end
374 end 374 end
@@ -377,14 +377,14 @@ describe AppsController do @@ -377,14 +377,14 @@ describe AppsController do
377 context 'with correct params' do 377 context 'with correct params' do
378 before do 378 before do
379 put :update, :id => @app.id, :app => { :issue_tracker_attributes => { 379 put :update, :id => @app.id, :app => { :issue_tracker_attributes => {
380 - :issue_tracker_type => 'mingle', :project_id => 'test', :account => 'http://mingle.example.com', 380 + :type => 'MingleTracker', :project_id => 'test', :account => 'http://mingle.example.com',
381 :username => '1234', :password => '123123', :ticket_properties => "card_type = Defect" 381 :username => '1234', :password => '123123', :ticket_properties => "card_type = Defect"
382 } } 382 } }
383 @app.reload 383 @app.reload
384 end 384 end
385 385
386 subject {@app.issue_tracker} 386 subject {@app.issue_tracker}
387 - its(:issue_tracker_type) {should == 'mingle'} 387 + its(:type) {should == "MingleTracker"}
388 its(:project_id) {should == 'test'} 388 its(:project_id) {should == 'test'}
389 its(:username) {should == '1234'} 389 its(:username) {should == '1234'}
390 its(:password) {should == '123123'} 390 its(:password) {should == '123123'}
@@ -392,12 +392,12 @@ describe AppsController do @@ -392,12 +392,12 @@ describe AppsController do
392 392
393 it "should show validation notice when sufficient params are not present" do 393 it "should show validation notice when sufficient params are not present" do
394 put :update, :id => @app.id, :app => { :issue_tracker_attributes => { 394 put :update, :id => @app.id, :app => { :issue_tracker_attributes => {
395 - :issue_tracker_type => 'mingle', :project_id => 'test', :account => 'http://mingle.example.com', 395 + :type => 'MingleTracker', :project_id => 'test', :account => 'http://mingle.example.com',
396 :username => '1234', :password => '1234', :ticket_properties => "cards_type = Defect" 396 :username => '1234', :password => '1234', :ticket_properties => "cards_type = Defect"
397 } } 397 } }
398 @app.reload 398 @app.reload
399 399
400 - @app.issue_tracker.should be_nil 400 + @app.issue_tracker_configured?.should == false
401 response.body.should match(/You must specify your Mingle URL, Project ID, Card Type \(in default card properties\), Sign-in name, and Password/) 401 response.body.should match(/You must specify your Mingle URL, Project ID, Card Type \(in default card properties\), Sign-in name, and Password/)
402 end 402 end
403 end 403 end
spec/controllers/errs_controller_spec.rb
@@ -173,7 +173,7 @@ describe ErrsController do @@ -173,7 +173,7 @@ describe ErrsController do
173 end 173 end
174 174
175 it "should exist for err's app with issue tracker" do 175 it "should exist for err's app with issue tracker" do
176 - tracker = Factory(:lighthouseapp_tracker) 176 + tracker = Factory(:lighthouse_tracker)
177 err = Factory(:err, :app => tracker.app) 177 err = Factory(:err, :app => tracker.app)
178 get :show, :app_id => err.app.id, :id => err.id 178 get :show, :app_id => err.app.id, :id => err.id
179 179
@@ -181,7 +181,7 @@ describe ErrsController do @@ -181,7 +181,7 @@ describe ErrsController do
181 end 181 end
182 182
183 it "should not exist for err with issue_link" do 183 it "should not exist for err with issue_link" do
184 - tracker = Factory(:lighthouseapp_tracker) 184 + tracker = Factory(:lighthouse_tracker)
185 err = Factory(:err, :app => tracker.app, :issue_link => "http://some.host") 185 err = Factory(:err, :app => tracker.app, :issue_link => "http://some.host")
186 get :show, :app_id => err.app.id, :id => err.id 186 get :show, :app_id => err.app.id, :id => err.id
187 187
@@ -262,7 +262,7 @@ describe ErrsController do @@ -262,7 +262,7 @@ describe ErrsController do
262 context "successful issue creation" do 262 context "successful issue creation" do
263 context "lighthouseapp tracker" do 263 context "lighthouseapp tracker" do
264 let(:notice) { Factory :notice } 264 let(:notice) { Factory :notice }
265 - let(:tracker) { Factory :lighthouseapp_tracker, :app => notice.err.app } 265 + let(:tracker) { Factory :lighthouse_tracker, :app => notice.err.app }
266 let(:err) { notice.err } 266 let(:err) { notice.err }
267 267
268 before(:each) do 268 before(:each) do
@@ -326,7 +326,7 @@ describe ErrsController do @@ -326,7 +326,7 @@ describe ErrsController do
326 326
327 context "pivotal tracker" do 327 context "pivotal tracker" do
328 let(:notice) { Factory :notice } 328 let(:notice) { Factory :notice }
329 - let(:tracker) { Factory :pivotal_tracker, :app => notice.err.app, :project_id => 10 } 329 + let(:tracker) { Factory :pivotal_labs_tracker, :app => notice.err.app, :project_id => 10 }
330 let(:err) { notice.err } 330 let(:err) { notice.err }
331 331
332 before(:each) do 332 before(:each) do
@@ -370,7 +370,7 @@ describe ErrsController do @@ -370,7 +370,7 @@ describe ErrsController do
370 before(:each) do 370 before(:each) do
371 number = 5 371 number = 5
372 @issue_link = "#{tracker.account}/projects/#{tracker.project_id}/cards/#{number}.xml" 372 @issue_link = "#{tracker.account}/projects/#{tracker.project_id}/cards/#{number}.xml"
373 - @basic_auth = tracker.account.gsub("https://", "https://#{tracker.username}:#{tracker.password}@") 373 + @basic_auth = tracker.account.gsub("://", "://#{tracker.username}:#{tracker.password}@")
374 body = "<card><id type=\"integer\">#{number}</id></card>" 374 body = "<card><id type=\"integer\">#{number}</id></card>"
375 stub_request(:post, "#{@basic_auth}/api/v1/projects/#{tracker.project_id}/cards.xml"). 375 stub_request(:post, "#{@basic_auth}/api/v1/projects/#{tracker.project_id}/cards.xml").
376 to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) 376 to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body )
@@ -415,7 +415,7 @@ describe ErrsController do @@ -415,7 +415,7 @@ describe ErrsController do
415 415
416 context "error during request to a tracker" do 416 context "error during request to a tracker" do
417 context "lighthouseapp tracker" do 417 context "lighthouseapp tracker" do
418 - let(:tracker) { Factory :lighthouseapp_tracker } 418 + let(:tracker) { Factory :lighthouse_tracker }
419 let(:err) { Factory :err, :app => tracker.app } 419 let(:err) { Factory :err, :app => tracker.app }
420 420
421 before(:each) do 421 before(:each) do
spec/factories/issue_tracker_factories.rb
1 -Factory.define :generic_tracker, :class => IssueTracker do |e| 1 +Factory.define :issue_tracker do |e|
2 e.api_token { Factory.next :word } 2 e.api_token { Factory.next :word }
3 e.project_id { Factory.next :word } 3 e.project_id { Factory.next :word }
4 e.association :app, :factory => :app 4 e.association :app, :factory => :app
5 -end  
6 -  
7 -Factory.define :lighthouseapp_tracker, :parent => :generic_tracker do |e|  
8 - e.issue_tracker_type 'lighthouseapp'  
9 e.account { Factory.next :word } 5 e.account { Factory.next :word }
  6 + e.username { Factory.next :word }
  7 + e.password { Factory.next :word }
10 end 8 end
11 9
12 -Factory.define :redmine_tracker, :parent => :generic_tracker do |e|  
13 - e.issue_tracker_type 'redmine'  
14 - e.account { "http://#{Factory.next(:word)}.com" } 10 +%w(lighthouse pivotal_labs fogbugz).each do |t|
  11 + Factory.define "#{t}_tracker".to_sym, :parent => :issue_tracker, :class => "#{t}_tracker".to_sym do |e|; end
15 end 12 end
16 13
17 -Factory.define :pivotal_tracker, :parent => :generic_tracker do |e|  
18 - e.issue_tracker_type 'pivotal' 14 +Factory.define :redmine_tracker, :parent => :issue_tracker, :class => :redmine_tracker do |e|
  15 + e.account 'http://redmine.example.com'
19 end 16 end
20 17
21 -Factory.define :mingle_tracker, :parent => :generic_tracker do |t|  
22 - t.issue_tracker_type 'mingle'  
23 - t.account "https://mingle.example.com"  
24 - t.ticket_properties 'card_type = Defect, defect_status = open, priority = essential'  
25 - t.username "test_user"  
26 - t.password "test_password" 18 +Factory.define :mingle_tracker, :parent => :issue_tracker, :class => :mingle_tracker do |e|
  19 + e.account 'https://mingle.example.com'
  20 + e.ticket_properties 'card_type = Defect, defect_status = open, priority = essential'
27 end 21 end
28 22
spec/views/errs/show.html.haml_spec.rb
@@ -51,7 +51,7 @@ describe &quot;errs/show.html.erb&quot; do @@ -51,7 +51,7 @@ describe &quot;errs/show.html.erb&quot; do
51 51
52 context "with issue tracker" do 52 context "with issue tracker" do
53 def with_issue_tracker(err) 53 def with_issue_tracker(err)
54 - err.app.issue_tracker = IssueTracker.new :issue_tracker_type => "lighthouseapp", :project_id => "1234" 54 + err.app.issue_tracker = PivotalLabsTracker.new :api_token => "token token token", :project_id => "1234"
55 assign :err, err 55 assign :err, err
56 assign :app, err.app 56 assign :app, err.app
57 end 57 end