OSDN Git Service

t30350#:fix destroy in op, p, user v05i18n
authoryasushiito <yas@pen-chan.jp>
Tue, 25 Dec 2012 23:05:52 +0000 (08:05 +0900)
committeryasushiito <yas@pen-chan.jp>
Tue, 25 Dec 2012 23:05:52 +0000 (08:05 +0900)
app/controllers/original_pictures_controller.rb
app/models/author.rb
app/models/original_picture.rb
app/models/picture.rb
app/models/resource_picture.rb
app/models/user.rb
app/views/pictures/show.html.erb
config/locales/pettanr.ja.yml
spec/controllers/original_pictures_controller_spec.rb
spec/models/original_picture_spec.rb
spec/models/picture_spec.rb

index 9739a0e..2ba2f48 100644 (file)
@@ -122,13 +122,15 @@ class OriginalPicturesController < ApplicationController
 
   def destroy
     @original_picture = OriginalPicture.edit(params[:id], @artist)
-    OriginalPicture.transaction do
-      @original_picture.destroy
-    end
     
     respond_to do |format|
-      format.html { redirect_to original_pictures_url }
-      format.json { head :ok }
+      if @original_picture.destroy_with_resource_picture
+        format.html { redirect_to original_pictures_url }
+        format.json { head :ok }
+      else
+        format.html { redirect_to original_picture_path(@original_picture) }
+        format.json { render json: @original_picture.errors, status: :unprocessable_entity }
+      end
     end
   end
 end
index 6de0ac8..4149e76 100644 (file)
@@ -1,8 +1,8 @@
 class Author < ActiveRecord::Base
   has_one :artist
   belongs_to :user
-  has_many :comic
-  has_many :panel
+  has_many :comics
+  has_many :panels
   
   validates :name, :presence => true, :length => {:maximum => 30}
   validates :user_id, :numericality => true, :existence => {:both => false}
index 33de4f2..f55c06b 100644 (file)
@@ -11,8 +11,6 @@ class OriginalPicture < ActiveRecord::Base
   validates :artist_id, :presence => true, :numericality => true, :existence => {:both => false}
   validates :md5, :presence => true, :length => {:minimum => 32, :maximum => 32}
   
-  before_destroy :destroy_with_file
-  
   def supply_default
   end
   
@@ -137,11 +135,6 @@ class OriginalPicture < ActiveRecord::Base
     {:include => {:resource_picture => {}, :pictures => {}}}
   end
   
-  def destroy_with_file
-    PictureIO.original_picture_io.delete self.filename
-    self.resource_picture.destroy
-  end
-  
   def store(imager)
     unless imager
       self.errors.add :base, I18n.t('errors.invalid_image')
@@ -167,6 +160,29 @@ class OriginalPicture < ActiveRecord::Base
     PictureIO.original_picture_io.get self.filename, subdir
   end
   
+  def destroy_with_resource_picture
+    res = false
+    OriginalPicture.transaction do
+      begin
+        PictureIO.original_picture_io.delete(self.filename) if PictureIO.original_picture_io.exist?(self.filename)
+      rescue PictureIO::Error
+        res = false
+        raise ActiveRecord::Rollback
+      end
+      if self.resource_picture
+        res = self.resource_picture.unpublish
+        raise ActiveRecord::Rollback unless res
+      end
+      self.pictures.each do |picture|
+        res = picture.unpublish
+        raise ActiveRecord::Rollback unless res
+      end
+      res = self.destroy
+      raise ActiveRecord::Rollback unless res
+    end
+    res
+  end
+  
   def self.export ar
     l = LicenseGroup.list
     op = OriginalPicture.list ar.id
index b9d7da1..3b1fc56 100644 (file)
@@ -44,6 +44,7 @@ class Picture < ActiveRecord::Base
   end
   
   def showable? au = nil
+    return false unless self.original_picture
     return true if self.own?(au)
     self.enable? and self.head?
   end
@@ -161,6 +162,12 @@ class Picture < ActiveRecord::Base
     PictureIO.picture_io.get self.filename, subdir
   end
   
