From 7af785933471ea10f172d1a02b286cadb39d56a6 Mon Sep 17 00:00:00 2001 From: Guillaume Lazzara Date: Wed, 15 Jun 2016 11:34:05 +0200 Subject: [PATCH] Implement procedure cloning --- Gemfile | 2 + Gemfile.lock | 5 ++- .../admin/procedures_controller.rb | 17 ++++++++ app/models/procedure.rb | 7 ++++ .../admin/procedures/_draft_list.html.haml | 2 + app/views/admin/procedures/_list.html.haml | 2 + config/routes.rb | 1 + .../admin/procedures_controller_spec.rb | 31 ++++++++++++++ spec/models/procedure_spec.rb | 40 +++++++++++++++++++ spec/spec_helper.rb | 7 ++++ 10 files changed, 113 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 5bd2a237d..12229eea2 100644 --- a/Gemfile +++ b/Gemfile @@ -22,6 +22,8 @@ gem 'jbuilder', '~> 2.0' # bundle exec rake doc:rails generates the API under doc/api. gem 'sdoc', '~> 0.4.0', group: :doc +# Enable deep clone of active record models +gem 'deep_cloneable', '~> 2.2.1' # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' diff --git a/Gemfile.lock b/Gemfile.lock index 21f55c50e..31a9b9b55 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -119,6 +119,8 @@ GEM sprockets (>= 2.0.0) database_cleaner (1.4.1) debug_inspector (0.0.2) + deep_cloneable (2.2.1) + activerecord (>= 3.1.0, < 5.2.0) devise (3.4.1) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -634,6 +636,7 @@ DEPENDENCIES coffee-rails (~> 4.1.0) css_splitter database_cleaner + deep_cloneable (~> 2.2.1) devise draper factory_girl @@ -690,4 +693,4 @@ DEPENDENCIES will_paginate-bootstrap BUNDLED WITH - 1.11.2 + 1.12.5 diff --git a/app/controllers/admin/procedures_controller.rb b/app/controllers/admin/procedures_controller.rb index 4539cc02f..19e003a70 100644 --- a/app/controllers/admin/procedures_controller.rb +++ b/app/controllers/admin/procedures_controller.rb @@ -83,6 +83,23 @@ class Admin::ProceduresController < AdminController change_status({archived: params[:archive]}) end + def clone + @procedure = current_administrateur.procedures.find(params[:procedure_id]) + + new_procedure = @procedure.clone + if new_procedure + flash.notice = 'Procédure clonée' + redirect_to edit_admin_procedure_path(id: new_procedure.id) + else + flash.now.alert = @procedure.errors.full_messages.join('
').html_safe + render 'index' + end + + rescue ActiveRecord::RecordNotFound + flash.alert = 'Procédure inéxistante' + redirect_to admin_procedures_path + end + def active_class @active_class = 'active' end diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 00aa073b6..5f4de31f6 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -58,4 +58,11 @@ class Procedure < ActiveRecord::Base published? end + def clone + procedure = self.deep_clone(include: [ :types_de_piece_justificative, :types_de_champ, :module_api_carto ]) + procedure.archived = false + procedure.published = false + return procedure if procedure.save + end + end diff --git a/app/views/admin/procedures/_draft_list.html.haml b/app/views/admin/procedures/_draft_list.html.haml index 7d3505614..b169f2fc5 100644 --- a/app/views/admin/procedures/_draft_list.html.haml +++ b/app/views/admin/procedures/_draft_list.html.haml @@ -3,6 +3,7 @@ %thead %th#ID= smart_listing.sortable 'ID', 'id' %th#libelle= smart_listing.sortable 'Libellé', 'libelle' + %th#lien Actions - @procedures.each do |procedure| - procedure = procedure.decorate @@ -10,6 +11,7 @@ %td.col-md-1.col-lg-1= procedure.id %td.col-md-6.col-lg-6 = link_to(procedure.libelle, "/admin/procedures/#{procedure.id}") + %td= link_to('cloner', admin_procedure_clone_path(procedure.id), 'data-method' => :put, class: 'btn-xs btn-primary') = smart_listing.paginate = smart_listing.pagination_per_page_links diff --git a/app/views/admin/procedures/_list.html.haml b/app/views/admin/procedures/_list.html.haml index 651583f12..4735d527f 100644 --- a/app/views/admin/procedures/_list.html.haml +++ b/app/views/admin/procedures/_list.html.haml @@ -4,6 +4,7 @@ %th#ID= smart_listing.sortable 'ID', 'id' %th#libelle= smart_listing.sortable 'Libellé', 'libelle' %th#lien Lien + %th#lien Actions - @procedures.each do |procedure| - procedure = procedure.decorate @@ -12,6 +13,7 @@ %td.col-md-6.col-lg-6 = link_to(procedure.libelle, "/admin/procedures/#{procedure.id}") %td= link_to procedure.lien, procedure.lien + %td= link_to('cloner', admin_procedure_clone_path(procedure.id), 'data-method' => :put, class: 'btn-xs btn-primary') = smart_listing.paginate = smart_listing.pagination_per_page_links diff --git a/config/routes.rb b/config/routes.rb index db74fc87f..764b56da0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -100,6 +100,7 @@ Rails.application.routes.draw do put 'archive' => 'procedures#archive', as: :archive put 'publish' => 'procedures#publish', as: :publish + put 'clone' => 'procedures#clone', as: :clone resource :accompagnateurs, only: [:show, :update] diff --git a/spec/controllers/admin/procedures_controller_spec.rb b/spec/controllers/admin/procedures_controller_spec.rb index 11587b94d..8d8779dc3 100644 --- a/spec/controllers/admin/procedures_controller_spec.rb +++ b/spec/controllers/admin/procedures_controller_spec.rb @@ -265,4 +265,35 @@ describe Admin::ProceduresController, type: :controller do it { expect(flash[:alert]).to have_content 'Procédure inéxistante' } end end + + describe 'PUT #clone' do + let!(:procedure) { create(:procedure, administrateur: admin) } + subject { put :clone, procedure_id: procedure.id } + + it { expect{ subject }.to change(Procedure, :count).by(1) } + + context 'when admin is the owner of the procedure' do + before do + subject + end + + it 'creates a new procedure and redirect to it' do + expect(response).to redirect_to edit_admin_procedure_path(id: Procedure.last.id) + expect(flash[:notice]).to have_content 'Procédure clonée' + end + end + + context 'when admin is not the owner of the procedure' do + let(:admin_2) { create(:administrateur) } + + before do + sign_out admin + sign_in admin_2 + subject + end + + it { expect(response).to redirect_to :admin_procedures } + it { expect(flash[:alert]).to have_content 'Procédure inéxistante' } + end + end end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index b36e64837..f805f0292 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -122,4 +122,44 @@ describe Procedure do it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) } end end + + describe 'clone' do + let(:archived) { false } + let(:published) { false } + let(:procedure) { create(:procedure, archived: archived, published: published) } + let!(:type_de_champ_0) { create(:type_de_champ, procedure: procedure, order_place: 0) } + let!(:type_de_champ_1) { create(:type_de_champ, procedure: procedure, order_place: 1) } + let!(:piece_justificative_0) { create(:type_de_piece_justificative, procedure: procedure, order_place: 0) } + let!(:piece_justificative_1) { create(:type_de_piece_justificative, procedure: procedure, order_place: 1) } + subject { procedure.clone } + + it 'should duplicate specific objects with different id' do + expect(subject.id).not_to eq(procedure.id) + expect(subject).to have_same_attributes_as(procedure) + expect(subject.module_api_carto).to have_same_attributes_as(procedure.module_api_carto) + + subject.types_de_champ.zip(procedure.types_de_champ).each do |stc, ptc| + expect(stc).to have_same_attributes_as(ptc) + end + + subject.types_de_piece_justificative.zip(procedure.types_de_piece_justificative).each do |stc, ptc| + expect(stc).to have_same_attributes_as(ptc) + end + end + + it 'should not duplicate specific related objects' do + expect(subject.dossiers).to eq([]) + expect(subject.gestionnaires).to eq([]) + expect(subject.assign_to).to eq([]) + end + + describe 'procedure status is reset' do + let(:archived) { true } + let(:published) { true } + it 'sets published and archived to false' do + expect(subject.archived).to be_falsey + expect(subject.published).to be_falsey + end + end + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 7411d7377..bca602fe6 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -113,4 +113,11 @@ RSpec.configure do |config| end end } + + RSpec::Matchers.define :have_same_attributes_as do |expected| + match do |actual| + ignored = [:id, :procedure_id, :updated_at, :created_at] + actual.attributes.with_indifferent_access.except(*ignored) == expected.attributes.with_indifferent_access.except(*ignored) + end + end end