Commit 889af62f910ad4d4ea4f1ef0d196c0f0983ee48d

Authored by Cyril Mougel
1 parent ed78ea13
Exists in master and in 1 other branch production

Refactor the Issue Tracker forms in App

@@ -25,6 +25,7 @@ gem 'rails_autolink' @@ -25,6 +25,7 @@ gem 'rails_autolink'
25 # Please don't update hoptoad_notifier to airbrake. 25 # Please don't update hoptoad_notifier to airbrake.
26 # It's for internal use only, and we monkeypatch certain methods 26 # It's for internal use only, and we monkeypatch certain methods
27 gem 'hoptoad_notifier', "~> 2.4" 27 gem 'hoptoad_notifier', "~> 2.4"
  28 +gem 'draper', :require => false
28 29
29 30
30 # Remove / comment out any of the gems below if you want to disable 31 # Remove / comment out any of the gems below if you want to disable
@@ -95,6 +95,10 @@ GEM @@ -95,6 +95,10 @@ GEM
95 warden (~> 1.2.3) 95 warden (~> 1.2.3)
96 diff-lcs (1.2.4) 96 diff-lcs (1.2.4)
97 dotenv (0.9.0) 97 dotenv (0.9.0)
  98 + draper (1.2.1)
  99 + actionpack (>= 3.0)
  100 + activesupport (>= 3.0)
  101 + request_store (~> 1.0.3)
98 email_spec (1.5.0) 102 email_spec (1.5.0)
99 launchy (~> 2.1) 103 launchy (~> 2.1)
100 mail (~> 2.2) 104 mail (~> 2.2)
@@ -282,6 +286,7 @@ GEM @@ -282,6 +286,7 @@ GEM
282 rdoc (3.12.2) 286 rdoc (3.12.2)
283 json (~> 1.4) 287 json (~> 1.4)
284 ref (1.0.5) 288 ref (1.0.5)
  289 + request_store (1.0.5)
285 rest-client (1.6.7) 290 rest-client (1.6.7)
286 mime-types (>= 1.16) 291 mime-types (>= 1.16)
287 ri_cal (0.8.8) 292 ri_cal (0.8.8)
@@ -382,6 +387,7 @@ DEPENDENCIES @@ -382,6 +387,7 @@ DEPENDENCIES
382 debugger 387 debugger
383 decent_exposure 388 decent_exposure
384 devise 389 devise
  390 + draper
385 email_spec 391 email_spec
386 execjs 392 execjs
387 fabrication 393 fabrication
app/controllers/apps_controller.rb
@@ -16,6 +16,9 @@ class AppsController < ApplicationController @@ -16,6 +16,9 @@ class AppsController < ApplicationController
16 } 16 }
17 17
18 expose(:app, :ancestor => :app_scope) 18 expose(:app, :ancestor => :app_scope)
  19 + expose(:app_decorate) do
  20 + AppDecorator.new(app)
  21 + end