+  def unpublish
+    imager = PettanImager.load(File.open(Rails.root + 'app/assets/images/error.png', 'rb').read)
+    return false unless imager
+    self.store imager
+  end
+  
   def credit_template
     "#{self.classname.tableize}/attributes/credit"
   end
index 68e22c0..15999d6 100644 (file)
@@ -216,15 +216,14 @@ class ResourcePicture < ActiveRecord::Base
     res = false
     ResourcePicture.transaction do
       begin
-        if res = self.destroy
-          PictureIO.resource_picture_io.delete(self.filename)
-          PictureIO.resource_picture_io.delete(self.filename, 'full')
-          res = true
-        end
+        PictureIO.resource_picture_io.delete(self.filename) if PictureIO.resource_picture_io.exist?(self.filename)
+        PictureIO.resource_picture_io.delete(self.filename, 'full') if PictureIO.resource_picture_io.exist?(self.filename, 'full')
       rescue PictureIO::Error
         res = false
         raise ActiveRecord::Rollback
       end
+      res = self.destroy
+      raise ActiveRecord::Rollback unless res
     end
     res
   end
index 54ee43d..5c80960 100644 (file)
@@ -28,8 +28,14 @@ class User < ActiveRecord::Base
       self.author.panels.each do |panel|
         raise ActiveRecord::Rollback unless panel.destroy_with_elements
       end
+      if self.author.artist
+        self.author.artist.original_pictures.each do |original_picture|
+          raise ActiveRecord::Rollback unless original_picture.destroy_with_resource_picture
+        end
+        raise ActiveRecord::Rollback unless self.author.artist.destroy
+      end
       raise ActiveRecord::Rollback unless self.author.destroy
-      raise ActiveRecord::Rollback unless self.destroy
+      raise ActiveRecord::Rollback unless super
       res = true
     end
     res
index 5a11460..59402e7 100644 (file)
@@ -1,26 +1,32 @@
 <h1><%= t('.title') %></h1>
 <div>
-  <% if @picture.enable? %>
-    <% if @picture.head? %>
+  <% if @picture.original_picture %>
+    <% if @picture.enable? %>
+      <% if @picture.head? %>
+        <p>
+          <%= t 'pictures.show.announce.head' -%>
+          <%= link_to tag(:img, @picture.tmb_opt_img_tag), resource_picture_path(@picture.resource_picture) %>
+        </p>
+      <% else %>
+        <p>
+          <%= t 'pictures.show.announce.tail' -%>
+          <%= link_to tag(:img, @picture.head.tmb_opt_img_tag), resource_picture_path(@picture.head.resource_picture) %>
+        </p>
+      <% end %>
+    <% else %>
       <p>
-        <%= t 'pictures.show.announce.head' -%>
-        <%= link_to tag(:img, @picture.tmb_opt_img_tag), resource_picture_path(@picture.resource_picture) %>
+        <%= t 'pictures.show.announce.disable' -%>
       </p>
-    <% else %>
+    <% end %>
+    <% if @picture.own? @artist %>
       <p>
-        <%= t 'pictures.show.announce.tail' -%>
-        <%= link_to tag(:img, @picture.head.tmb_opt_img_tag), resource_picture_path(@picture.head.resource_picture) %>
+        <%= t 'pictures.show.announce.owner' -%>
+        <%= link_to tag(:img, @picture.original_picture.tmb_opt_img_tag), original_picture_path(@picture.original_picture) %>
       </p>
     <% end %>
   <% else %>
     <p>
-      <%= t 'pictures.show.announce.disable' -%>
-    </p>
-  <% end %>
-  <% if @picture.own? @artist %>
-    <p>
-      <%= t 'pictures.show.announce.owner' -%>
-      <%= link_to tag(:img, @picture.original_picture.tmb_opt_img_tag), original_picture_path(@picture.original_picture) %>
+      <%= t 'pictures.show.announce.destroyed' -%>
     </p>
   <% end %>
 </div>
