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 24 end
25 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 39 def new
28 40 plug_params build_resource
29 41 new!
... ... @@ -34,7 +46,16 @@ class AppsController < InheritedResources::Base
34 46 edit!
35 47 end
36 48  
  49 +
37 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 59 def begin_of_association_chain
39 60 current_user unless current_user.admin?
40 61 end
... ... @@ -45,7 +66,7 @@ class AppsController < InheritedResources::Base
45 66  
46 67 def plug_params app
47 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 70 end
50 71  
51 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 29 tallies.values.inject(0) {|sum, n| sum + n}
30 30 end
31 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 32 end
44 33  
... ...
app/models/app.rb
... ... @@ -11,7 +11,7 @@ class App
11 11 field :notify_on_deploys, :type => Boolean, :default => true
12 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 15 identity :type => String
16 16 # There seems to be a Mongoid bug making it impossible to use String identity with references_many feature:
17 17 # https://github.com/mongoid/mongoid/issues/703
... ... @@ -37,7 +37,7 @@ class App
37 37 accepts_nested_attributes_for :watchers, :allow_destroy => true,
38 38 :reject_if => proc { |attrs| attrs[:user_id].blank? && attrs[:email].blank? }
39 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 42 def self.find_by_id!(app_id)
43 43 find app_id
... ... @@ -71,7 +71,7 @@ class App
71 71 end
72 72  
73 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 75 end
76 76  
77 77 def notification_recipients
... ...
app/models/issue_tracker.rb
... ... @@ -5,171 +5,28 @@ class IssueTracker
5 5 include Rails.application.routes.url_helpers
6 6 default_url_options[:host] = Errbit::Application.config.action_mailer.default_url_options[:host]
7 7  
8   - validate :check_params
9   -
10 8 embedded_in :app, :inverse_of => :issue_tracker
11 9  
12   - field :account, :type => String
13   - field :api_token, :type => String
14 10 field :project_id, :type => String
15   - field :ticket_properties, :type => String
  11 + field :api_token, :type => String
  12 + field :account, :type => String
16 13 field :username, :type => String
17 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 22 def issue_title err
117 23 "[#{ err.environment }][#{ err.where }] #{err.message.to_s.truncate(100)}"
118 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 31 end
175 32  
... ...
app/models/issue_trackers/fogbugz_tracker.rb 0 → 100644
... ... @@ -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 @@
  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 @@
  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 @@
  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 @@
  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 50 = f.fields_for :issue_tracker do |w|
