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

Gemfile
... ... @@ -25,6 +25,7 @@ gem 'rails_autolink'
25 25 # Please don't update hoptoad_notifier to airbrake.
26 26 # It's for internal use only, and we monkeypatch certain methods
27 27 gem 'hoptoad_notifier', "~> 2.4"
  28 +gem 'draper', :require => false
28 29  
29 30  
30 31 # Remove / comment out any of the gems below if you want to disable
... ...
Gemfile.lock
... ... @@ -95,6 +95,10 @@ GEM
95 95 warden (~> 1.2.3)
96 96 diff-lcs (1.2.4)
97 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 102 email_spec (1.5.0)
99 103 launchy (~> 2.1)
100 104 mail (~> 2.2)
... ... @@ -282,6 +286,7 @@ GEM
282 286 rdoc (3.12.2)
283 287 json (~> 1.4)
284 288 ref (1.0.5)
  289 + request_store (1.0.5)
285 290 rest-client (1.6.7)
286 291 mime-types (>= 1.16)
287 292 ri_cal (0.8.8)
... ... @@ -382,6 +387,7 @@ DEPENDENCIES
382 387 debugger
383 388 decent_exposure
384 389 devise
  390 + draper
385 391 email_spec
386 392 execjs
387 393 fabrication
... ...
app/controllers/apps_controller.rb
... ... @@ -16,6 +16,9 @@ class AppsController < ApplicationController
16 16 }
17 17  
18 18 expose(:app, :ancestor => :app_scope)
  19 + expose(:app_decorate) do
  20 + AppDecorator.new(app)
  21 + end
19 22  
20 23 expose(:all_errs) {
21 24 !!params[:all_errs]
... ...
app/decorators/app_decorator.rb 0 → 100644
... ... @@ -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 @@
  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 @@
  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 @@
  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 3 include Mongoid::Timestamps
4 4 include HashHelper
5 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 11 default_url_options[:host] = ActionMailer::Base.default_url_options[:host]
7 12  
8 13 embedded_in :app, :inverse_of => :issue_tracker
... ... @@ -41,7 +46,6 @@ class IssueTracker
41 46 def url; nil; end
42 47  
43 48 # Retrieve tracker label from either class or instance.
44   - Label = ''
45 49 def self.label; self::Label; end
46 50 def label; self.class.label; end
47 51  
... ...
app/views/apps/_fields.html.haml
... ... @@ -29,9 +29,9 @@
29 29 = f.check_box :notify_on_errs, 'data-show-when-checked' => '.email_at_notices_nested'
30 30 = f.label :notify_on_errs, 'Notify on errors'
31 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 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 35 .field-helpertext times an error occurs (comma separated).
36 36 %div.checkbox
37 37 = f.check_box :notify_on_deploys
... ... @@ -42,16 +42,16 @@
42 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 46 %legend Watchers
47 47 = f.fields_for :watchers do |w|
48 48 %div.watcher.nested
49 49 %div.choose
50 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 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 55 = w.select :user_id, User.all.map{|u| [u.name,u.id.to_s]}, :include_blank => '-- Select a User --'
56 56 %div.watcher_params.email{:class => w.object.email.present? ? 'chosen' : nil}
57 57 = w.text_field :email
... ...
app/views/apps/_issue_tracker_fields.html.haml
1 1 %fieldset
2   - %legend Issue tracker
  2 + %legend= t('.legend')
3 3 = f.fields_for :issue_tracker do |w|
4 4 %div.issue_tracker.nested
5 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 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 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 5 :data => { :confirm => t('apps.confirm_delete') }, :class => 'button'
6 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 10 = render 'fields', :f => f
11 11  
... ...
app/views/apps/new.html.haml
... ... @@ -3,7 +3,7 @@
3 3 = link_to_copy_attributes_from_other_app
4 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 8 = render 'fields', :f => f
9 9  
... ...
config/application.rb
... ... @@ -9,6 +9,8 @@ require &quot;action_mailer/railtie&quot;
9 9 require 'mongoid/railtie'
10 10 require "sprockets/railtie"
11 11  
  12 +require 'draper'
  13 +
12 14 if defined?(Bundler)
13 15 # If you precompile assets before deploying to production, use this line
14 16 Bundler.require(*Rails.groups(:assets => %w(development test)))
... ...
config/locales/en.yml
... ... @@ -125,4 +125,6 @@ en:
125 125 watchers: Watchers
126 126 when: When
127 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 28 click_link app.name if page.current_url != app_url(app)
29 29 expect(page).to_not have_button I18n.t('apps.show.edit')
30 30 end
31   -
32 31 end
... ...
spec/decorators/app_decorator_spec.rb 0 → 100644
... ... @@ -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 @@
  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 @@
  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 @@
  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 2  
3 3 describe "apps/edit.html.haml" do
4 4 let(:app) { stub_model(App) }
  5 + let(:app_decorate) { AppDecorator.new(app) }
5 6 before do
6 7 view.stub(:app).and_return(app)
  8 + view.stub(:app_decorate).and_return(app_decorate)
7 9 controller.stub(:current_user) { stub_model(User) }
8 10 end
9 11  
... ...
spec/views/apps/new.html.haml_spec.rb
... ... @@ -2,8 +2,10 @@ require &#39;spec_helper&#39;
2 2  
3 3 describe "apps/new.html.haml" do
4 4 let(:app) { stub_model(App) }
  5 + let(:app_decorate) { AppDecorator.new(app) }
5 6 before do
6 7 view.stub(:app).and_return(app)
  8 + view.stub(:app_decorate).and_return(app_decorate)
7 9 controller.stub(:current_user) { stub_model(User) }
8 10 end
9 11  
... ...