index 1612769..f3dd974 100644 (file)
@@ -584,6 +584,7 @@ ja:
         head: 素材として利用できます。
         tail: この画像は既に改訂されています。素材として利用するなら、こちら(最新版)が利用できます。
         owner: あなたの画像です。原画を管理するなら、こちらを利用してください。
+        destroyed: この素材は削除されています。素材として利用できません。
     md5:
       title: 実素材MD5検索一覧
     credit:
index 4a325ef..782b8eb 100644 (file)
@@ -909,4 +909,99 @@ describe OriginalPicturesController do
     end
   end
 
+  describe '削除に於いて' do
+    before do
+      @op = FactoryGirl.create :original_picture, :artist_id => @artist.id
+      sign_in @user
+    end
+    context '事前チェックしておく' do
+      before do
+        OriginalPicture.stub(:edit).with(any_args()).and_return @op
+        OriginalPicture.any_instance.stub(:destroy_with_resource_picture).with(any_args()).and_return(true)
+      end
+      it '原画モデルに編集取得を問い合わせている' do
+        OriginalPicture.should_receive(:edit).exactly(1)
+        delete :destroy, :id => @op.id
+      end
+      it 'モデルに削除を依頼する' do
+        OriginalPicture.any_instance.should_receive(:destroy_with_resource_picture).exactly(1)
+        delete :destroy, :id => @op.id
+      end
+      it '@original_pictureにアレを取得している' do
+        delete :destroy, :id => @op.id
+        assigns(:original_picture).id.should eq(@op.id)
+      end
+    end
+    context 'つつがなく終わるとき' do
+      before do
+        OriginalPicture.any_instance.stub(:destroy_with_resource_picture).with(any_args()).and_return(true)
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          delete :destroy, :id => @op.id
+          response.status.should eq 302
+        end
+        it '原画の一覧ページへ遷移する' do
+          delete :destroy, :id => @op.id
+          response.should redirect_to(original_pictures_path)
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+          delete :destroy, :id => @op.id, :format => :json
+          response.should be_success 
+        end
+        it 'ページ本体は特に返さない' do
+          delete :destroy, :id => @op.id, :format => :json
+          response.body.should match /./
+        end
+      end
+    end
+    context '作家権限がないとき' do
+      before do
+        sign_out @user
+      end
+      it 'ステータスコード302 Foundを返す' do
+        delete :destroy, :id => @op.id
+        response.status.should eq 302
+      end
+      context 'html形式' do
+        it 'サインインページへ遷移する' do
+          delete :destroy, :id => @op.id
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it '応答メッセージにUnauthorizedを返す' do
+          delete :destroy, :id => @op.id, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+    context '削除に失敗したとき' do
+      before do
+        OriginalPicture.any_instance.stub(:destroy_with_resource_picture).and_return(false)
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          delete :destroy, :id => @op.id
+          response.status.should eq 302
+        end
+        it 'その原画の詳細ページへ遷移する' do
+          delete :destroy, :id => @op.id
+          response.should redirect_to(original_picture_path(@op))
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード422 unprocessable_entity を返す' do
+          delete :destroy, :id => @op.id, :format => :json
+          response.status.should eq 422
+        end
+        it '応答メッセージUnprocessable Entityを返す' do
+          delete :destroy, :id => @op.id, :format => :json
+          response.message.should match(/Unprocessable/)
+        end
+      end
+    end
+  end
 end
index ae736ba..8991830 100644 (file)
@@ -783,6 +783,140 @@ describe OriginalPicture do
     end
   end
   
