Commit dc526592cfaeb3a8bccbc4715c26c8f114f1c2a7
1 parent
e7ff4c6f
Exists in
master
and in
1 other branch
Added a button to copy settings from another app when editing or creating an app…
…. (We needed to set up 10+ apps using the same issue tracker and watcher email). Works great, and is tested, but I wouldn't mind some help on the UI.
Showing
8 changed files
with
83 additions
and
16 deletions
Show diff stats
app/controllers/apps_controller.rb
@@ -34,12 +34,12 @@ class AppsController < InheritedResources::Base | @@ -34,12 +34,12 @@ class AppsController < InheritedResources::Base | ||
34 | end | 34 | end |
35 | 35 | ||
36 | def new | 36 | def new |
37 | - plug_params build_resource | 37 | + plug_params(build_resource) |
38 | new! | 38 | new! |
39 | end | 39 | end |
40 | 40 | ||
41 | def edit | 41 | def edit |
42 | - plug_params resource | 42 | + plug_params(resource) |
43 | edit! | 43 | edit! |
44 | end | 44 | end |
45 | 45 | ||
@@ -63,6 +63,7 @@ class AppsController < InheritedResources::Base | @@ -63,6 +63,7 @@ class AppsController < InheritedResources::Base | ||
63 | def plug_params app | 63 | def plug_params app |
64 | app.watchers.build if app.watchers.none? | 64 | app.watchers.build if app.watchers.none? |
65 | app.issue_tracker = IssueTracker.new unless app.issue_tracker_configured? | 65 | app.issue_tracker = IssueTracker.new unless app.issue_tracker_configured? |
66 | + app.copy_attributes_from(params[:copy_attributes_from]) if params[:copy_attributes_from] | ||
66 | end | 67 | end |
67 | 68 | ||
68 | # email_at_notices is edited as a string, and stored as an array. | 69 | # email_at_notices is edited as a string, and stored as an array. |
@@ -0,0 +1,14 @@ | @@ -0,0 +1,14 @@ | ||
1 | +module AppsHelper | ||
2 | + def link_to_copy_attributes_from_other_app | ||
3 | + if App.count > 1 | ||
4 | + html = link_to('copy settings from another app', '#', | ||
5 | + :class => 'button copy_config') | ||
6 | + html << select("duplicate", "app", | ||
7 | + App.all.reject{|a| a == @app }. | ||
8 | + collect{|p| [ p.name, p.id ] }, {:include_blank => "[choose app]"}, | ||
9 | + {:class => "choose_other_app", :style => "display: none;"}) | ||
10 | + return html | ||
11 | + end | ||
12 | + end | ||
13 | +end | ||
14 | + |
app/models/app.rb
@@ -78,6 +78,22 @@ class App | @@ -78,6 +78,22 @@ class App | ||
78 | notify_all_users ? User.all.map(&:email).reject(&:blank?) : watchers.map(&:address) | 78 | notify_all_users ? User.all.map(&:email).reject(&:blank?) : watchers.map(&:address) |
79 | end | 79 | end |
80 | 80 | ||
81 | + # Copy app attributes from another app. | ||
82 | + def copy_attributes_from(app_id) | ||
83 | + if copy_app = App.where(:_id => app_id).first | ||
84 | + # Copy fields | ||
85 | + (copy_app.fields.keys - %w(_id name created_at updated_at)).each do |k| | ||
86 | + self.send("#{k}=", copy_app.send(k)) | ||
87 | + end | ||
88 | + # Clone the embedded objects that can be changed via apps/edit (ignore errs & deploys, etc.) | ||
89 | + %w(watchers issue_tracker).each do |relation| | ||
90 | + if obj = copy_app.send(relation) | ||
91 | + self.send("#{relation}=", obj.is_a?(Array) ? obj.map(&:clone) : obj.clone) | ||
92 | + end | ||
93 | + end | ||
94 | + end | ||
95 | + end | ||
96 | + | ||
81 | protected | 97 | protected |
82 | 98 | ||
83 | def generate_api_key | 99 | def generate_api_key |
app/views/apps/edit.html.haml
1 | - content_for :title, 'Edit App' | 1 | - content_for :title, 'Edit App' |
2 | -- content_for :action_bar, link_to('cancel', app_path(@app), :class => 'button') | 2 | +- content_for :action_bar do |
3 | + = link_to_copy_attributes_from_other_app | ||
4 | + = link_to('cancel', app_path(@app), :class => 'button') | ||
3 | 5 | ||
4 | = form_for @app do |f| | 6 | = form_for @app do |f| |
5 | - | 7 | + |
6 | = render 'fields', :f => f | 8 | = render 'fields', :f => f |
7 | - | ||
8 | - %div.buttons= f.submit 'Update App' | ||
9 | \ No newline at end of file | 9 | \ No newline at end of file |
10 | + | ||
11 | + %div.buttons= f.submit 'Update App' | ||
12 | + |
app/views/apps/new.html.haml
1 | - content_for :title, 'Add App' | 1 | - content_for :title, 'Add App' |
2 | -- content_for :action_bar, link_to('cancel', apps_path, :class => 'button') | 2 | +- content_for :action_bar do |
3 | + = link_to_copy_attributes_from_other_app | ||
4 | + = link_to('cancel', apps_path, :class => 'button') | ||
3 | 5 | ||
4 | = form_for @app do |f| | 6 | = form_for @app do |f| |
5 | - | 7 | + |
6 | = render 'fields', :f => f | 8 | = render 'fields', :f => f |
7 | - | ||
8 | - %div.buttons= f.submit 'Add App' | ||
9 | \ No newline at end of file | 9 | \ No newline at end of file |
10 | + | ||
11 | + %div.buttons= f.submit 'Add App' | ||
12 | + |
public/javascripts/application.js
@@ -2,7 +2,7 @@ | @@ -2,7 +2,7 @@ | ||
2 | 2 | ||
3 | $(function(){ | 3 | $(function(){ |
4 | activateTabbedPanels(); | 4 | activateTabbedPanels(); |
5 | - | 5 | + |
6 | $('#watcher_name').live("click", function() { | 6 | $('#watcher_name').live("click", function() { |
7 | $(this).closest('form').find('.show').removeClass('show'); | 7 | $(this).closest('form').find('.show').removeClass('show'); |
8 | $('#app_watchers_attributes_0_user_id').addClass('show'); | 8 | $('#app_watchers_attributes_0_user_id').addClass('show'); |
@@ -13,6 +13,14 @@ $(function(){ | @@ -13,6 +13,14 @@ $(function(){ | ||
13 | $('#app_watchers_attributes_0_email').addClass('show'); | 13 | $('#app_watchers_attributes_0_email').addClass('show'); |
14 | }); | 14 | }); |
15 | 15 | ||
16 | + $('a.copy_config').live("click", function() { | ||
17 | + $('select.choose_other_app').show().focus(); | ||
18 | + }); | ||
19 | + $('select.choose_other_app').live("change", function() { | ||
20 | + var loc = window.location; | ||
21 | + window.location.href = loc.protocol + "//" + loc.host + loc.pathname + | ||
22 | + "?copy_attributes_from=" + $(this).val(); | ||
23 | + }); | ||
16 | }); | 24 | }); |
17 | 25 | ||
18 | function activateTabbedPanels() { | 26 | function activateTabbedPanels() { |
@@ -22,8 +30,8 @@ function activateTabbedPanels() { | @@ -22,8 +30,8 @@ function activateTabbedPanels() { | ||
22 | panel.addClass('panel'); | 30 | panel.addClass('panel'); |
23 | panel.find('h3').hide(); | 31 | panel.find('h3').hide(); |
24 | }) | 32 | }) |
25 | - | ||
26 | - $('.tab-bar a').click(function(){ | 33 | + |
34 | + $('.tab-bar a').click(function(){ | ||
27 | activateTab($(this)); | 35 | activateTab($(this)); |
28 | return(false); | 36 | return(false); |
29 | }); | 37 | }); |
@@ -33,10 +41,11 @@ function activateTabbedPanels() { | @@ -33,10 +41,11 @@ function activateTabbedPanels() { | ||
33 | function activateTab(tab) { | 41 | function activateTab(tab) { |
34 | tab = $(tab); | 42 | tab = $(tab); |
35 | var panel = $('#'+tab.attr('rel')); | 43 | var panel = $('#'+tab.attr('rel')); |
36 | - | 44 | + |
37 | tab.closest('.tab-bar').find('a.active').removeClass('active'); | 45 | tab.closest('.tab-bar').find('a.active').removeClass('active'); |
38 | tab.addClass('active'); | 46 | tab.addClass('active'); |
39 | - | 47 | + |
40 | $('.panel').hide(); | 48 | $('.panel').hide(); |
41 | panel.show(); | 49 | panel.show(); |
42 | -} | ||
43 | \ No newline at end of file | 50 | \ No newline at end of file |
51 | +} | ||
52 | + |
spec/controllers/apps_controller_spec.rb
@@ -173,6 +173,16 @@ describe AppsController do | @@ -173,6 +173,16 @@ describe AppsController do | ||
173 | assigns(:app).should be_new_record | 173 | assigns(:app).should be_new_record |
174 | assigns(:app).watchers.should_not be_empty | 174 | assigns(:app).watchers.should_not be_empty |
175 | end | 175 | end |
176 | + | ||
177 | + it "should copy attributes from an existing app" do | ||
178 | + @app = Factory(:app, :name => "do not copy", | ||
179 | + :github_url => "github.com/test/example") | ||
180 | + get :new, :copy_attributes_from => @app.id | ||
181 | + assigns(:app).should be_a(App) | ||
182 | + assigns(:app).should be_new_record | ||
183 | + assigns(:app).name.should be_blank | ||
184 | + assigns(:app).github_url.should == "github.com/test/example" | ||
185 | + end | ||
176 | end | 186 | end |
177 | 187 | ||
178 | describe "GET /apps/:id/edit" do | 188 | describe "GET /apps/:id/edit" do |
spec/models/app_spec.rb
@@ -99,5 +99,16 @@ describe App do | @@ -99,5 +99,16 @@ describe App do | ||
99 | end | 99 | end |
100 | end | 100 | end |
101 | 101 | ||
102 | + context "copying attributes from existing app" do | ||
103 | + it "should only copy the necessary fields" do | ||
104 | + @app, @copy_app = Factory(:app, :name => "app", :github_url => "url"), | ||
105 | + Factory(:app, :name => "copy_app", :github_url => "copy url") | ||
106 | + @copy_watcher = Factory(:watcher, :email => "copywatcher@example.com", :app => @copy_app) | ||
107 | + @app.copy_attributes_from(@copy_app.id) | ||
108 | + @app.name.should == "app" | ||
109 | + @app.github_url.should == "copy url" | ||
110 | + @app.watchers.first.email.should == "copywatcher@example.com" | ||
111 | + end | ||
112 | + end | ||
102 | end | 113 | end |
103 | 114 |