51 51 %div.issue_tracker.nested
52 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 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 68 = w.label :account, "Account"
69 69 = w.text_field :account, :placeholder => "abc from abc.lighthouseapp.com"
70 70 = w.label :api_token, "API token"
71 71 = w.text_field :api_token, :placeholder => "API Token for your account"
72 72 = w.label :project_id, "Project ID"
73 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 75 = w.label :account, "Redmine URL"
76 76 = w.text_field :account, :placeholder => "like http://www.redmine.org/"
77 77 = w.label :api_token, "API token"
78 78 = w.text_field :api_token, :placeholder => "API Token for your account"
79 79 = w.label :project_id, "Project ID"
80 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 82 = w.label :project_id, "Project ID"
83 83 = w.text_field :project_id
84 84 = w.label :api_token, "API token"
85 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 87 = w.label :project_id, "Area Name"
88 88 = w.text_field :project_id
89 89 = w.label :account, "FogBugz URL"
... ... @@ -92,7 +92,7 @@
92 92 = w.text_field :username, :placeholder => 'Username/Email for your account'
93 93 = w.label :password, 'Password'
94 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 96 = w.label :account, "Mingle URL"
97 97 = w.text_field :account, :placeholder => "http://mingle.yoursite.com/"
98 98 = w.label :project_id, "Project ID"
... ...
app/views/errs/fogbugz_body.txt.erb
... ... @@ -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   -[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   -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   -<% 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 13 - content_for :action_bar do
14 14 - if @err.app.issue_tracker_configured?
15 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 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 19 = link_to 'unlink issue', unlink_issue_app_err_path(@app, @err), :method => :delete, :confirm => "Unlink err issues?", :class => "unlink-issue"
20 20 - if @err.unresolved?
21 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 @@
  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 @@
  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 @@
  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 @@
  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 19 # -- all .rb files in that directory are automatically loaded.
20 20  
21 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 24 # Only load the plugins named here, in the order given (default is alphabetical).
25 25 # :all can be used as a placeholder for all plugins not explicitly named.
... ... @@ -53,3 +53,4 @@ module Errbit
53 53 config.filter_parameters += [:password]
54 54 end
55 55 end
  56 +
... ...
config/initializers/issue_tracker_apis.rb 0 → 100644
... ... @@ -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   -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 @@
  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 @@
  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   -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 71 $('div.'+field_class+' > div.'+section_class).not('.chosen').find('input')
72 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 76 var wrapper = $(this).closest('.nested');
77 77 wrapper.find('div.chosen.'+section_class).removeClass('chosen').find('input').attr('disabled','disabled');
78 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 263 context "unknown tracker type" do
264 264 before(:each) do
265 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 268 @app.reload
269 269 end
270 270  
271 271 it "should not create issue tracker" do
272   - @app.issue_tracker.should be_nil
  272 + @app.issue_tracker_configured?.should == false
273 273 end
274 274 end
275 275  
276 276 context "lighthouseapp" do
277 277 it "should save tracker params" do
278 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 281 @app.reload
282 282  
283 283 tracker = @app.issue_tracker
284   - tracker.issue_tracker_type.should == 'lighthouseapp'
  284 + tracker.should be_a(LighthouseTracker)
285 285 tracker.project_id.should == '1234'
286 286 tracker.api_token.should == '123123'
287 287 tracker.account.should == 'myapp'
... ... @@ -289,11 +289,11 @@ describe AppsController do
289 289  
290 290 it "should show validation notice when sufficient params are not present" do
291 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 294 @app.reload
295 295  
296   - @app.issue_tracker.should be_nil
  296 + @app.issue_tracker_configured?.should == false
297 297 response.body.should match(/You must specify your Lighthouseapp account, API token and Project ID/)
298 298 end
299 299 end
... ... @@ -301,12 +301,12 @@ describe AppsController do
301 301 context "redmine" do
302 302 it "should save tracker params" do
303 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 306 @app.reload
307 307  
308 308 tracker = @app.issue_tracker
309   - tracker.issue_tracker_type.should == 'redmine'
  309 + tracker.should be_a(RedmineTracker)
310 310 tracker.project_id.should == '1234'
311 311 tracker.api_token.should == '123123'
312 312 tracker.account.should == 'http://myapp.com'
... ... @@ -314,11 +314,11 @@ describe AppsController do
314 314  
315 315 it "should show validation notice when sufficient params are not present" do
316 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 319 @app.reload
320 320  
321   - @app.issue_tracker.should be_nil
  321 + @app.issue_tracker_configured?.should == false
322 322 response.body.should match(/You must specify your Redmine URL, API token and Project ID/)
323 323 end
324 324 end
... ... @@ -326,21 +326,21 @@ describe AppsController do
326 326 context "pivotal" do
327 327 it "should save tracker params" do
328 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 330 @app.reload
331 331  
332 332 tracker = @app.issue_tracker
333   - tracker.issue_tracker_type.should == 'pivotal'
  333 + tracker.should be_a(PivotalLabsTracker)
334 334 tracker.project_id.should == '1234'
335 335 tracker.api_token.should == '123123'
336 336 end
337 337  
338 338 it "should show validation notice when sufficient params are not present" do
339 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 341 @app.reload
342 342  
343   - @app.issue_tracker.should be_nil
  343 + @app.issue_tracker_configured?.should == false
344 344 response.body.should match(/You must specify your Pivotal Tracker API token and Project ID/)
345 345 end
346 346 end
... ... @@ -349,12 +349,12 @@ describe AppsController do
349 349 context 'with correct params' do
350 350 before do
351 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 353 @app.reload
354 354 end
355 355  
356 356 subject {@app.issue_tracker}
357   - its(:issue_tracker_type) {should == 'fogbugz'}
  357 + its(:type) {should == "FogbugzTracker"}
358 358 its(:account) {should == 'abc'}
359 359 its(:project_id) {should == 'Service - Peon'}
360 360 its(:username) {should == '1234'}
... ... @@ -364,11 +364,11 @@ describe AppsController do
364 364 context 'insufficient params' do
365 365 it 'shows validation notice' do
366 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 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 372 end
373 373 end
374 374 end
... ... @@ -377,14 +377,14 @@ describe AppsController do
377 377 context 'with correct params' do
378 378 before do
379 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 381 :username => '1234', :password => '123123', :ticket_properties => "card_type = Defect"
382 382 } }
383 383 @app.reload
384 384 end
385 385  
386 386 subject {@app.issue_tracker}
387   - its(:issue_tracker_type) {should == 'mingle'}
  387 + its(:type) {should == "MingleTracker"}