+  describe '削除に於いて' do
+    before do
+      @op = FactoryGirl.create :original_picture, :artist_id => @artist.id
+    end
+    context '事前チェックしておく' do
+      before do
+        OriginalPicture.any_instance.stub(:destroy).and_return(true)
+        ResourcePicture.any_instance.stub(:unpublish).and_return(true)
+        Picture.any_instance.stub(:unpublish).with(any_args).and_return(true)
+        PictureIO.original_picture_io.stub(:delete).with(any_args).and_return(true)
+      end
+      it '原画モデルに削除を依頼している' do
+        OriginalPicture.any_instance.should_receive(:destroy).exactly(1)
+        r = @op.destroy_with_resource_picture
+      end
+      it '保管庫に原画の画像データ削除を依頼している' do
+        PictureIO.original_picture_io.should_receive(:delete).with(@op.filename).exactly(1)
+        r = @op.destroy_with_resource_picture
+      end
+      context '自身にリンクされた素材があるとき' do
+        it '素材モデルに削除を依頼している' do
+          @p = FactoryGirl.create :picture, :original_picture_id => @op.id, :license_id => @license.id, :artist_id => @artist.id, :revision => 0
+          @rp = FactoryGirl.create :resource_picture, :artist_id => @artist.id, :license_id => @license.id, :original_picture_id => @op.id, :picture_id => @p.id
+          ResourcePicture.any_instance.should_receive(:unpublish).exactly(1)
+          r = @op.destroy_with_resource_picture
+        end
+      end
+      context '自身にリンクされた素材がないとき' do
+        it '素材モデルに削除を依頼しない' do
+          ResourcePicture.any_instance.should_not_receive(:unpublish)
+          r = @op.destroy_with_resource_picture
+        end
+      end
+      context '自身にリンクされた実素材があるとき' do
+        it 'すべての実素材に墨塗を依頼している' do
+          @p = FactoryGirl.create :picture, :original_picture_id => @op.id, :license_id => @license.id, :artist_id => @artist.id, :revision => 0
+          Picture.any_instance.should_receive(:unpublish).with(any_args).exactly(1)
+          r = @op.destroy_with_resource_picture
+        end
+      end
+      context '自身にリンクされた実素材がないとき' do
+        it '実素材に墨塗を依頼しない' do
+          Picture.any_instance.should_not_receive(:unpublish)
+          r = @op.destroy_with_resource_picture
+        end
+      end
+    end
+    context 'つつがなく終わるとき' do
+      before do
+        PictureIO.original_picture_io.stub(:delete).with(any_args).and_return(true)
+        @p = FactoryGirl.create :picture, :original_picture_id => @op.id, :license_id => @license.id, :artist_id => @artist.id, :revision => 0
+        @rp = FactoryGirl.create :resource_picture, :artist_id => @artist.id, :license_id => @license.id, :original_picture_id => @op.id, :picture_id => @p.id
+      end
+      it '自身を削除する' do
+        lambda {
+          r = @op.destroy_with_resource_picture
+        }.should change(OriginalPicture, :count).by(-1)
+        lambda {
+          r = OriginalPicture.find @op.id
+        }.should raise_error
+      end
+      it 'Trueを返す' do
+        r = @op.destroy_with_resource_picture
+        r.should be_true
+      end
+    end
+    context '自身の削除に失敗したとき' do
+      before do
+        OriginalPicture.any_instance.stub(:destroy).with(any_args).and_return(false)
+      end
+      it 'Falseを返す' do
+        r = @op.destroy_with_resource_picture
+        r.should be_false
+      end
+      it 'ロールバックしている' do
+        lambda {
+          r = @op.destroy_with_resource_picture
+        }.should_not change(OriginalPicture, :count)
+      end
+    end
+    context '画像の削除に失敗したとき' do
+      before do
+        PictureIO.original_picture_io.stub(:delete).with(@op.filename).and_raise(PictureIO::Error)
+      end
+      it 'Falseを返す' do
+        r = @op.destroy_with_resource_picture
+        r.should be_false
+      end
+      it 'ロールバックしている' do
+        lambda {
+          r = @op.destroy_with_resource_picture
+        }.should_not change(OriginalPicture, :count)
+      end
+    end
+    context '素材の削除に失敗とき' do
+      before do
+        @p = FactoryGirl.create :picture, :original_picture_id => @op.id, :license_id => @license.id, :artist_id => @artist.id, :revision => 0
+        @rp = FactoryGirl.create :resource_picture, :artist_id => @artist.id, :license_id => @license.id, :original_picture_id => @op.id, :picture_id => @p.id
+        ResourcePicture.any_instance.stub(:unpublish).with(any_args).and_return(false)
+      end
+      it 'Falseを返す' do
+        r = @op.destroy_with_resource_picture
+        r.should be_false
+      end
+      it 'ロールバックしている' do
+        lambda {
+          r = @op.destroy_with_resource_picture
+        }.should_not change(OriginalPicture, :count)
+        lambda {
+          r = @op.destroy_with_resource_picture
+        }.should_not change(ResourcePicture, :count)
+      end
+    end
+    context '実素材の墨塗に失敗とき' do
+      before do
+        @p = FactoryGirl.create :picture, :original_picture_id => @op.id, :license_id => @license.id, :artist_id => @artist.id, :revision => 0
+        @rp = FactoryGirl.create :resource_picture, :artist_id => @artist.id, :license_id => @license.id, :original_picture_id => @op.id, :picture_id => @p.id
+        Picture.any_instance.stub(:unpublish).with(any_args).and_return(false)
+      end
+      it 'Falseを返す' do
+        r = @op.destroy_with_resource_picture
+        r.should be_false
+      end
+      it 'ロールバックしている' do
+        lambda {
+          r = @op.destroy_with_resource_picture
+        }.should_not change(OriginalPicture, :count)
+        lambda {
+          r = @op.destroy_with_resource_picture
+        }.should_not change(ResourcePicture, :count)
+      end
+    end
+  end
+  
 =begin
   describe 'エクスポートに於いて' do
     before do