19 22
20 expose(:all_errs) { 23 expose(:all_errs) {
21 !!params[:all_errs] 24 !!params[:all_errs]
app/decorators/app_decorator.rb 0 → 100644
@@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
  1 +class AppDecorator < Draper::Decorator
  2 +
  3 + decorates_association :watchers
  4 + decorates_association :issue_tracker, :with => IssueTrackerDecorator
  5 + delegate_all
  6 +
  7 + def email_at_notices
  8 + object.email_at_notices.join(', ')
  9 + end
  10 +
  11 + def notify_user_display
  12 + object.notify_all_users ? 'display: none;' : ''
  13 + end
  14 +
  15 + def notify_err_display
  16 + object.notify_on_errs ? '' : 'display: none;'
  17 + end
  18 +
  19 +end
app/decorators/issue_tracker_decorator.rb 0 → 100644
@@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
  1 +class IssueTrackerDecorator < Draper::Decorator
  2 +
  3 + delegate_all
  4 +
  5 + def issue_trackers
  6 + @issue_trackers ||= [
  7 + IssueTracker,
  8 + IssueTracker.subclasses
  9 + ].flatten
  10 + @issue_trackers.each do |it|
  11 + yield IssueTrackerDecorator.new(it)
  12 + end
  13 + end
  14 +
  15 + def note
  16 + object::Note.html_safe
  17 + end
  18 +
  19 + def fields
  20 + object::Fields.each do |field, field_info|
  21 + yield IssueTrackerFieldDecorator.new(field, field_info)
  22 + end
  23 + end
  24 +
  25 + def params_class(tracker)
  26 + [choosen?(tracker), label].join(" ").strip
  27 + end
  28 +
  29 + private
  30 +
  31 + def choosen?(issue_tracker)
  32 + object.to_s == issue_tracker._type ? 'chosen' : ''
  33 + end
  34 +
  35 +end
app/decorators/issue_tracker_field_decorator.rb 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +class IssueTrackerFieldDecorator < Draper::Decorator
  2 +
  3 + def initialize(field, field_info)
  4 + @object = field
  5 + @field_info = field_info
  6 + end
  7 + attr_reader :object, :field_info
  8 +
  9 + alias :key :object
  10 +
  11 + def label
  12 + field_info[:label] || object.to_s.titleize
  13 + end
  14 +
  15 +
  16 + def input(form)
  17 + form.send(input_field, object,
  18 + :placeholder => field_info[:placeholder],
  19 + :value => form.object.send(object))
  20 + end
  21 +
  22 + private
  23 +
  24 + def input_field
  25 + object == :password ? :password_field : :text_field
  26 + end
  27 +end
app/decorators/watcher_decorator.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class WatcherDecorator < Draper::Decorator
  2 +
  3 + delegate_all
  4 +
  5 + def email_choosen
  6 + object.email.blank? ? 'chosen' : ''
  7 + end
  8 +
  9 +end
app/models/issue_tracker.rb
@@ -3,6 +3,11 @@ class IssueTracker @@ -3,6 +3,11 @@ class IssueTracker
3 include Mongoid::Timestamps 3 include Mongoid::Timestamps
4 include HashHelper 4 include HashHelper
5 include Rails.application.routes.url_helpers 5 include Rails.application.routes.url_helpers
  6 +
  7 + Fields = []
  8 + Note = 'When no issue tracker has been configured, you will be able to leave comments on errors.'
  9 + Label = 'none'
  10 +
6 default_url_options[:host] = ActionMailer::Base.default_url_options[:host] 11 default_url_options[:host] = ActionMailer::Base.default_url_options[:host]
7 12
8 embedded_in :app, :inverse_of => :issue_tracker 13 embedded_in :app, :inverse_of => :issue_tracker
@@ -41,7 +46,6 @@ class IssueTracker @@ -41,7 +46,6 @@ class IssueTracker
41 def url; nil; end 46 def url; nil; end
42 47
43 # Retrieve tracker label from either class or instance. 48 # Retrieve tracker label from either class or instance.
44 - Label = ''  
45 def self.label; self::Label; end 49 def self.label; self::Label; end
46 def label; self.class.label; end 50 def label; self.class.label; end
47 51
app/views/apps/_fields.html.haml
@@ -29,9 +29,9 @@ @@ -29,9 +29,9 @@
29 = f.check_box :notify_on_errs, 'data-show-when-checked' => '.email_at_notices_nested' 29 = f.check_box :notify_on_errs, 'data-show-when-checked' => '.email_at_notices_nested'
30 = f.label :notify_on_errs, 'Notify on errors' 30 = f.label :notify_on_errs, 'Notify on errors'
31 - if Errbit::Config.per_app_email_at_notices 31 - if Errbit::Config.per_app_email_at_notices
32 - %div.email_at_notices_nested{:style => f.object.notify_on_errs ? '' : 'display: none;'} 32 + %div.email_at_notices_nested{:style => app_decorate.notify_err_display}
33 .field-helpertext Send a notification every 33 .field-helpertext Send a notification every
34 - = f.text_field :email_at_notices, :value => f.object.email_at_notices.join(", ") 34 + = f.text_field :email_at_notices, :value => app_decorate.email_at_notices
35 .field-helpertext times an error occurs (comma separated). 35 .field-helpertext times an error occurs (comma separated).
36 %div.checkbox 36 %div.checkbox
37 = f.check_box :notify_on_deploys 37 = f.check_box :notify_on_deploys
@@ -42,16 +42,16 @@ @@ -42,16 +42,16 @@
42 = f.label :notify_all_users, 'Send notifications to all users' 42 = f.label :notify_all_users, 'Send notifications to all users'
43 43
44 44
45 -%fieldset.watchers.nested-wrapper{:style => f.object.notify_all_users ? 'display: none;' : ''} 45 +%fieldset.watchers.nested-wrapper{:style => app_decorate.notify_user_display}
46 %legend Watchers 46 %legend Watchers
47 = f.fields_for :watchers do |w| 47 = f.fields_for :watchers do |w|
48 %div.watcher.nested 48 %div.watcher.nested
49 %div.choose 49 %div.choose
50 = w.radio_button :watcher_type, :user 50 = w.radio_button :watcher_type, :user
51 - = label_tag :watcher_type_user, 'User', :for => label_for_attr(w, 'watcher_type_user') 51 + = w.label :watcher_type_user, 'User'
52 = w.radio_button :watcher_type, :email 52 = w.radio_button :watcher_type, :email
53 - = label_tag :watcher_type_email, 'Email Address', :for => label_for_attr(w, 'watcher_type_email')  
54 - %div.watcher_params.user{:class => w.object.email.blank? ? 'chosen' : nil} 53 + = w.label :watcher_type_email, 'Email Address'
  54 + %div.watcher_params.user{:class => w.object.email_choosen}
55 = w.select :user_id, User.all.map{|u| [u.name,u.id.to_s]}, :include_blank => '-- Select a User --' 55 = w.select :user_id, User.all.map{|u| [u.name,u.id.to_s]}, :include_blank => '-- Select a User --'
56 %div.watcher_params.email{:class => w.object.email.present? ? 'chosen' : nil} 56 %div.watcher_params.email{:class => w.object.email.present? ? 'chosen' : nil}
57 = w.text_field :email 57 = w.text_field :email
app/views/apps/_issue_tracker_fields.html.haml
1 %fieldset 1 %fieldset
2 - %legend Issue tracker 2 + %legend= t('.legend')
3 = f.fields_for :issue_tracker do |w| 3 = f.fields_for :issue_tracker do |w|
4 %div.issue_tracker.nested 4 %div.issue_tracker.nested
5 %div.choose 5 %div.choose
6 - = label_tag :type_none, :for => label_for_attr(w, 'type_issuetracker'), :class => "label_radio none" do  
7 - = w.radio_button :type, "IssueTracker", 'data-section' => 'none'  
8 - (None)  
9 - - IssueTracker.subclasses.each do |tracker|  
10 - = label_tag "type_#{tracker.label}:", :for => label_for_attr(w, "type_#{tracker.name.downcase.gsub(':','')}"), :class => "label_radio #{tracker.label}" do 6 + - w.object.issue_trackers do |tracker|
  7 + = w.label :type, :class => "label_radio #{tracker.label}", :value => tracker.name do
11 = w.radio_button :type, tracker.name, 'data-section' => tracker.label 8 = w.radio_button :type, tracker.name, 'data-section' => tracker.label
12 - = tracker.name[/::(.*)Tracker/,1].titleize 9 + = tracker.label
13 10
14 - %div.tracker_params.none{:class => (w.object && !(w.object.class < IssueTracker)) ? 'chosen' : nil}  
15 - %p When no issue tracker has been configured, you will be able to leave comments on errors.  
16 - - IssueTracker.subclasses.each do |tracker|  
17 - %div.tracker_params{:class => (w.object.is_a?(tracker) ? 'chosen ' : '') << tracker.label}  
18 - - if defined?(tracker::Note)  
19 - %p= tracker::Note.html_safe  
20 - - tracker::Fields.each do |field, field_info|  
21 - = w.label field, field_info[:label] || field.to_s.titleize  
22 - - field_type = field == :password ? :password_field : :text_field  
23 - = w.send field_type, field, :placeholder => field_info[:placeholder], :value => w.object.send(field) 11 + - w.object.issue_trackers do |tracker|
  12 + %div.tracker_params{:class => tracker.params_class(w.object)}
  13 + %p= tracker.note
  14 + - tracker.fields do |field|
  15 + = w.label field.key, field.label
  16 + = field.input(w)
24 17
25 .image_preloader 18 .image_preloader
26 - - (IssueTracker.subclasses.map{|t| t.label } << 'none').each do |tracker|  
27 - = image_tag "#{tracker}_inactive.png"  
28 - = image_tag "#{tracker}_create.png" 19 + - w.object.issue_trackers do |tracker|
  20 + = image_tag "#{tracker.label}_inactive.png"
  21 + = image_tag "#{tracker.label}_create.png"
29 22
app/views/apps/edit.html.haml
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 :data => { :confirm => t('apps.confirm_delete') }, :class => 'button' 5 :data => { :confirm => t('apps.confirm_delete') }, :class => 'button'
6 = link_to('cancel', app_path(app), :class => 'button') 6 = link_to('cancel', app_path(app), :class => 'button')
7 7
8 -= form_for app do |f| 8 += form_for app_decorate do |f|
9 9
10 = render 'fields', :f => f 10 = render 'fields', :f => f
11 11
app/views/apps/new.html.haml
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 = link_to_copy_attributes_from_other_app 3 = link_to_copy_attributes_from_other_app
4 = link_to('cancel', apps_path, :class => 'button') 4 = link_to('cancel', apps_path, :class => 'button')
5 5
6 -= form_for app do |f| 6 += form_for app_decorate do |f|
7 7
8 = render 'fields', :f => f 8 = render 'fields', :f => f
9 9
config/application.rb
@@ -9,6 +9,8 @@ require &quot;action_mailer/railtie&quot; @@ -9,6 +9,8 @@ require &quot;action_mailer/railtie&quot;
9 require 'mongoid/railtie' 9 require 'mongoid/railtie'
10 require "sprockets/railtie" 10 require "sprockets/railtie"
11 11
  12 +require 'draper'
  13 +
12 if defined?(Bundler) 14 if defined?(Bundler)
13 # If you precompile assets before deploying to production, use this line 15 # If you precompile assets before deploying to production, use this line
14 Bundler.require(*Rails.groups(:assets => %w(development test))) 16 Bundler.require(*Rails.groups(:assets => %w(development test)))
config/locales/en.yml
@@ -125,4 +125,6 @@ en: @@ -125,4 +125,6 @@ en:
125 watchers: Watchers 125 watchers: Watchers
126 when: When 126 when: When
127 who: Who 127 who: Who
  128 + issue_tracker_fields:
  129 + legend: Issue Tracker
128 130
spec/acceptance/app_regenerate_api_key_spec.rb
@@ -28,5 +28,4 @@ feature &quot;Regeneration api_Key&quot; do @@ -28,5 +28,4 @@ feature &quot;Regeneration api_Key&quot; do
28 click_link app.name if page.current_url != app_url(app) 28 click_link app.name if page.current_url != app_url(app)
29 expect(page).to_not have_button I18n.t('apps.show.edit') 29 expect(page).to_not have_button I18n.t('apps.show.edit')
30 end 30 end
31 -  
32 end 31 end
spec/decorators/app_decorator_spec.rb 0 → 100644
@@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
  1 +require 'spec_helper'
  2 +
  3 +describe AppDecorator do
  4 +
  5 + describe "#email_at_notices" do
  6 +
  7 + it 'return the list separate by comma' do
  8 + expect(AppDecorator.new(double(:email_at_notices => [2,3])).email_at_notices).to eql '2, 3'
  9 + end
  10 +
  11 + end
  12 +
  13 + describe "#notify_user_display" do
  14 +
  15 + it 'return display:none if notify' do
  16 + expect(AppDecorator.new(double(:notify_all_users => true)).notify_user_display).to eql 'display: none;'
  17 + end
  18 +
  19 + it 'return blank if no notify' do
  20 + expect(AppDecorator.new(double(:notify_all_users => false)).notify_user_display).to eql ''
  21 + end
  22 +
  23 + end
  24 +
  25 + describe "#notify_err_display" do
  26 +
  27 + it 'return display:none if no notify' do
  28 + expect(AppDecorator.new(double(:notify_on_errs => false)).notify_err_display).to eql 'display: none;'
  29 + end
  30 +
  31 + it 'return blank if no notify' do
  32 + expect(AppDecorator.new(double(:notify_on_errs => true)).notify_err_display).to eql ''
  33 + end
  34 +
  35 + end
  36 +
  37 +end
spec/decorators/issue_tracker_decorator_spec.rb 0 → 100644
@@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
  1 +require 'spec_helper'
  2 +
  3 +describe IssueTrackerDecorator do
  4 +
  5 + class Foo
  6 + Note = 'hello <strong></strong>'
  7 + Fields = [
  8 + [:foo, :bar]
  9 + ]
  10 + Label = 'foo'
  11 + def self.label; Label; end
  12 + def _type
  13 + 'Foo'
  14 + end
  15 + end
  16 +
  17 + class Bar
  18 + Label = 'bar'
  19 + def self.label; Label; end
  20 + def _type
  21 + 'Bar'
  22 + end
  23 + end
  24 +
  25 + describe "#note" do
  26 +
  27 +
  28 + it 'return the html_safe of Note' do
  29 + expect(IssueTrackerDecorator.new(Foo).note).to eql 'hello <strong></strong>'.html_safe
  30 + end
  31 + end
  32 +
  33 + describe "#issue_trackers" do
  34 + it 'return an array of IssueTrackerDecorator' do
  35 + IssueTrackerDecorator.new(Foo).issue_trackers do |it|
  36 + expect(it).to be_a(IssueTrackerDecorator)
  37 + end
  38 + end
  39 + end
  40 +
  41 + describe "#fields" do
  42 + it 'return all Fields define decorate' do
  43 + IssueTrackerDecorator.new(Foo).fields do |itf|
  44 + expect(itf).to be_a(IssueTrackerFieldDecorator)
  45 + expect(itf.object).to eql :foo
  46 + expect(itf.field_info).to eql :bar
  47 + end
  48 + end
  49 + end
  50 +
  51 + describe "#params_class" do
  52 + it 'add the label in class' do
  53 + expect(IssueTrackerDecorator.new(Bar).params_class(Foo.new)).to eql 'bar'
  54 + end
  55 + it 'add chosen class if _type is same' do
  56 + expect(IssueTrackerDecorator.new(Foo).params_class(Foo.new)).to eql 'chosen foo'
  57 + end
  58 + end
  59 +end
spec/decorators/issue_tracker_field_decorator.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +require 'spec_helper'
  2 +
  3 +describe IssueTrackerFieldDecorator do
  4 +
  5 + describe "#label" do
  6 + it 'return the label of field_info by default' do
  7 + expect(IssueTrackerFieldDecorator.new(:foo, {:label => 'hello'}).label).to eq 'hello'
  8 + end
  9 + it 'return the key of field if no label define' do
  10 + expect(IssueTrackerFieldDecorator.new(:foo, {}).label).to eq 'foo'
  11 + end
  12 + end
  13 +
  14 +end
spec/decorators/watcher_decorator_spec.rb 0 → 100644
@@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
  1 +require 'spec_helper'
  2 +
  3 +describe WatcherDecorator do
  4 + describe "#email_choosen" do
  5 + context "with email define" do
  6 + it 'return blank' do
  7 + expect(WatcherDecorator.new(double(:email => 'foo')).email_choosen).to eql ''
  8 + end
  9 + end
  10 +
  11 + context "without email define" do
  12 + it 'return choosen' do
  13 + expect(WatcherDecorator.new(double(:email => '')).email_choosen).to eql 'chosen'
  14 + end
  15 + end
  16 + end
  17 +end
spec/views/apps/edit.html.haml_spec.rb
@@ -2,8 +2,10 @@ require &#39;spec_helper&#39; @@ -2,8 +2,10 @@ require &#39;spec_helper&#39;
2 2
3 describe "apps/edit.html.haml" do 3 describe "apps/edit.html.haml" do
4 let(:app) { stub_model(App) } 4 let(:app) { stub_model(App) }
  5 + let(:app_decorate) { AppDecorator.new(app) }
5 before do 6 before do
6 view.stub(:app).and_return(app) 7 view.stub(:app).and_return(app)
  8 + view.stub(:app_decorate).and_return(app_decorate)
7 controller.stub(:current_user) { stub_model(User) } 9 controller.stub(:current_user) { stub_model(User) }
8 end 10 end
9 11
spec/views/apps/new.html.haml_spec.rb
@@ -2,8 +2,10 @@ require &#39;spec_helper&#39; @@ -2,8 +2,10 @@ require &#39;spec_helper&#39;
2 2
3 describe "apps/new.html.haml" do 3 describe "apps/new.html.haml" do
4 let(:app) { stub_model(App) } 4 let(:app) { stub_model(App) }
  5 + let(:app_decorate) { AppDecorator.new(app) }
5 before do 6 before do
6 view.stub(:app).and_return(app) 7 view.stub(:app).and_return(app)
  8 + view.stub(:app_decorate).and_return(app_decorate)
7 controller.stub(:current_user) { stub_model(User) } 9 controller.stub(:current_user) { stub_model(User) }
8 end 10 end
9 11