388 388 its(:project_id) {should == 'test'}
389 389 its(:username) {should == '1234'}
390 390 its(:password) {should == '123123'}
... ... @@ -392,12 +392,12 @@ describe AppsController do
392 392  
393 393 it "should show validation notice when sufficient params are not present" do
394 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 396 :username => '1234', :password => '1234', :ticket_properties => "cards_type = Defect"
397 397 } }
398 398 @app.reload
399 399  
400   - @app.issue_tracker.should be_nil
  400 + @app.issue_tracker_configured?.should == false
401 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 402 end
403 403 end
... ...
spec/controllers/errs_controller_spec.rb
... ... @@ -173,7 +173,7 @@ describe ErrsController do
173 173 end
174 174  
175 175 it "should exist for err's app with issue tracker" do
176   - tracker = Factory(:lighthouseapp_tracker)
  176 + tracker = Factory(:lighthouse_tracker)
177 177 err = Factory(:err, :app => tracker.app)
178 178 get :show, :app_id => err.app.id, :id => err.id
179 179  
... ... @@ -181,7 +181,7 @@ describe ErrsController do
181 181 end
182 182  
183 183 it "should not exist for err with issue_link" do
184   - tracker = Factory(:lighthouseapp_tracker)
  184 + tracker = Factory(:lighthouse_tracker)
185 185 err = Factory(:err, :app => tracker.app, :issue_link => "http://some.host")
186 186 get :show, :app_id => err.app.id, :id => err.id
187 187  
... ... @@ -262,7 +262,7 @@ describe ErrsController do
262 262 context "successful issue creation" do
263 263 context "lighthouseapp tracker" do
264 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 266 let(:err) { notice.err }
267 267  
268 268 before(:each) do
... ... @@ -326,7 +326,7 @@ describe ErrsController do
326 326  
327 327 context "pivotal tracker" do
328 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 330 let(:err) { notice.err }
331 331  
332 332 before(:each) do
... ... @@ -370,7 +370,7 @@ describe ErrsController do
370 370 before(:each) do
371 371 number = 5
372 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 374 body = "<card><id type=\"integer\">#{number}</id></card>"
375 375 stub_request(:post, "#{@basic_auth}/api/v1/projects/#{tracker.project_id}/cards.xml").
376 376 to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body )
... ... @@ -415,7 +415,7 @@ describe ErrsController do
415 415  
416 416 context "error during request to a tracker" do
417 417 context "lighthouseapp tracker" do
418   - let(:tracker) { Factory :lighthouseapp_tracker }
  418 + let(:tracker) { Factory :lighthouse_tracker }
419 419 let(:err) { Factory :err, :app => tracker.app }
420 420  
421 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 2 e.api_token { Factory.next :word }
3 3 e.project_id { Factory.next :word }
4 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 5 e.account { Factory.next :word }
  6 + e.username { Factory.next :word }
  7 + e.password { Factory.next :word }
10 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 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 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 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 51  
52 52 context "with issue tracker" do
53 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 55 assign :err, err
56 56 assign :app, err.app
57 57 end
... ...