index e37eac7..8684f43 100644 (file)
@@ -286,6 +286,15 @@ describe Picture do
     before do
       @p = FactoryGirl.build :picture, :original_picture_id => @op.id, :license_id => @license.id, :artist_id => @artist.id
     end
+    context '自身に原画がリンクしていないとき' do
+      before do
+        Picture.any_instance.stub(:original_picture).with(any_args).and_return(nil)
+      end
+      it 'Falseを返す' do
+        r = @p.showable?(@author)
+        r.should be_false
+      end
+    end
     it '自作の実素材ならyes' do
       Picture.any_instance.stub(:own?).with(any_args).and_return(true)
       @p.showable?(@artist).should == true
@@ -852,6 +861,55 @@ describe Picture do
     
   end
   
+  describe '墨塗に於いて' do
+    before do
+      @p = FactoryGirl.create :picture, :original_picture_id => @op.id, :license_id => @license.id, :artist_id => @artist.id, :ext => 'png'
+    end
+    context '事前チェック' do
+      before do
+        @imager = ImagerTest.load "abc\ndef\nghi"
+        Picture.any_instance.stub(:store).with(any_args).and_return(true)
+        PettanImager.stub(:load).with(any_args).and_return(@imager)
+      end
+      it '画像ライブラリにロードを依頼している' do
+        PettanImager.should_receive(:load).with(any_args).exactly(1)
+        r = @p.unpublish
+      end
+      it '自身に作成を依頼している' do
+        Picture.any_instance.should_receive(:store).with(any_args).exactly(1)
+        r = @p.unpublish
+      end
+    end
+    context 'つつがなく終わるとき' do
+      before do
+        Picture.any_instance.stub(:store).with(any_args).and_return(true)
+      end
+      it 'Trueを返す' do
+        r = @p.unpublish
+        r.should be_true
+      end
+    end
+    #例外ケース
+    context '画像ライブラリのロードに失敗したとき' do
+      before do
+        PettanImager.stub(:load).and_return(false)
+      end
+      it 'Falseを返す' do
+        r = @p.unpublish
+        r.should be_false
+      end
+    end
+    context '作成に失敗したとき' do
+      before do
+        Picture.any_instance.stub(:store).with(any_args).and_return(false)
+      end
+      it 'Falseを返す' do
+        r = @p.unpublish
+        r.should be_false
+      end
+    end
+  end
+  
   describe 'フラグ展開に於いて' do
     before do
       @p = FactoryGirl.build :picture, :original_picture_id => @op.id, :license_id => @license.id,