OSDN Git Service

t#32046:
authoryasushiito <yas@pen-chan.jp>
Sun, 22 Sep 2013 01:10:42 +0000 (10:10 +0900)
committeryasushiito <yas@pen-chan.jp>
Sun, 22 Sep 2013 01:10:42 +0000 (10:10 +0900)
96 files changed:
.gitignore
app/assets/javascripts/sheet_panels.js.coffee [new file with mode: 0644]
app/assets/javascripts/sheets.js.coffee [new file with mode: 0644]
app/assets/javascripts/story_sheets.js.coffee [new file with mode: 0644]
app/assets/stylesheets/sheet_panels.css.scss [new file with mode: 0644]
app/assets/stylesheets/sheets.css.scss [new file with mode: 0644]
app/assets/stylesheets/story_sheets.css.scss [new file with mode: 0644]
app/controllers/comics_controller.rb [new file with mode: 0644]
app/controllers/sheet_panels_controller.rb [new file with mode: 0644]
app/controllers/sheets_controller.rb [new file with mode: 0644]
app/controllers/stories_controller.rb [new file with mode: 0644]
app/controllers/story_sheets_controller.rb [new file with mode: 0644]
app/helpers/comics_helper.rb [new file with mode: 0644]
app/helpers/sheet_panels_helper.rb [new file with mode: 0644]
app/helpers/sheets_helper.rb [new file with mode: 0644]
app/helpers/stories_helper.rb [new file with mode: 0644]
app/helpers/story_sheets_helper.rb [new file with mode: 0644]
app/models/author.rb
app/models/comic.rb
app/models/sheet.rb [new file with mode: 0644]
app/models/sheet_panel.rb [new file with mode: 0644]
app/models/story.rb
app/models/story_sheet.rb [new file with mode: 0644]
app/views/comics/_form.html.erb [new file with mode: 0644]
app/views/comics/_list_item.html.erb [new file with mode: 0644]
app/views/comics/browse.html.erb [new file with mode: 0644]
app/views/comics/edit.html.erb [new file with mode: 0644]
app/views/comics/edit.js.erb [new file with mode: 0644]
app/views/comics/index.atom.builder [new file with mode: 0644]
app/views/comics/index.html.erb [new file with mode: 0644]
app/views/comics/index.rss.builder [new file with mode: 0644]
app/views/comics/list.html.erb [new file with mode: 0644]
app/views/comics/new.html.erb [new file with mode: 0644]
app/views/comics/new.js.erb [new file with mode: 0644]
app/views/comics/show.atom.builder [new file with mode: 0644]
app/views/comics/show.html.erb [new file with mode: 0644]
app/views/comics/show.rss.builder [new file with mode: 0644]
app/views/stories/_append_panel.html.erb [new file with mode: 0644]
app/views/stories/_comic_header.html.erb [new file with mode: 0644]
app/views/stories/_footer.html.erb [new file with mode: 0644]
app/views/stories/_form.html.erb [new file with mode: 0644]
app/views/stories/_licensed_pictures.html.erb [new file with mode: 0644]
app/views/stories/_list_item.html.erb [new file with mode: 0644]
app/views/stories/_order.html.erb [new file with mode: 0644]
app/views/stories/browse.html.erb [new file with mode: 0644]
app/views/stories/comic.html.erb [new file with mode: 0644]
app/views/stories/edit.html.erb [new file with mode: 0644]
app/views/stories/edit.js.erb [new file with mode: 0644]
app/views/stories/index.html.erb [new file with mode: 0644]
app/views/stories/list.html.erb [new file with mode: 0644]
app/views/stories/new.html.erb [new file with mode: 0644]
app/views/stories/show.html.erb [new file with mode: 0644]
config/routes.rb
db/migrate/20130914055043_create_sheets.rb [new file with mode: 0644]
db/migrate/20130914055219_create_sheet_panels.rb [new file with mode: 0644]
db/migrate/20130914061319_restruct_comic.rb [new file with mode: 0644]
db/migrate/20130920042226_create_story_sheets.rb [new file with mode: 0644]
spec/controllers/artists_controller_spec.rb
spec/controllers/authors_controller_spec.rb
spec/controllers/balloons_controller_spec.rb
spec/controllers/ground_colors_controller_spec.rb
spec/controllers/ground_pictures_controller_spec.rb
spec/controllers/home_controller_spec.rb
spec/controllers/panels_controller_spec.rb
spec/controllers/sheet_panels_controller_spec.rb [new file with mode: 0644]
spec/controllers/sheets_controller_spec.rb [new file with mode: 0644]
spec/controllers/speech_balloon_templates_controller_spec.rb
spec/controllers/speech_balloons_controller_spec.rb
spec/controllers/speeches_controller_spec.rb
spec/controllers/story_sheets_controller_spec.rb [new file with mode: 0644]
spec/controllers/system_pictures_controller_spec.rb
spec/factories.rb
spec/helpers/sheet_panels_helper_spec.rb [new file with mode: 0644]
spec/helpers/sheets_helper_spec.rb [new file with mode: 0644]
spec/helpers/story_sheets_helper_spec.rb [new file with mode: 0644]
spec/models/artist_spec.rb
spec/models/author_spec.rb
spec/models/balloon_spec.rb
spec/models/comic_spec.rb [new file with mode: 0644]
spec/models/ground_color_spec.rb
spec/models/ground_picture_spec.rb
spec/models/license_group_spec.rb
spec/models/original_picture_spec.rb
spec/models/panel_picture_spec.rb
spec/models/panel_spec.rb
spec/models/resource_picture_spec.rb
spec/models/scroll_panel_spec.rb
spec/models/scroll_spec.rb
spec/models/sheet_panel_spec.rb [new file with mode: 0644]
spec/models/sheet_spec.rb [new file with mode: 0644]
spec/models/speech_balloon_spec.rb
spec/models/speech_balloon_template_spec.rb
spec/models/speech_spec.rb
spec/models/story_sheet_spec.rb [new file with mode: 0644]
spec/models/story_spec.rb [new file with mode: 0644]
spec/models/system_picture_spec.rb

index 603100a..72fab49 100644 (file)
@@ -25,4 +25,4 @@ db/migrate/*_unknown_v??_licenses_attributes.rb
 db/migrate/*_speech_balloon_speech_balloons.rb
 db/migrate/*_speech_balloon_balloons.rb
 db/migrate/*_speech_balloon_speeches.rb
-db/migrate/*_speech_balloon_import_*.rb
+db/migrate/*_speech_balloon_import*.rb
diff --git a/app/assets/javascripts/sheet_panels.js.coffee b/app/assets/javascripts/sheet_panels.js.coffee
new file mode 100644 (file)
index 0000000..7615679
--- /dev/null
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/app/assets/javascripts/sheets.js.coffee b/app/assets/javascripts/sheets.js.coffee
new file mode 100644 (file)
index 0000000..7615679
--- /dev/null
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/app/assets/javascripts/story_sheets.js.coffee b/app/assets/javascripts/story_sheets.js.coffee
new file mode 100644 (file)
index 0000000..7615679
--- /dev/null
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/app/assets/stylesheets/sheet_panels.css.scss b/app/assets/stylesheets/sheet_panels.css.scss
new file mode 100644 (file)
index 0000000..0550fe9
--- /dev/null
@@ -0,0 +1,3 @@
+// Place all the styles related to the sheet_panels controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/sheets.css.scss b/app/assets/stylesheets/sheets.css.scss
new file mode 100644 (file)
index 0000000..bc4b9bf
--- /dev/null
@@ -0,0 +1,3 @@
+// Place all the styles related to the sheets controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/story_sheets.css.scss b/app/assets/stylesheets/story_sheets.css.scss
new file mode 100644 (file)
index 0000000..557cdb4
--- /dev/null
@@ -0,0 +1,3 @@
+// Place all the styles related to the story_sheets controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/controllers/comics_controller.rb b/app/controllers/comics_controller.rb
new file mode 100644 (file)
index 0000000..c5f8546
--- /dev/null
@@ -0,0 +1,131 @@
+class ComicsController < ApplicationController
+  layout 'test' if MagicNumber['test_layout']
+  if MagicNumber['run_mode'] == 0
+    before_filter :authenticate_user, :only => [:new, :create, :edit, :update, :destroy]
+    before_filter :authenticate_author, :only => [:new, :create, :edit, :update, :destroy]
+  else
+    before_filter :authenticate_reader, :only => [:top, :index, :show]
+    before_filter :authenticate_user, :only => [:new, :create, :edit, :update, :destroy]
+    before_filter :authenticate_author, :only => [:new, :create, :edit, :update, :destroy]
+  end
+  before_filter :authenticate_admin!, :only => [:list, :browse]
+
+  def index
+    @page = Comic.page params[:page]
+    @page_size = Comic.page_size params[:page_size]
+    @comics = Comic.list(@page, @page_size)
+    respond_to do |format|
+      format.html {
+        @paginate = Comic.list_paginate(@page, @page_size)
+      }
+      format.json { render json: @comics.to_json(Comic.list_json_opt) }
+      format.atom 
+      format.rss
+    end
+  end
+
+  def show
+    @comic = Comic.show(params[:id], [@user, @admin])
+
+    respond_to do |format|
+      format.html # show.html.erb
+      format.json { render json: @comic.to_json(Comic.show_json_opt) }
+      format.atom 
+      format.rss 
+    end
+  end
+
+  def count
+    @comic = {:count => Comic.visible_count}
+    respond_to do |format|
+      format.json { render json: @comic.to_json }
+    end
+  end
+  
+  def list
+    @comics = Comic.all
+
+    respond_to do |format|
+      format.html { render layout: 'system' }# index.html.erb
+      format.json { render json: @comics }
+    end
+  end
+
+  def browse
+    @comic = Comic.find(params[:id])
+
+    respond_to do |format|
+      format.html { render layout: 'system' } # show.html.erb
+      format.json { render json: @comic }
+    end
+  end
+  
+  def new
+    @comic = Comic.new
+    @comic.supply_default
+    respond_to do |format|
+      format.html # new.html.erb
+      format.js
+      format.json { render json: @comic.to_json(Comic.show_json_opt) }
+    end
+  end
+
+  def edit
+    @comic = Comic.edit(params[:id], @author)
+    respond_to do |format|
+      format.html 
+      format.js
+    end
+  end
+
+  def create
+    @comic = Comic.new
+    @comic.supply_default 
+    @comic.attributes = params[:comic]
+    @comic.overwrite @author
+
+    respond_to do |format|
+      if @comic.save
+        flash[:notice] = I18n.t('flash.notice.created', :model => Comic.model_name.human)
+        format.html { redirect_to @comic }
+        format.json { render json: @comic.to_json(Comic.show_json_opt), status: :created, location: @comic }
+      else
+        flash[:notice] = I18n.t('flash.notice.not_created', :model => Comic.model_name.human)
+        format.html { render action: "new" }
+        format.json { render json: @comic.errors, status: :unprocessable_entity }
+      end
+    end
+  end
+
+  def update
+    @comic = Comic.edit(params[:id], @author)
+    @comic.attributes = params[:comic]
+    @comic.overwrite @author
+    respond_to do |format|
+      if @comic.save
+        flash[:notice] = I18n.t('flash.notice.updated', :model => Comic.model_name.human)
+        format.html { redirect_to @comic }
+        format.json { head :ok }
+      else
+        flash[:notice] = I18n.t('flash.notice.not_updated', :model => Comic.model_name.human)
+        format.html { render action: "edit" }
+        format.json { render json: @comic.errors, status: :unprocessable_entity }
+      end
+    end
+  end
+
+  def destroy
+    @comic = Comic.edit(params[:id], @author)
+    respond_to do |format|
+      if @comic.destroy_with_story
+        flash[:notice] = I18n.t('flash.notice.destroyed', :model => Comic.model_name.human)
+        format.html { redirect_to '/home/comics' }
+        format.json { head :ok }
+      else
+        flash[:notice] = I18n.t('flash.notice.not_destroyed', :model => Comic.model_name.human)
+        format.html { redirect_to @comic }
+        format.json { render json: @comic.errors, status: :unprocessable_entity }
+      end
+    end
+  end
+end
diff --git a/app/controllers/sheet_panels_controller.rb b/app/controllers/sheet_panels_controller.rb
new file mode 100644 (file)
index 0000000..cece2c7
--- /dev/null
@@ -0,0 +1,2 @@
+class SheetPanelsController < ApplicationController
+end
diff --git a/app/controllers/sheets_controller.rb b/app/controllers/sheets_controller.rb
new file mode 100644 (file)
index 0000000..b24b423
--- /dev/null
@@ -0,0 +1,2 @@
+class SheetsController < ApplicationController
+end
diff --git a/app/controllers/stories_controller.rb b/app/controllers/stories_controller.rb
new file mode 100644 (file)
index 0000000..ca77f0d
--- /dev/null
@@ -0,0 +1,157 @@
+class StoriesController < ApplicationController
+  layout 'test' if MagicNumber['test_layout']
+  if MagicNumber['run_mode'] == 0
+    before_filter :authenticate_user, :only => [:new, :create, :edit, :update, :destroy]
+    before_filter :authenticate_author, :only => [:new, :create, :edit, :update, :destroy]
+  else
+    before_filter :authenticate_reader, :only => [:index, :show, :comic]
+    before_filter :authenticate_user, :only => [:new, :create, :edit, :update, :destroy]
+    before_filter :authenticate_author, :only => [:new, :create, :edit, :update, :destroy]
+  end
+  before_filter :authenticate_admin!, :only => [:list, :browse]
+
+  def index
+    @page = Story.page params[:page]
+    @page_size = Story.page_size params[:page_size]
+    @stories = Story.list(@page, @page_size)
+
+    respond_to do |format|
+      format.html {
+        @paginate = Story.list_paginate(@page, @page_size)
+      }
+      format.json { render :json => @stories.to_json(Story.list_json_opt) }
+    end
+  end
+
+  def show
+    @story = Story.show(params[:id], [@user, @admin])
+
+    respond_to do |format|
+      format.html # show.html.erb
+      format.json { render json: @story.story_as_json(@author) }
+    end
+  end
+  
+  def comic
+    @comic = Comic.show(params[:id], [@user, @admin])
+    cnt = Story.count(:conditions => ['comic_id = ?', @comic.id]).to_i
+    @offset = Story.offset cnt, params[:offset]
+    @panel_count = Story.panel_count cnt, params[:count]
+    @stories = Story.play_list(@comic, @author, @offset, @panel_count)
+    respond_to do |format|
+      format.html {
+        @prev_offset = if @offset > 0
+          if @offset - @panel_count < 0
+            0
+          else
+            @offset - @panel_count
+          end
+        else
+          nil
+        end
+        @next_offset = if @offset + @panel_count > cnt
+          nil
+        else
+          @offset + @panel_count
+        end
+        if @author
+          @new_panels = Panel.mylist(@author, 1, 5)
+        end
+      }
+      format.json {render text: Story.list_as_json_text(@stories, @author)}
+      format.jsonp {
+        render :json => "callback(" + @stories.to_json_list + ");"
+      }
+    end
+  end
+  
+  def list
+    @stories = Story.all
+
+    respond_to do |format|
+      format.html { render layout: 'system' }# index.html.erb
+      format.json { render json: @stories }
+    end
+  end
+
+  def browse
+    @story = Story.find(params[:id])
+
+    respond_to do |format|
+      format.html { render layout: 'system' } # show.html.erb
+      format.json { render json: @story }
+    end
+  end
+  
+  def new
+    @story = Story.new 
+    @story.supply_default
+    respond_to do |format|
+      format.html # new.html.erb
+      format.js
+      format.json { render json: @story.story_as_json(@author) }
+    end
+  end
+
+  def edit
+    @story = Story.edit(params[:id], @author)
+    respond_to do |format|
+      format.html 
+      format.js
+    end
+  end
+
+  def create
+    @story = Story.new 
+    @story.supply_default
+    @story.attributes = params[:story]
+    @story.overwrite @author
+    @comic = Comic.edit(@story.comic_id, @author) if @story.comic_id
+    @panel = Panel.show(@story.panel_id, @author) if @story.panel_id
+    
+    respond_to do |format|
+      if @story.store
+        flash[:notice] = I18n.t('flash.notice.created', :model => Story.model_name.human)
+        format.html { redirect_to action: :comic, id: @story.comic_id }
+        format.json { render json: @story.story_as_json(@author) }
+      else
+        flash[:notice] = I18n.t('flash.notice.not_created', :model => Story.model_name.human)
+        format.html { render action: "new" }
+        format.json { render json: @story.errors, status: :unprocessable_entity }
+      end
+    end
+  end
+  
+  def update
+    @story = Story.edit(params[:id], @author)
+    ot = @story.t
+    @story.attributes = params[:story]
+    @story.overwrite @author
+    respond_to do |format|
+      if @story.store ot
+        flash[:notice] = I18n.t('flash.notice.updated', :model => Story.model_name.human)
+        format.html { redirect_to action: :comic, id: @story.comic_id }
+        format.json { head :ok }
+      else
+        flash[:notice] = I18n.t('flash.notice.not_updated', :model => Story.model_name.human)
+        format.html { render action: "edit" }
+        format.json { render json: @story.errors, status: :unprocessable_entity }
+      end
+    end
+  end
+
+  def destroy
+    @story = Story.edit(params[:id], @author)
+    respond_to do |format|
+      if @story.destroy_and_shorten
+        flash[:notice] = I18n.t('flash.notice.destroyed', :model => Story.model_name.human)
+        format.html { redirect_to :controller => 'stories', :action => :comic, :id => @story.comic_id }
+        format.json { head :ok }
+      else
+        flash[:notice] = I18n.t('flash.notice.not_destroyed', :model => Story.model_name.human)
+        format.html { redirect_to story_path(@story) }
+        format.json { render json: @story.errors, status: :unprocessable_entity }
+      end
+    end
+  end
+end
diff --git a/app/controllers/story_sheets_controller.rb b/app/controllers/story_sheets_controller.rb
new file mode 100644 (file)
index 0000000..55d1259
--- /dev/null
@@ -0,0 +1,2 @@
+class StorySheetsController < ApplicationController
+end
diff --git a/app/helpers/comics_helper.rb b/app/helpers/comics_helper.rb
new file mode 100644 (file)
index 0000000..597e65a
--- /dev/null
@@ -0,0 +1,2 @@
+module ComicsHelper
+end
diff --git a/app/helpers/sheet_panels_helper.rb b/app/helpers/sheet_panels_helper.rb
new file mode 100644 (file)
index 0000000..741800d
--- /dev/null
@@ -0,0 +1,2 @@
+module SheetPanelsHelper
+end
diff --git a/app/helpers/sheets_helper.rb b/app/helpers/sheets_helper.rb
new file mode 100644 (file)
index 0000000..e9eb223
--- /dev/null
@@ -0,0 +1,2 @@
+module SheetsHelper
+end
diff --git a/app/helpers/stories_helper.rb b/app/helpers/stories_helper.rb
new file mode 100644 (file)
index 0000000..43e5cd8
--- /dev/null
@@ -0,0 +1,2 @@
+module StoriesHelper
+end
diff --git a/app/helpers/story_sheets_helper.rb b/app/helpers/story_sheets_helper.rb
new file mode 100644 (file)
index 0000000..0a4e02c
--- /dev/null
@@ -0,0 +1,2 @@
+module StorySheetsHelper
+end
index 12f860a..40d0c1d 100644 (file)
@@ -2,6 +2,7 @@ class Author < ActiveRecord::Base
   has_one :artist
   belongs_to :user
   has_many :scrolls
+  has_many :sheets
   has_many :panels
   belongs_to :working_panel, :class_name => "Panel"
   
@@ -146,6 +147,81 @@ class Author < ActiveRecord::Base
     page_size
   end
   
+  def self.default_comic_page_size
+    25
+  end
+  
+  def self.comic_max_page_size
+    100
+  end
+  
+  def self.comic_page_size prm = self.default_comic_page_size
+    page_size = prm.to_i
+    page_size = self.comic_max_page_size if page_size > self.comic_max_page_size
+    page_size = self.default_comic_page_size if page_size < 1
+    page_size
+  end
+  
+  def self.default_story_page_size
+    25
+  end
+  
+  def self.story_max_page_size
+    100
+  end
+  
+  def self.story_page_size prm = self.default_story_page_size
+    page_size = prm.to_i
+    page_size = self.story_max_page_size if page_size > self.story_max_page_size
+    page_size = self.default_story_page_size if page_size < 1
+    page_size
+  end
+  
+  def self.default_story_sheet_page_size
+    25
+  end
+  
+  def self.story_sheet_max_page_size
+    100
+  end
+  
+  def self.story_sheet_page_size prm = self.default_story_sheet_page_size
+    page_size = prm.to_i
+    page_size = self.story_sheet_max_page_size if page_size > self.story_sheet_max_page_size
+    page_size = self.default_story_sheet_page_size if page_size < 1
+    page_size
+  end
+  
+  def self.default_sheet_page_size
+    25
+  end
+  
+  def self.sheet_max_page_size
+    100
+  end
+  
+  def self.sheet_page_size prm = self.default_sheet_page_size
+    page_size = prm.to_i
+    page_size = self.sheet_max_page_size if page_size > self.sheet_max_page_size
+    page_size = self.default_sheet_page_size if page_size < 1
+    page_size
+  end
+  
+  def self.default_sheet_panel_page_size
+    25
+  end
+  
+  def self.sheet_panel_max_page_size
+    100
+  end
+  
+  def self.sheet_panel_page_size prm = self.default_sheet_panel_page_size
+    page_size = prm.to_i
+    page_size = self.sheet_panel_max_page_size if page_size > self.sheet_panel_max_page_size
+    page_size = self.default_sheet_panel_page_size if page_size < 1
+    page_size
+  end
+  
   def self.default_panel_page_size
     25
   end
index 49cce7b..7ae8ba8 100644 (file)
@@ -1,3 +1,168 @@
 #コミック
 class Comic < ActiveRecord::Base
+  has_many :stories
+  belongs_to :author
+  
+  validates :title, :presence => true, :length => {:maximum => 100}
+  validates :visible, :presence => true, :numericality => true, :inclusion => {:in => 0..1}
+  before_validation :valid_encode
+  
+  def valid_encode
+    ['title', 'description'].each do |a|
+      next if attributes[a] == nil
+      raise Pettanr::BadRequest unless attributes[a].valid_encoding?
+    end
+  end
+  
+  def supply_default
+    self.visible = 0 if self.visible.blank?
+  end
+  
+  def overwrite au
+    return false unless au
+    self.author_id = au.id
+  end
+  
+  def own? roles
+    roles = [roles] unless roles.respond_to?(:each)
+    au = Comic.get_author_from_roles roles
+    return false unless au
+    self.author_id == au.id
+  end
+  
+  def visible? roles
+    if MagicNumber['run_mode'] == 0
+      return false unless guest_role_check(roles)
+    else
+      return false unless reader_role_check(roles)
+    end
+    return true if self.own?(roles)
+    self.visible > 0
+  end
+  
+  def self.default_page_size
+    25
+  end
+  
+  def self.max_page_size
+    100
+  end
+  
+  def self.default_panel_size
+    30
+  end
+  
+  def self.max_panel_size
+    200
+  end
+  
+  def self.page prm = nil
+    page = prm.to_i
+    page = 1 if page < 1
+    page
+  end
+  
+  def self.page_size prm = self.default_page_size
+    page_size = prm.to_i
+    page_size = self.max_page_size if page_size > self.max_page_size
+    page_size = self.default_page_size if page_size < 1
+    page_size
+  end
+  
+  def self.list_where
+    'comics.visible > 0'
+  end
+  
+  def self.mylist_where au
+    ['comics.author_id = ?', au.id]
+  end
+  
+  def self.himlist_where au
+    ['comics.author_id = ? and comics.visible > 0', au.id]
+  end
+  
+  def self.list page = 1, page_size = self.default_page_size
+    Comic.where(self.list_where()).includes(Comic.list_opt).order('comics.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.mylist au, page = 1, page_size = Author.default_comic_page_size
+    Comic.where(self.mylist_where(au)).includes(Comic.list_opt).order('comics.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.himlist au, page = 1, page_size = Author.default_comic_page_size
+    Comic.where(self.himlist_where(au)).includes(Comic.list_opt).order('comics.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.list_paginate page = 1, page_size = self.default_page_size
+    Kaminari.paginate_array(Array.new(Comic.where(self.list_where()).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.mylist_paginate au, page = 1, page_size = Author.default_comic_page_size
+    Kaminari.paginate_array(Array.new(Comic.where(self.mylist_where(au)).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.himlist_paginate au, page = 1, page_size = Author.default_comic_page_size
+    Kaminari.paginate_array(Array.new(Comic.where(self.himlist_where(au)).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.list_opt
+    {:stories => {}, :author => {} }
+  end
+  
+  def self.list_json_opt
+    {:include => {:stories => {}, :author => {}} }
+  end
+  
+  def self.show cid, roles
+    opt = {}
+    opt.merge!(Comic.show_opt)
+    res = Comic.find(cid, opt)
+    raise ActiveRecord::Forbidden unless res.visible?(roles)
+    res
+  end
+  
+  def self.edit cid, au
+    opt = {}
+    opt.merge!(Comic.show_opt)
+    res = Comic.find(cid, opt)
+    raise ActiveRecord::Forbidden unless res.own?(au)
+    res
+  end
+  
+  def self.show_opt
+    {:include => {:stories => {}, :author => {}} }
+  end
+  
+  def self.show_json_opt
+    {:include => {:stories => {}, :author => {}} }
+  end
+  
+  def self.visible_count
+    Comic.count 'visible > 0'
+  end
+  
+  def destroy_with_story
+    res = false
+    Comic.transaction do
+      self.stories.each do |story|
+        raise ActiveRecord::Rollback unless story.destroy
+      end
+      raise ActiveRecord::Rollback unless self.destroy
+      res = true
+    end
+    res
+  end
+  
+  def scenario
+    panels.map {|panel|
+      panel.scenario
+    }.join
+  end
+  
+  def plain_scenario
+    panels.map {|panel|
+      panel.plain_scenario
+    }.join
+  end
+  
 end
diff --git a/app/models/sheet.rb b/app/models/sheet.rb
new file mode 100644 (file)
index 0000000..a3b8bff
--- /dev/null
@@ -0,0 +1,172 @@
+class Sheet < ActiveRecord::Base
+  has_many :sheet_panels
+  has_many :story_sheets
+  belongs_to :author
+  
+  validates :caption, :presence => true, :length => {:maximum => 100}
+  validates :visible, :presence => true, :numericality => true, :inclusion => {:in => 0..1}
+  before_validation :valid_encode
+  
+  def valid_encode
+    ['caption', 'description'].each do |a|
+      next if attributes[a] == nil
+      raise Pettanr::BadRequest unless attributes[a].valid_encoding?
+    end
+  end
+  
+  def supply_default
+    self.visible = 0 if self.visible.blank?
+  end
+  
+  def overwrite au
+    return false unless au
+    self.author_id = au.id
+  end
+  
+  def own? roles
+    roles = [roles] unless roles.respond_to?(:each)
+    au = Sheet.get_author_from_roles roles
+    return false unless au
+    self.author_id == au.id
+  end
+  
+  def visible? roles
+    if MagicNumber['run_mode'] == 0
+      return false unless guest_role_check(roles)
+    else
+      return false unless reader_role_check(roles)
+    end
+    return true if self.own?(roles)
+    self.visible > 0
+  end
+  
+  def usable? au
+    visible? au
+  end
+  
+  def self.default_page_size
+    25
+  end
+  
+  def self.max_page_size
+    100
+  end
+  
+  def self.default_panel_size
+    30
+  end
+  
+  def self.max_panel_size
+    200
+  end
+  
+  def self.page prm = nil
+    page = prm.to_i
+    page = 1 if page < 1
+    page
+  end
+  
+  def self.page_size prm = self.default_page_size
+    page_size = prm.to_i
+    page_size = self.max_page_size if page_size > self.max_page_size
+    page_size = self.default_page_size if page_size < 1
+    page_size
+  end
+  
+  def self.list_where
+    'sheets.visible > 0'
+  end
+  
+  def self.mylist_where au
+    ['sheets.author_id = ?', au.id]
+  end
+  
+  def self.himlist_where au
+    ['sheets.author_id = ? and sheets.visible > 0', au.id]
+  end
+  
+  def self.list page = 1, page_size = self.default_page_size
+    Sheet.where(self.list_where()).includes(Sheet.list_opt).order('sheets.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.mylist au, page = 1, page_size = Author.default_sheet_page_size
+    Sheet.where(self.mylist_where(au)).includes(Sheet.list_opt).order('sheets.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.himlist au, page = 1, page_size = Author.default_sheet_page_size
+    Sheet.where(self.himlist_where(au)).includes(Sheet.list_opt).order('sheets.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.list_paginate page = 1, page_size = self.default_page_size
+    Kaminari.paginate_array(Array.new(Sheet.where(self.list_where()).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.mylist_paginate au, page = 1, page_size = Author.default_sheet_page_size
+    Kaminari.paginate_array(Array.new(Sheet.where(self.mylist_where(au)).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.himlist_paginate au, page = 1, page_size = Author.default_sheet_page_size
+    Kaminari.paginate_array(Array.new(Sheet.where(self.himlist_where(au)).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.list_opt
+    {:sheet_panels => {:panel => {}}, :author => {} }
+  end
+  
+  def self.list_json_opt
+    {:include => {:sheet_panels => {:include => {:panel => {}}}, :author => {}}}
+  end
+  
+  def self.show sid, roles
+    opt = {}
+    opt.merge!(Sheet.show_opt)
+    res = Sheet.find(sid, opt)
+    raise ActiveRecord::Forbidden unless res.visible?(roles)
+    res
+  end
+  
+  def self.edit sid, au
+    opt = {}
+    opt.merge!(Sheet.show_opt)
+    res = Sheet.find(sid, opt)
+    raise ActiveRecord::Forbidden unless res.own?(au)
+    res
+  end
+  
+  def self.show_opt
+    {:include => {:sheet_panels => {:panel => {}}, :author => {}}}
+  end
+  
+  def self.show_json_opt
+    {:include => {:sheet_panels => {:include => {:panel => {}}}, :author => {}}}
+  end
+  
+  def self.visible_count
+    Sheet.count 'visible > 0'
+  end
+  
+  def destroy_with_sheet_panel
+    res = false
+    Sheet.transaction do
+      self.sheet_panels.each do |sheet_panel|
+        raise ActiveRecord::Rollback unless sheet_panel.destroy
+      end
+      raise ActiveRecord::Rollback unless self.destroy
+      res = true
+    end
+    res
+  end
+  
+  def scenario
+    panels.map {|panel|
+      panel.scenario
+    }.join
+  end
+  
+  def plain_scenario
+    panels.map {|panel|
+      panel.plain_scenario
+    }.join
+  end
+  
+end
diff --git a/app/models/sheet_panel.rb b/app/models/sheet_panel.rb
new file mode 100644 (file)
index 0000000..39d251b
--- /dev/null
@@ -0,0 +1,319 @@
+class SheetPanel < ActiveRecord::Base
+  belongs_to :author
+  belongs_to :panel
+  belongs_to :sheet
+  
+  validates :sheet_id, :presence => true, :numericality => true, :existence => {:both => false}
+  validates :panel_id, :presence => true, :numericality => true, :existence => {:both => false}
+  validates :author_id, :presence => true, :numericality => true, :existence => {:both => false}
+  validates :x, :presence => true, :numericality => true
+  validates :y, :presence => true, :numericality => true
+  validates :z, :presence => true, :numericality => {:greater_than => 0}
+  validates :t, :presence => true, :numericality => {:greater_than_or_equal_to => 0}
+  
+  def supply_default
+    self.sheet_id = nil
+    self.panel_id = nil
+    self.t = nil
+  end
+  
+  def overwrite au
+    return false unless au
+    self.author_id = au.id
+  end
+  
+  def own? roles
+    roles = [roles] unless roles.respond_to?(:each)
+    au = SheetPanel.get_author_from_roles roles
+    return false unless au
+    self.author_id == au.id
+  end
+  
+  def visible? roles
+    if MagicNumber['run_mode'] == 0
+      return false unless guest_role_check(roles)
+    else
+      return false unless reader_role_check(roles)
+    end
+    return true if self.sheet.own?(roles)
+    self.sheet.visible? roles
+  end
+  
+  def self.default_panel_size
+    30
+  end
+  
+  def self.max_panel_size
+    200
+  end
+  
+  def self.offset cnt, prm = nil
+    offset = prm.to_i
+    offset = cnt - 1 if offset >= cnt
+    offset = cnt - offset.abs if offset < 0
+    offset = 0 if offset < 0
+    offset
+  end
+  
+  def self.panel_count cnt, prm = self.default_panel_size
+    count = prm.to_i
+    count = self.max_panel_size if count > self.max_panel_size
+    count = self.default_panel_size if count < 1
+    count
+  end
+  
+  def self.default_page_size
+    25
+  end
+  
+  def self.max_page_size
+    100
+  end
+  
+  def self.page prm = nil
+    page = prm.to_i
+    page = 1 if page < 1
+    page
+  end
+  
+  def self.page_size prm = self.default_page_size
+    page_size = prm.to_i
+    page_size = self.max_page_size if page_size > self.max_page_size
+    page_size = self.default_page_size if page_size < 1
+    page_size
+  end
+  
+  def self.play_list_where cid
+    ['sheet_panels.sheet_id = ?', cid]
+  end
+  
+  def self.list_where
+    'sheets.visible > 0'
+  end
+  
+  def self.mylist_where au
+    ['sheet_panels.author_id = ?', au.id]
+  end
+  
+  def self.himlist_where au
+    ['sheet_panels.author_id = ? and sheets.visible > 0', au.id]
+  end
+  
+  def self.play_list sheet, author, offset = 0, limit = SheetPanel.default_panel_size
+    SheetPanel.where(self.play_list_where(sheet.id)).includes(SheetPanel.list_opt).order('sheet_panels.t').offset(offset).limit(limit)
+  end
+  
+  def self.list page = 1, page_size = self.default_page_size
+    SheetPanel.where(self.list_where()).includes(SheetPanel.list_opt).order('sheet_panels.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.mylist au, page = 1, page_size = Author.default_sheet_panel_page_size
+    SheetPanel.where(self.mylist_where(au)).includes(SheetPanel.list_opt).order('sheet_panels.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.himlist au, page = 1, page_size = Author.default_sheet_panel_page_size
+    SheetPanel.where(self.himlist_where(au)).includes(SheetPanel.list_opt).order('sheet_panels.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.list_paginate page = 1, page_size = self.default_page_size
+    Kaminari.paginate_array(Array.new(SheetPanel.where(self.list_where()).includes(SheetPanel.list_opt).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.mylist_paginate au, page = 1, page_size = Author.default_sheet_panel_page_size
+    Kaminari.paginate_array(Array.new(SheetPanel.where(self.mylist_where(au)).includes(SheetPanel.list_opt).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.himlist_paginate au, page = 1, page_size = Author.default_sheet_panel_page_size
+    Kaminari.paginate_array(Array.new(SheetPanel.where(self.himlist_where(au)).includes(SheetPanel.list_opt).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.list_opt
+    {
+      :author => {}, 
+      :sheet => {
+        :author => {}
+      }, 
+      :panel => {
+        :author => {}, 
+        :panel_pictures => {:picture => {:artist => {}, :license => {}}}, 
+        :speech_balloons =>{:balloon => {}, :speech => {}}
+      }
+    }
+  end
+  
+  def self.list_json_opt
+    {:include => {
+      :author => {}, 
+      :sheet => {
+        :author => {}
+      }, 
+      :panel => {
+        :author => {}, 
+        :panel_pictures => {:picture => {:artist => {}, :license => {}}}, 
+        :speech_balloons =>{:balloon => {}, :speech => {}}
+      }
+    }}
+  end
+  
+  def self.show spid, roles
+    opt = {}
+    opt.merge!(SheetPanel.show_opt)
+    res = SheetPanel.find spid, opt
+    raise ActiveRecord::Forbidden unless res.visible?(roles)
+    res
+  end
+  
+  def self.edit spid, au
+    opt = {}
+    opt.merge!(SheetPanel.show_opt)
+    res = SheetPanel.find spid, opt
+    raise ActiveRecord::Forbidden unless res.own?(au)
+    res
+  end
+  
+  def self.show_opt
+    {:include => {
+      :author => {}, 
+      :sheet => {
+        :author => {}
+      }, 
+      :panel => {
+        :author => {}, 
+        :panel_pictures => {:picture => {:artist => {}, :license => {}}}, 
+        :speech_balloons =>{:balloon => {}, :speech => {}}
+      }
+    }}
+  end
+  
+  def elements
+    self.panel.elements
+  end
+  
+  def sheet_panel_as_json au
+    panel_include = if self.panel and self.panel.visible?(au)
+      {:include => {:author => {}}, :methods => :elements}
+    else
+      {:include => {:author => {}}}
+    end
+    self.to_json({:include => {:sheet => {:include => {:author => {}}}, :author => {}, :panel => panel_include}})
+  end
+  
+  def self.list_as_json_text ary, au
+    '[' + ary.map {|i| i.sheet_panel_as_json(au) }.join(',') + ']'
+  end
+  
+  def self.licensed_pictures sheet_panels
+    r = {}
+    sheet_panels.each do |sheet_panel|
+      r.merge!(sheet_panel.panel.licensed_pictures) if sheet_panel.panel
+    end
+    r
+  end
+  
+  def self.new_t sheet_id
+    r = SheetPanel.max_t(sheet_id)
+    r.blank? ? 0 : r.to_i + 1
+  end
+  
+  def self.max_t sheet_id
+    SheetPanel.maximum(:t, :conditions => ['sheet_id = ?', sheet_id])
+  end
+  
+  def self.find_t sheet_id, t
+    SheetPanel.find(:first, :conditions => ['sheet_id = ? and t = ?', sheet_id, t])
+  end
+  
+  def self.collect_t sheet_panel
+    r = SheetPanel.find(:all, :conditions => ['sheet_id = ?', sheet_panel.sheet_id], :order => 't')
+    r.map {|sp| sp.t}
+  end
+  
+  def self.serial? ary
+    i = 0
+    ary.compact.sort.each do |t|
+      break false unless t == i
+      i += 1
+    end
+    ary.compact.size == i
+  end
+  
+  def self.validate_t sheet_panel
+    SheetPanel.serial?(SheetPanel.collect_t(sheet_panel))
+  end
+  
+  def insert_shift
+    SheetPanel.update_all('t = t + 1', ['sheet_id = ? and t >= ?', self.sheet_id, self.t])
+  end
+  
+  def lesser_shift old_t
+    self.t = 0 if self.t < 0
+    SheetPanel.update_all('t = t + 1', ['sheet_id = ? and (t >= ? and t < ?)', self.sheet_id, self.t, old_t])
+  end
+  
+  def higher_shift old_t
+    nf = SheetPanel.find_t(self.sheet_id, self.t)
+    max_t = SheetPanel.max_t(self.sheet_id).to_i
+    self.t = max_t if self.t > max_t
+    SheetPanel.update_all('t = t - 1', ['sheet_id = ? and (t > ? and t <= ?)', self.sheet_id, old_t, self.t])
+  end
+  
+  def update_shift old_t
+    if self.t > old_t
+      higher_shift old_t
+    else
+      lesser_shift old_t
+    end
+  end
+  
+  def rotate old_t = nil
+    if self.new_record?
+      if self.t.blank?
+        self.t = SheetPanel.new_t self.sheet_id
+      else
+        self.insert_shift
+      end
+    else
+      if self.t.blank?
+      else
+        self.update_shift old_t
+      end
+    end
+  end
+  
+  def allow?
+    return nil if self.sheet_id == nil or self.panel_id == nil
+    self.sheet.own?(self.author) and self.panel.usable?(self.author)
+  end
+  
+  def store old_t = nil
+    res = false
+    SheetPanel.transaction do
+      case self.allow?
+      when true
+        self.rotate old_t
+      when false
+        raise ActiveRecord::Forbidden
+      else
+      end
+      res = self.save
+      raise ActiveRecord::Rollback unless res
+      res = SheetPanel.validate_t(self) 
+      unless res
+        self.errors.add :t, 'unserialized'
+        raise ActiveRecord::Rollback 
+      end
+    end
+    res
+  end
+  
+  def destroy_and_shorten
+    res = false
+    SheetPanel.transaction do
+      SheetPanel.update_all('t = t - 1', ['sheet_id = ? and (t > ?)', self.sheet_id, self.t])
+      raise ActiveRecord::Rollback unless self.destroy
+      res = true
+    end
+    res
+  end
+  
+end
index 7f646f8..b655b67 100644 (file)
@@ -1,3 +1,269 @@
 #ストーリー
 class Story < ActiveRecord::Base
+  belongs_to :author
+  has_many :story_sheets
+  belongs_to :comic
+  
+  validates :comic_id, :presence => true, :numericality => true, :existence => {:both => false}
+  validates :title, :presence => true, :length => {:maximum => 100}
+  validates :visible, :presence => true, :numericality => true, :inclusion => {:in => 0..1}
+  validates :author_id, :presence => true, :numericality => true, :existence => {:both => false}
+  validates :t, :presence => true, :numericality => {:greater_than_or_equal_to => 0}
+  before_validation :valid_encode
+  
+  def valid_encode
+    ['title', 'description'].each do |a|
+      next if attributes[a] == nil
+      raise Pettanr::BadRequest unless attributes[a].valid_encoding?
+    end
+  end
+  
+  def supply_default
+    self.comic_id = nil
+    self.visible = 0 if self.visible.blank?
+    self.t = nil
+  end
+  
+  def overwrite au
+    return false unless au
+    self.author_id = au.id
+  end
+  
+  def own? roles
+    roles = [roles] unless roles.respond_to?(:each)
+    au = Story.get_author_from_roles roles
+    return false unless au
+    self.author_id == au.id
+  end
+  
+  def visible? roles
+    if MagicNumber['run_mode'] == 0
+      return false unless guest_role_check(roles)
+    else
+      return false unless reader_role_check(roles)
+    end
+    return true if self.own?(roles)
+    self.visible > 0
+  end
+  
+  def self.default_page_size
+    25
+  end
+  
+  def self.max_page_size
+    100
+  end
+  
+  def self.default_panel_size
+    30
+  end
+  
+  def self.max_panel_size
+    200
+  end
+  
+  def self.page prm = nil
+    page = prm.to_i
+    page = 1 if page < 1
+    page
+  end
+  
+  def self.page_size prm = self.default_page_size
+    page_size = prm.to_i
+    page_size = self.max_page_size if page_size > self.max_page_size
+    page_size = self.default_page_size if page_size < 1
+    page_size
+  end
+  
+  def self.list_where
+    'stories.visible > 0'
+  end
+  
+  def self.mylist_where au
+    ['stories.author_id = ?', au.id]
+  end
+  
+  def self.himlist_where au
+    ['stories.author_id = ? and stories.visible > 0', au.id]
+  end
+  
+  def self.list page = 1, page_size = self.default_page_size
+    Story.where(self.list_where()).includes(Story.list_opt).order('stories.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.mylist au, page = 1, page_size = Author.default_story_page_size
+    Story.where(self.mylist_where(au)).includes(Story.list_opt).order('stories.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.himlist au, page = 1, page_size = Author.default_story_page_size
+    Story.where(self.himlist_where(au)).includes(Story.list_opt).order('stories.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.list_paginate page = 1, page_size = self.default_page_size
+    Kaminari.paginate_array(Array.new(Story.where(self.list_where()).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.mylist_paginate au, page = 1, page_size = Author.default_story_page_size
+    Kaminari.paginate_array(Array.new(Story.where(self.mylist_where(au)).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.himlist_paginate au, page = 1, page_size = Author.default_story_page_size
+    Kaminari.paginate_array(Array.new(Story.where(self.himlist_where(au)).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.list_opt
+    {:author => {}, :story_sheets => {:sheet => {}, :author => {}} }
+  end
+  
+  def self.list_json_opt
+    {:include => {:author => {}, :story_sheets => {:include => {:sheet => {}, :author => {}}} }}
+  end
+  
+  def self.show sid, roles
+    opt = {}
+    opt.merge!(Story.show_opt)
+    res = Story.find(sid, opt)
+    raise ActiveRecord::Forbidden unless res.visible?(roles)
+    res
+  end
+  
+  def self.edit sid, au
+    opt = {}
+    opt.merge!(Story.show_opt)
+    res = Story.find(sid, opt)
+    raise ActiveRecord::Forbidden unless res.own?(au)
+    res
+  end
+  
+  def self.show_opt
+    {:include => {:author => {}, :story_sheets => {:sheet => {}, :author => {}} }}
+  end
+  
+  def self.show_json_opt
+    {:include => {:author => {}, :story_sheets => {:include => {:sheet => {}, :author => {}}} }}
+  end
+  
+  def self.visible_count
+    Story.count 'visible > 0'
+  end
+  
+  def destroy_with_story_sheet
+    res = false
+    Story.transaction do
+      self.story_sheets.each do |story_sheet|
+        raise ActiveRecord::Rollback unless story_sheet.destroy
+      end
+      raise ActiveRecord::Rollback unless self.destroy
+      res = true
+    end
+    res
+  end
+  
+  def self.new_t comic_id
+    r = Story.max_t(comic_id)
+    r.blank? ? 0 : r.to_i + 1
+  end
+  
+  def self.max_t comic_id
+    Story.maximum(:t, :conditions => ['comic_id = ?', comic_id])
+  end
+  
+  def self.find_t comic_id, t
+    Story.find(:first, :conditions => ['comic_id = ? and t = ?', comic_id, t])
+  end
+  
+  def self.collect_t story
+    r = Story.find(:all, :conditions => ['comic_id = ?', story.comic_id], :order => 't')
+    r.map {|s| s.t}
+  end
+  
+  def self.serial? ary
+    i = 0
+    ary.compact.sort.each do |t|
+      break false unless t == i
+      i += 1
+    end
+    ary.compact.size == i
+  end
+  
+  def self.validate_t story
+    Story.serial?(Story.collect_t(story))
+  end
+  
+  def insert_shift
+    Story.update_all('t = t + 1', ['comic_id = ? and t >= ?', self.comic_id, self.t])
+  end
+  
+  def lesser_shift old_t
+    self.t = 0 if self.t < 0
+    Story.update_all('t = t + 1', ['comic_id = ? and (t >= ? and t < ?)', self.comic_id, self.t, old_t])
+  end
+  
+  def higher_shift old_t
+    nf = Story.find_t(self.comic_id, self.t)
+    max_t = Story.max_t(self.comic_id).to_i
+    self.t = max_t if self.t > max_t
+    Story.update_all('t = t - 1', ['comic_id = ? and (t > ? and t <= ?)', self.comic_id, old_t, self.t])
+  end
+  
+  def update_shift old_t
+    if self.t > old_t
+      higher_shift old_t
+    else
+      lesser_shift old_t
+    end
+  end
+  
+  def rotate old_t = nil
+    if self.new_record?
+      if self.t.blank?
+        self.t = Story.new_t self.comic_id
+      else
+        self.insert_shift
+      end
+    else
+      if self.t.blank?
+      else
+        self.update_shift old_t
+      end
+    end
+  end
+  
+  def allow?
+    return nil if self.comic_id == nil
+    self.comic.own?(self.author)
+  end
+  
+  def store old_t = nil
+    res = false
+    Story.transaction do
+      case self.allow?
+      when true
+        self.rotate old_t
+      when false
+        raise ActiveRecord::Forbidden
+      else
+      end
+      res = self.save
+      raise ActiveRecord::Rollback unless res
+      res = Story.validate_t(self) 
+      unless res
+        self.errors.add :t, 'unserialized'
+        raise ActiveRecord::Rollback 
+      end
+    end
+    res
+  end
+  
+  def destroy_and_shorten
+    res = false
+    Story.transaction do
+      Story.update_all('t = t - 1', ['comic_id = ? and (t > ?)', self.comic_id, self.t])
+      raise ActiveRecord::Rollback unless self.destroy_with_story_sheet
+      res = true
+    end
+    res
+  end
+  
+  
 end
diff --git a/app/models/story_sheet.rb b/app/models/story_sheet.rb
new file mode 100644 (file)
index 0000000..f988d29
--- /dev/null
@@ -0,0 +1,289 @@
+class StorySheet < ActiveRecord::Base
+  belongs_to :author
+  belongs_to :story
+  belongs_to :sheet
+  
+  validates :story_id, :presence => true, :numericality => true, :existence => {:both => false}
+  validates :sheet_id, :presence => true, :numericality => true, :existence => {:both => false}
+  validates :author_id, :presence => true, :numericality => true, :existence => {:both => false}
+  validates :t, :presence => true, :numericality => {:greater_than_or_equal_to => 0}
+  
+  def supply_default
+    self.story_id = nil
+    self.sheet_id = nil
+    self.t = nil
+  end
+  
+  def overwrite au
+    return false unless au
+    self.author_id = au.id
+  end
+  
+  def own? roles
+    roles = [roles] unless roles.respond_to?(:each)
+    au = StorySheet.get_author_from_roles roles
+    return false unless au
+    self.author_id == au.id
+  end
+  
+  def visible? roles
+    if MagicNumber['run_mode'] == 0
+      return false unless guest_role_check(roles)
+    else
+      return false unless reader_role_check(roles)
+    end
+    return true if self.story.own?(roles)
+    self.story.visible? roles
+  end
+  
+  def self.default_sheet_size
+    30
+  end
+  
+  def self.max_sheet_size
+    200
+  end
+  
+  def self.offset cnt, prm = nil
+    offset = prm.to_i
+    offset = cnt - 1 if offset >= cnt
+    offset = cnt - offset.abs if offset < 0
+    offset = 0 if offset < 0
+    offset
+  end
+  
+  def self.sheet_count cnt, prm = self.default_sheet_size
+    count = prm.to_i
+    count = self.max_sheet_size if count > self.max_sheet_size
+    count = self.default_sheet_size if count < 1
+    count
+  end
+  
+  def self.default_page_size
+    25
+  end
+  
+  def self.max_page_size
+    100
+  end
+  
+  def self.page prm = nil
+    page = prm.to_i
+    page = 1 if page < 1
+    page
+  end
+  
+  def self.page_size prm = self.default_page_size
+    page_size = prm.to_i
+    page_size = self.max_page_size if page_size > self.max_page_size
+    page_size = self.default_page_size if page_size < 1
+    page_size
+  end
+  
+  def self.play_list_where cid
+    ['story_sheets.story_id = ?', cid]
+  end
+  
+  def self.list_where
+    'stories.visible > 0'
+  end
+  
+  def self.mylist_where au
+    ['story_sheets.author_id = ?', au.id]
+  end
+  
+  def self.himlist_where au
+    ['story_sheets.author_id = ? and stories.visible > 0', au.id]
+  end
+  
+  def self.play_list story, author, offset = 0, limit = StorySheet.default_sheet_size
+    StorySheet.where(self.play_list_where(story.id)).includes(StorySheet.list_opt).order('story_sheets.t').offset(offset).limit(limit)
+  end
+  
+  def self.list page = 1, page_size = self.default_page_size
+    StorySheet.where(self.list_where()).includes(StorySheet.list_opt).order('story_sheets.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.mylist au, page = 1, page_size = Author.default_story_sheet_page_size
+    StorySheet.where(self.mylist_where(au)).includes(StorySheet.list_opt).order('story_sheets.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.himlist au, page = 1, page_size = Author.default_story_sheet_page_size
+    StorySheet.where(self.himlist_where(au)).includes(StorySheet.list_opt).order('story_sheets.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.list_paginate page = 1, page_size = self.default_page_size
+    Kaminari.paginate_array(Array.new(StorySheet.where(self.list_where()).includes(StorySheet.list_opt).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.mylist_paginate au, page = 1, page_size = Author.default_story_sheet_page_size
+    Kaminari.paginate_array(Array.new(StorySheet.where(self.mylist_where(au)).includes(StorySheet.list_opt).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.himlist_paginate au, page = 1, page_size = Author.default_story_sheet_page_size
+    Kaminari.paginate_array(Array.new(StorySheet.where(self.himlist_where(au)).includes(StorySheet.list_opt).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.list_opt
+    {
+      :author => {}, 
+      :story => {
+        :author => {}
+      }, 
+      :sheet => {
+        :author => {},
+      }
+    }
+  end
+  
+  def self.list_json_opt
+    {:include => {
+      :author => {}, 
+      :story => {:include => {:author => {}}} ,
+      :sheet => {:include => {:author => {}}} 
+    }}
+  end
+  
+  def self.show spid, roles
+    opt = {}
+    opt.merge!(StorySheet.show_opt)
+    res = StorySheet.find spid, opt
+    raise ActiveRecord::Forbidden unless res.visible?(roles)
+    res
+  end
+  
+  def self.edit spid, au
+    opt = {}
+    opt.merge!(StorySheet.show_opt)
+    res = StorySheet.find spid, opt
+    raise ActiveRecord::Forbidden unless res.own?(au)
+    res
+  end
+  
+  def self.show_opt
+    {:include => {
+      :author => {}, 
+      :story => {
+        :author => {}
+      }, 
+      :sheet => {
+        :author => {} 
+      }
+    }}
+  end
+  
+  def self.show_json_opt
+    {:include => {
+      :author => {}, 
+      :story => {:include => {:author => {}}} ,
+      :sheet => {:include => {:author => {}}} 
+    }}
+  end
+  
+  def self.new_t story_id
+    r = StorySheet.max_t(story_id)
+    r.blank? ? 0 : r.to_i + 1
+  end
+  
+  def self.max_t story_id
+    StorySheet.maximum(:t, :conditions => ['story_id = ?', story_id])
+  end
+  
+  def self.find_t story_id, t
+    StorySheet.find(:first, :conditions => ['story_id = ? and t = ?', story_id, t])
+  end
+  
+  def self.collect_t story_sheet
+    r = StorySheet.find(:all, :conditions => ['story_id = ?', story_sheet.story_id], :order => 't')
+    r.map {|sp| sp.t}
+  end
+  
+  def self.serial? ary
+    i = 0
+    ary.compact.sort.each do |t|
+      break false unless t == i
+      i += 1
+    end
+    ary.compact.size == i
+  end
+  
+  def self.validate_t story_sheet
+    StorySheet.serial?(StorySheet.collect_t(story_sheet))
+  end
+  
+  def insert_shift
+    StorySheet.update_all('t = t + 1', ['story_id = ? and t >= ?', self.story_id, self.t])
+  end
+  
+  def lesser_shift old_t
+    self.t = 0 if self.t < 0
+    StorySheet.update_all('t = t + 1', ['story_id = ? and (t >= ? and t < ?)', self.story_id, self.t, old_t])
+  end
+  
+  def higher_shift old_t
+    nf = StorySheet.find_t(self.story_id, self.t)
+    max_t = StorySheet.max_t(self.story_id).to_i
+    self.t = max_t if self.t > max_t
+    StorySheet.update_all('t = t - 1', ['story_id = ? and (t > ? and t <= ?)', self.story_id, old_t, self.t])
+  end
+  
+  def update_shift old_t
+    if self.t > old_t
+      higher_shift old_t
+    else
+      lesser_shift old_t
+    end
+  end
+  
+  def rotate old_t = nil
+    if self.new_record?
+      if self.t.blank?
+        self.t = StorySheet.new_t self.story_id
+      else
+        self.insert_shift
+      end
+    else
+      if self.t.blank?
+      else
+        self.update_shift old_t
+      end
+    end
+  end
+  
+  def allow?
+    return nil if self.story_id == nil or self.sheet_id == nil
+    self.story.own?(self.author) and self.sheet.usable?(self.author)
+  end
+  
+  def store old_t = nil
+    res = false
+    StorySheet.transaction do
+      case self.allow?
+      when true
+        self.rotate old_t
+      when false
+        raise ActiveRecord::Forbidden
+      else
+      end
+      res = self.save
+      raise ActiveRecord::Rollback unless res
+      res = StorySheet.validate_t(self) 
+      unless res
+        self.errors.add :t, 'unserialized'
+        raise ActiveRecord::Rollback 
+      end
+    end
+    res
+  end
+  
+  def destroy_and_shorten
+    res = false
+    StorySheet.transaction do
+      StorySheet.update_all('t = t - 1', ['story_id = ? and (t > ?)', self.story_id, self.t])
+      raise ActiveRecord::Rollback unless self.destroy
+      res = true
+    end
+    res
+  end
+  
+end
diff --git a/app/views/comics/_form.html.erb b/app/views/comics/_form.html.erb
new file mode 100644 (file)
index 0000000..84b983c
--- /dev/null
@@ -0,0 +1,23 @@
+<%= form_for(@comic) do |f| %>
+  <%= render 'system/error_explanation', :obj => @comic %>
+
+  <div class="field">
+    <%= f.label :title %><br />
+    <%= f.text_field :title %>
+  </div>
+  <div class="field">
+    <%= f.label :description %><br />
+    <%= f.text_area :description %>
+  </div>
+  <div class="field">
+    <%= f.label :visible %><br />
+    <%= f.collection_select :visible, t_select_items(MagicNumber['comic_visible_items']), :last, :first, :html => {:selected => @comic.visible} %>
+  </div>
+  <div class="field">
+    <%= f.label :author_id %><br />
+    <%= h @author.name %>
+  </div>
+  <div class="actions">
+    <%= f.submit %>
+  </div>
+<% end %>
diff --git a/app/views/comics/_list_item.html.erb b/app/views/comics/_list_item.html.erb
new file mode 100644 (file)
index 0000000..2e9d719
--- /dev/null
@@ -0,0 +1,25 @@
+<tr>
+  <td>
+    <%= link_to comic_icon(:object => comic, :size => 25), comic_path(comic) %>
+    <%= link_to h(truncate(comic.title, :length => 40)), :controller => 'stories', :action => :comic, :id => comic.id %>
+    (<%= comic.stories.size -%>)
+  </td>
+  <td>
+    <%= distance_of_time_in_words_to_now comic.updated_at %>
+  </td>
+  <td>
+    <%= link_to author_icon(:object => comic.author, :size => 25), comic_path(comic.author) %>
+    <%= link_to h(truncate(comic.author.name, :length => 12)), author_path(comic.author) %>
+  </td>
+  <td>
+    <% if comic.own? author %>
+      <%= link_to t('link.edit'), edit_comic_path(comic) %>
+      <%= link_to t('link.destroy'), comic_path(comic), :method => :delete %>
+    <% end %>
+  </td>
+</tr>
+<tr>
+  <td colspan="4">
+    <%= h(truncate(comic.description, :length => 40)) %>
+  </td>
+</tr>
diff --git a/app/views/comics/browse.html.erb b/app/views/comics/browse.html.erb
new file mode 100644 (file)
index 0000000..c93e622
--- /dev/null
@@ -0,0 +1,34 @@
+<h1><%= t '.title' -%></h1>
+<p>
+  <b><%= t_m 'Comic.id' -%>:</b>
+  <%= h @comic.id %>
+</p>
+
+<p>
+  <b><%= t_m 'Comic.title' -%>:</b>
+  <%= h @comic.title %>
+</p>
+
+<p>
+  <b><%= t_m 'Comic.visible' -%>:</b>
+  <%= @comic.visible %>
+  <%= t_selected_item('comic_visible_items', @comic.visible) %>
+</p>
+
+<p>
+  <b><%= t_m 'Comic.author_id' -%>:</b>
+  <%= @comic.author_id %>
+  <%= link_to h(@comic.author.name), browse_author_path(@comic.author) %>
+</p>
+
+<p>
+  <b><%= t_m 'Comic.created_at' -%>:</b>
+  <%= l @comic.created_at %>
+</p>
+
+<p>
+  <b><%= t_m 'Comic.updated_at' -%>:</b>
+  <%= l @comic.updated_at %>
+</p>
+
+<%= link_to t('link.back'), :action => :list %>
diff --git a/app/views/comics/edit.html.erb b/app/views/comics/edit.html.erb
new file mode 100644 (file)
index 0000000..1d67f75
--- /dev/null
@@ -0,0 +1,4 @@
+<h1><%= t('.title') %></h1>
+<p id="notice"><%= notice %></p>
+
+<%= render 'form' %>
diff --git a/app/views/comics/edit.js.erb b/app/views/comics/edit.js.erb
new file mode 100644 (file)
index 0000000..ab2a7d2
--- /dev/null
@@ -0,0 +1 @@
+$("#newcomic").html("<%= escape_javascript(render('form')) -%>");
diff --git a/app/views/comics/index.atom.builder b/app/views/comics/index.atom.builder
new file mode 100644 (file)
index 0000000..a1e9b0c
--- /dev/null
@@ -0,0 +1,25 @@
+atom_feed(
+  :language => 'ja-JP',
+  :root_url => root_url,
+  :url => root_url(:format => :atom),
+  :id => root_url
+) do |feed|
+  feed.title MagicNumber['profile']['users']['caption']
+  feed.subtitle sanitize(MagicNumber['profile']['users']['description'], :tags => %w(a p img br))
+  feed.updated Time.now
+  feed.author {|author| author.name(MagicNumber['profile']['web_master'])}
+
+  @comics.each do |comic|
+    feed.entry(
+      comic,
+      :url => comic_url(comic),
+      :id => comic_url(comic),
+      :published => comic.updated_at,
+      :updated => comic.updated_at
+    ) do |item|
+      item.title(comic.title)
+      item.content(comic.description, :type => 'html')
+      item.author {|author| author.name(comic.author.name)}
+    end
+  end
+end
diff --git a/app/views/comics/index.html.erb b/app/views/comics/index.html.erb
new file mode 100644 (file)
index 0000000..cef2ab9
--- /dev/null
@@ -0,0 +1,9 @@
+<h1><%= t '.title' -%></h1>
+
+<table>
+  <% @comics.each do |comic| %>
+    <%= render 'list_item', :comic => comic, :author => @author %>
+  <% end %>
+</table>
+<%= paginate(@paginate) %>
+<%= link_to t('comics.new.title'), new_comic_path %>
diff --git a/app/views/comics/index.rss.builder b/app/views/comics/index.rss.builder
new file mode 100644 (file)
index 0000000..b319a9d
--- /dev/null
@@ -0,0 +1,24 @@
+xml.instruct!
+xml.rss("version"    => "2.0",
+        "xmlns:dc"   => "http://purl.org/dc/elements/1.1/",
+        "xmlns:atom" => "http://www.w3.org/2005/Atom") do
+  xml.channel do
+    xml.title       MagicNumber['profile']['users']['caption']
+    xml.link        root_url
+    xml.pubDate     Time.now.rfc822
+    xml.description sanitize(MagicNumber['profile']['users']['description'], :tags => %w(a p img br))
+    xml.atom :link, "href" => @rss_url, "rel" => "self", "type" => "application/rss+xml"
+    @comics.each do |comic|
+      xml.item do
+        xml.title        comic.title
+        xml.link         comic_url(comic)
+        xml.guid         comic_url(comic)
+        xml.description  comic.description
+        xml.pubDate      comic.updated_at.to_formatted_s(:rfc822)
+        xml.dc :creator, comic.author.name
+      end
+    end
+  end
+end
diff --git a/app/views/comics/list.html.erb b/app/views/comics/list.html.erb
new file mode 100644 (file)
index 0000000..43fdf7e
--- /dev/null
@@ -0,0 +1,24 @@
+<h1><%= t '.title' -%></h1>
+
+<table>
+  <tr>
+    <th><%= t_m 'Comic.id' -%></th>
+    <th><%= t_m 'Comic.title' -%></th>
+    <th><%= t_m 'Comic.visible' -%></th>
+    <th><%= t_m 'Comic.author_id' -%></th>
+    <th><%= t_m 'Comic.created_at' -%></th>
+    <th><%= t_m 'Comic.updated_at' -%></th>
+  </tr>
+
+<% @comics.each do |comic| %>
+  <tr>
+    <td><%= link_to comic.id, browse_comic_path(comic) %></td>
+    <td><%= h(truncate(comic.title, :length => 12)) %></td>
+    <td><%= comic.visible %></td>
+    <td><%= link_to comic.author_id, browse_author_path(comic.author) %></td>
+    <td><%= l comic.created_at %></td>
+    <td><%= l comic.updated_at %></td>
+  </tr>
+<% end %>
+</table>
+
diff --git a/app/views/comics/new.html.erb b/app/views/comics/new.html.erb
new file mode 100644 (file)
index 0000000..7142b57
--- /dev/null
@@ -0,0 +1,5 @@
+<h1><%= t('.title') %></h1>
+
+<%= render 'form' %>
+
+<%= link_to t('link.back'), comics_path %>
diff --git a/app/views/comics/new.js.erb b/app/views/comics/new.js.erb
new file mode 100644 (file)
index 0000000..ab2a7d2
--- /dev/null
@@ -0,0 +1 @@
+$("#newcomic").html("<%= escape_javascript(render('form')) -%>");
diff --git a/app/views/comics/show.atom.builder b/app/views/comics/show.atom.builder
new file mode 100644 (file)
index 0000000..6f5c967
--- /dev/null
@@ -0,0 +1,25 @@
+atom_feed(
+  :language => 'ja-JP',
+  :root_url => root_url,
+  :url => root_url(:format => :atom),
+  :id => root_url
+) do |feed|
+  feed.title MagicNumber['profile']['users']['caption']
+  feed.subtitle sanitize(MagicNumber['profile']['users']['description'], :tags => %w(a p img br))
+  feed.updated Time.now
+  feed.author {|author| author.name(MagicNumber['profile']['web_master'])}
+
+  @comic.stories.each do |story|
+    feed.entry(
+      story.panel,
+      :url => panel_url(story.panel),
+      :id => panel_url(story.panel),
+      :published => story.panel.updated_at,
+      :updated => story.panel.updated_at
+    ) do |item|
+      item.title(story.panel.caption)
+      item.content(story.panel.scenario, :type => 'html')
+      item.author {|author| author.name(story.panel.author.name)}
+    end
+  end
+end
diff --git a/app/views/comics/show.html.erb b/app/views/comics/show.html.erb
new file mode 100644 (file)
index 0000000..81a1bbf
--- /dev/null
@@ -0,0 +1,35 @@
+<% @page_title = t('.title') + ':' + @comic.title %>
+<h1><%= t('.title') %></h1>
+<p id="notice"><%= notice %></p>
+
+<p>
+  <b><%= t_m 'Comic.title' -%>:</b>
+  <%= link_to h(@comic.title), :controller => 'stories', :action => :comic, :id => @comic.id %>
+</p>
+
+<p>
+  <b><%= t_m 'Comic.description' -%>:</b>
+  <%= h(@comic.description) %>
+</p>
+
+<p>
+  <b><%= t_m 'Comic.visible' -%>:</b>
+  <%= t_selected_item('comic_visible_items', @comic.visible) %>
+</p>
+
+<p>
+  <b><%= t_m 'Comic.author_id' -%>:</b>
+  <%= link_to h(@comic.author.name), author_path(@comic.author) %>
+</p>
+
+<p>
+  <b><%= t_m 'Comic.created_at' -%>:</b>
+  <%= l @comic.created_at %>
+</p>
+
+<p>
+  <b><%= t_m 'Comic.updated_at' -%>:</b>
+  <%= l @comic.updated_at %>
+</p>
+
+<%= link_to t('link.edit'), edit_comic_path(@comic) %>
diff --git a/app/views/comics/show.rss.builder b/app/views/comics/show.rss.builder
new file mode 100644 (file)
index 0000000..553f344
--- /dev/null
@@ -0,0 +1,24 @@
+xml.instruct!
+xml.rss("version"    => "2.0",
+        "xmlns:dc"   => "http://purl.org/dc/elements/1.1/",
+        "xmlns:atom" => "http://www.w3.org/2005/Atom") do
+  xml.channel do
+    xml.title       MagicNumber['profile']['users']['caption']
+    xml.link        root_url
+    xml.pubDate     Time.now.rfc822
+    xml.description sanitize(MagicNumber['profile']['users']['description'], :tags => %w(a p img br))
+    xml.atom :link, "href" => @rss_url, "rel" => "self", "type" => "application/rss+xml"
+  @comic.stories.each do |story|
+      xml.item do
+        xml.title        story.panel.caption
+        xml.link         panel_url(story.panel)
+        xml.guid         panel_url(story.panel)
+        xml.description  story.panel.plain_scenario
+        xml.pubDate      story.panel.updated_at.to_formatted_s(:rfc822)
+        xml.dc :creator, story.panel.author.name
+      end
+    end
+  end
+end
diff --git a/app/views/stories/_append_panel.html.erb b/app/views/stories/_append_panel.html.erb
new file mode 100644 (file)
index 0000000..241bcf8
--- /dev/null
@@ -0,0 +1,21 @@
+<tr>
+  <td>
+    <%= link_to panel_icon(:object => panel, :size => 25), panel_path(panel) %>
+    <%= link_to author_icon(:object => panel.author, :size => 17), author_path(panel.author) %>
+  </td>
+  <td>
+    <%= link_to h(truncate(h(panel.caption), :length => 40)), :controller => 'stories', :action => :comic, :id => comic.id %>
+  </td>
+  <td>
+    <%= l panel.updated_at %>
+  </td>
+  <td>
+    <% @story = Story.new :comic_id => comic.id, :panel_id => panel.id -%>
+    <%= form_for(@story) do |f| %>
+      <%= f.hidden_field :comic_id %>
+      <%= f.hidden_field :t %>
+      <%= f.hidden_field :panel_id %>
+      <%= f.submit t('stories.comic.append') %>
+    <% end %>
+  </td>
+</tr>
diff --git a/app/views/stories/_comic_header.html.erb b/app/views/stories/_comic_header.html.erb
new file mode 100644 (file)
index 0000000..d1eb9e7
--- /dev/null
@@ -0,0 +1,40 @@
+<h1>
+  <%= link_to comic_icon, comic_path(comic) %>
+  <%= link_to h(comic.title), :controller => 'stories', :action => :comic, :id => comic.id %>
+</h1>
+<% if comic.own? author -%>
+  <%= form_for(comic) do |f| %>
+    <div class="field">
+      <%= f.label :title %><br />
+      <%= f.text_field :title %>
+    </div>
+    <div class="field">
+      <%= f.label :visible %><br />
+      <%= f.collection_select :visible, t_select_items(MagicNumber['comic_visible_items']), :last, :first, :html => {:selected => @comic.visible} %>
+    </div>
+    <div class="actions">
+      <%= f.submit %>
+    </div>
+  <% end %>
+<% else %>
+  <p>
+    <b><%= t_m 'Comic.visible' -%>:</b>
+    <%= t_selected_item('comic_visible_items', @comic.visible) %>
+  </p>
+<% end %>
+
+<p>
+  <b><%= t_m 'Comic.author_id' -%>:</b>
+  <%= link_to h(@comic.author.name), author_path(@comic.author) %>
+</p>
+
+<p>
+  <b><%= t_m 'Comic.created_at' -%>:</b>
+  <%= l @comic.created_at %>
+</p>
+
+<p>
+  <b><%= t_m 'Comic.updated_at' -%>:</b>
+  <%= l @comic.updated_at %>
+</p>
+
diff --git a/app/views/stories/_footer.html.erb b/app/views/stories/_footer.html.erb
new file mode 100644 (file)
index 0000000..56e9367
--- /dev/null
@@ -0,0 +1,23 @@
+<table class="no-border" style="margin: 0px; padding: 0px; width=100%">
+  <tr>
+    <td>
+      <% if story.panel -%>
+        <%= link_to panel_icon(:object => story.panel, :size => 25), panel_path(story.panel) %>
+        <%= link_to author_icon(:object => story.panel.author, :size => 17), author_path(story.panel.author) %>
+        <%= distance_of_time_in_words_to_now story.panel.updated_at %>
+      <% end %>
+    </td>
+    <td>
+      <%= link_to story_icon(:object => story, :size => 25), story_path(story) %>
+      <%= link_to author_icon(:object => story.author, :size => 17), author_path(story.author) %>
+      <%= l story.updated_at %>
+    </td>
+    <td>
+      <% if story.own? author %>
+        <%= render 'order', :story => story %>
+      <% else %>
+        No.<%= story.t %>
+      <% end %>
+    </td>
+  </tr>
+</table>
diff --git a/app/views/stories/_form.html.erb b/app/views/stories/_form.html.erb
new file mode 100644 (file)
index 0000000..8d705f5
--- /dev/null
@@ -0,0 +1,20 @@
+<%= form_for(@story) do |f| %>
+  <%= render 'system/error_explanation', :obj => @story %>
+
+  <div class="field">
+    <%= f.label :comic_id %><br />
+    <%= f.number_field :comic_id %>
+  </div>
+  <div class="field">
+    <%= f.label :t %><br />
+    <%= f.number_field :t %>
+  </div>
+  <div class="field">
+    <%= f.label :panel_id %><br />
+    <%= f.number_field :panel_id %>
+  </div>
+
+  <div class="actions">
+    <%= f.submit %>
+  </div>
+<% end %>
diff --git a/app/views/stories/_licensed_pictures.html.erb b/app/views/stories/_licensed_pictures.html.erb
new file mode 100644 (file)
index 0000000..1c10cc7
--- /dev/null
@@ -0,0 +1,14 @@
+<div class="pettanr-licensed_pictures">
+  <% licensed_pictures.each do |pid, picture| %>
+    <table class="no-border">
+      <tr>
+        <td>
+          <%= link_to(tag(:img, picture.tmb_opt_img_tag), picture_path(picture.id)) -%>
+        </td>
+        <td>
+          <%= render picture.credit_template, :picture => picture %>
+        </td>
+      </tr>
+    </table>
+  <% end %>
+</div>
diff --git a/app/views/stories/_list_item.html.erb b/app/views/stories/_list_item.html.erb
new file mode 100644 (file)
index 0000000..1b18f0f
--- /dev/null
@@ -0,0 +1,28 @@
+  <tr>
+    <td>
+      <%= link_to comic_icon(:object => story.comic, :size => 25), comic_path(story.comic) %>
+    </td>
+    <td>
+      <%= link_to h(story.comic.title), :controller => 'stories', :action => :comic, :id => story.comic.id %>
+    </td>
+    <td>
+      <%= link_to author_icon(:object => story.comic.author, :size => 17), author_path(story.comic.author) %>
+    </td>
+    <td>
+      <%= link_to story_icon(:object => story, :size => 25), story_path(story) %>
+    </td>
+    <td>
+      <%= link_to author_icon(:object => story.author, :size => 17), author_path(story.author) %>
+    </td>
+    <td>
+      No.<%= story.t %>
+    </td>
+    <td>
+      <%= l story.updated_at %>
+    </td>
+    <td>
+      <% if story.panel %>
+        <%= h story.panel.caption %>
+      <% end %>
+    </td>
+  </tr>
diff --git a/app/views/stories/_order.html.erb b/app/views/stories/_order.html.erb
new file mode 100644 (file)
index 0000000..e0ceadf
--- /dev/null
@@ -0,0 +1,15 @@
+<%= form_for(story) do |f| %>
+  <table class="no-border">
+    <tr>
+      <td>
+        No.
+        <%= f.number_field :t, :size => 3 %>
+        <%= f.hidden_field :panel_id %>
+        <%= f.hidden_field :comic_id %>
+      </td>
+      <td>
+        <%= f.submit t 'stories.move' %>
+      </td>
+    </tr>
+  </table>
+<% end %>
diff --git a/app/views/stories/browse.html.erb b/app/views/stories/browse.html.erb
new file mode 100644 (file)
index 0000000..24a7a6c
--- /dev/null
@@ -0,0 +1,28 @@
+<p id="notice"><%= notice %></p>
+
+<p>
+  <b>comic_id:</b>
+  <%= @story.comic_id %>
+</p>
+
+<p>
+  <b>panel_id:</b>
+  <%= @story.panel_id %>
+</p>
+
+<p>
+  <b>t:</b>
+  <%= @story.t %>
+</p>
+
+<p>
+  <b>author_id:</b>
+  <%= @story.author_id %>
+</p>
+
+<p>
+  <b>updated_at:</b>
+  <%= @story.updated_at %>
+</p>
+
+<%= link_to 'Back', :action => :list %>
diff --git a/app/views/stories/comic.html.erb b/app/views/stories/comic.html.erb
new file mode 100644 (file)
index 0000000..631f6f0
--- /dev/null
@@ -0,0 +1,33 @@
+<% @page_title = @comic.title %>
+<h1><%= t '.title' -%></h1>
+<p id="notice"><%= notice %></p>
+<%= render 'comic_header', :comic => @comic, :author => @author %>
+
+<% if @stories.empty? -%>
+  <h2><%= t('stories.comic.empty') %></h2>
+<% else %>
+  <% @stories.each do |story| %>
+    <% if story.panel -%>
+      <%= render 'panels/body', :panel => story.panel, :author => @author, :spot => nil %>
+    <% end %>
+    <%= render 'footer', :story => story, :author => @author %>
+  <% end %>
+  <%= render 'licensed_pictures', :licensed_pictures => Story.licensed_pictures(@stories) %>
+
+  <% if @prev_offset -%>
+    <%= link_to h('<<prev'), comic_story_path(@comic, {:offset => @prev_offset, :count => @panel_count}) %>
+  <% end %>
+
+  <% if @next_offset -%>
+    <%= link_to h('next>>'), comic_story_path(@comic, {:offset => @next_offset, :count => @panel_count}) %>
+  <% end %>
+
+<% end %>
+<% if @comic.own? @author -%>
+  <h3><%= t('stories.comic.new_panels') -%></h3>
+  <table>
+    <% @new_panels.each do |panel| %>
+      <%= render 'append_panel', :comic => @comic, :panel => panel, :author => @author %>
+    <% end %>
+  </table>
+<% end %>
diff --git a/app/views/stories/edit.html.erb b/app/views/stories/edit.html.erb
new file mode 100644 (file)
index 0000000..cee9463
--- /dev/null
@@ -0,0 +1,5 @@
+<h1><%= t('.title') %></h1>
+<p id="notice"><%= notice %></p>
+
+<%= render 'form' %>
+<%= button_to 'Destroy', @story.panel, confirm: 'Are you sure?', method: :delete %>
diff --git a/app/views/stories/edit.js.erb b/app/views/stories/edit.js.erb
new file mode 100644 (file)
index 0000000..29c687f
--- /dev/null
@@ -0,0 +1 @@
+$("#story-update-<%= @story.id -%>").html("<%= escape_javascript(render('editform')) -%>");
diff --git a/app/views/stories/index.html.erb b/app/views/stories/index.html.erb
new file mode 100644 (file)
index 0000000..7b61ab8
--- /dev/null
@@ -0,0 +1,8 @@
+<h1><%= t '.title' -%></h1>
+<table>
+  <% @stories.each do |story| %>
+    <%= render 'list_item', :story => story, :author => @author %>
+  <% end %>
+</table>
+<%= paginate(@paginate) %>
+<%= link_to t('stories.new.title'), new_story_path %>
diff --git a/app/views/stories/list.html.erb b/app/views/stories/list.html.erb
new file mode 100644 (file)
index 0000000..6fae81c
--- /dev/null
@@ -0,0 +1,29 @@
+<h1>Listing Story</h1>
+
+<table>
+  <tr>
+    <th>id</th>
+    <th>comic_id</th>
+    <th>panel_id</th>
+    <th>t</th>
+    <th>author_id</th>
+    <th>created_at</th>
+    <th>updated_at</th>
+    <th></th>
+  </tr>
+
+<%  @stories.each do |story| %>
+  <tr>
+    <td><%= link_to story.id, :action => :browse, :id => story.id %></td>
+    <td><%= story.comic_id %></td>
+    <td><%= story.panel_id %></td>
+    <td><%= story.t %></td>
+    <td><%= link_to story.author_id, :controller => '/authors', :action => :browse, :id => story.author_id %></td>
+    <td><%= story.created_at %></td>
+    <td><%= story.updated_at %></td>
+    <td>
+    </td>
+  </tr>
+<% end %>
+</table>
+
diff --git a/app/views/stories/new.html.erb b/app/views/stories/new.html.erb
new file mode 100644 (file)
index 0000000..f33a2f8
--- /dev/null
@@ -0,0 +1,3 @@
+<h1><%= t('.title') %></h1>
+
+<%= render 'form' %>
diff --git a/app/views/stories/show.html.erb b/app/views/stories/show.html.erb
new file mode 100644 (file)
index 0000000..fa0c3a0
--- /dev/null
@@ -0,0 +1,41 @@
+<h1><%= t('.title') %></h1>
+<p id="notice"><%= notice %></p>
+
+<p>
+  <b><%= t_m 'Story.comic_id' -%>:</b>
+  <%= link_to comic_icon(:object => @story.comic), comic_path(@story.comic) %>
+  <%= link_to h(@story.comic.title), :controller => 'stories', :action => :comic, :id => @story.comic.id %>
+</p>
+
+<p>
+  <b><%= t_m 'Story.panel_id' -%>:</b>
+  <%= link_to panel_icon(:object => @story.panel), panel_path(@story.panel) %>
+</p>
+
+<p>
+  <b><%= t_m 'Story.t' -%>:</b>
+  <%= @story.t %>
+</p>
+
+<p>
+  <b><%= t_m 'Story.author_id' -%>:</b>
+  <%= link_to author_icon(:object => @story.author), author_path(@story.author) %>
+  <%= link_to h(@story.author.name), author_path(@story.author) %>
+</p>
+
+<p>
+  <b><%= t_m 'Story.created_at' -%>:</b>
+  <%= l @story.created_at %>
+</p>
+
+<p>
+  <b><%= t_m 'Story.updated_at' -%>:</b>
+  <%= l @story.updated_at %>
+</p>
+<% if @story.own? @author -%>
+  <%= link_to t('link.edit'), edit_story_path(@story) %>
+  <%= link_to t('link.destroy'), story_path(@story), :method => :delete %>
+<% end %>
+
+<hr>
+<%= render 'panels/standard', :panel => @story.panel, :author => @author %>
index 9764470..48f61d0 100644 (file)
@@ -55,6 +55,77 @@ Pettanr::Application.routes.draw do
       get :resource_pictures
     end
   end
+  resources :comics do
+    new do
+      get :new
+    end
+    collection do
+      get :index
+      get :show
+      get :count
+      post :create
+      get :list
+    end
+    member do
+      get :play
+      get :edit
+      put :update
+      delete :destroy
+      get :browse
+    end
+  end
+  resources :stories do
+    new do
+      get :new
+    end
+    collection do
+      get :index
+      get :show
+      post :create
+      get :list
+    end
+    member do
+      get :edit
+      put :update
+      delete :destroy
+      get :browse
+    end
+  end
+  resources :sheets do
+    new do
+      get :new
+    end
+    collection do
+      get :index
+      get :show
+      get :count
+      post :create
+      get :list
+    end
+    member do
+      get :edit
+      put :update
+      delete :destroy
+      get :browse
+    end
+  end
+  resources :sheet_panels do
+    new do
+      get :new
+    end
+    collection do
+      get :index
+      get :show
+      post :create
+      get :list
+    end
+    member do
+      get :edit
+      put :update
+      delete :destroy
+      get :browse
+    end
+  end
   resources :scrolls do
     new do
       get :new
@@ -86,7 +157,6 @@ Pettanr::Application.routes.draw do
     end
     member do
       get :edit
-      get :scroll
       put :update
       delete :destroy
       get :browse
diff --git a/db/migrate/20130914055043_create_sheets.rb b/db/migrate/20130914055043_create_sheets.rb
new file mode 100644 (file)
index 0000000..af8eeb9
--- /dev/null
@@ -0,0 +1,13 @@
+class CreateSheets < ActiveRecord::Migration
+  def change
+    create_table :sheets do |t|
+      t.string :caption, :null => false, :limit => 100
+      t.integer :width, :null => false, :default => 839
+      t.integer :height, :null => false, :default => 1191
+      t.integer :visible, :null => false, :default => 0
+      t.integer :author_id, :null => false
+
+      t.timestamps
+    end
+  end
+end
diff --git a/db/migrate/20130914055219_create_sheet_panels.rb b/db/migrate/20130914055219_create_sheet_panels.rb
new file mode 100644 (file)
index 0000000..0679741
--- /dev/null
@@ -0,0 +1,15 @@
+class CreateSheetPanels < ActiveRecord::Migration
+  def change
+    create_table :sheet_panels do |t|
+      t.integer :sheet_id, :null => false, :default => 0
+      t.integer :panel_id, :null => false, :default => 0
+      t.integer :author_id, :null => false, :default => 0
+      t.integer :x
+      t.integer :y
+      t.integer :z, :null => false, :default => 0
+      t.integer :t, :null => false, :default => 0
+
+      t.timestamps
+    end
+  end
+end
diff --git a/db/migrate/20130914061319_restruct_comic.rb b/db/migrate/20130914061319_restruct_comic.rb
new file mode 100644 (file)
index 0000000..3e3838f
--- /dev/null
@@ -0,0 +1,27 @@
+class RestructComic < ActiveRecord::Migration
+  def up
+    drop_table :comics
+    create_table :comics do |t|
+      t.string :title, :null => false, :limit => 100
+      t.integer :visible, :null => false, :default => 0
+      t.text :description
+      t.integer :author_id, :null => false
+
+      t.timestamps
+    end
+    drop_table :stories
+    create_table :stories do |t|
+      t.integer :comic_id, :null => false, :default => 0
+      t.string :title, :null => false, :limit => 100
+      t.integer :visible, :null => false, :default => 0
+      t.text :description
+      t.integer :author_id, :null => false, :default => 0
+      t.integer :t, :null => false, :default => 0
+
+      t.timestamps
+    end
+  end
+
+  def down
+  end
+end
diff --git a/db/migrate/20130920042226_create_story_sheets.rb b/db/migrate/20130920042226_create_story_sheets.rb
new file mode 100644 (file)
index 0000000..f8bb2ca
--- /dev/null
@@ -0,0 +1,12 @@
+class CreateStorySheets < ActiveRecord::Migration
+  def change
+    create_table :story_sheets do |t|
+      t.integer :story_id, :null => false, :default => 0
+      t.integer :sheet_id, :null => false, :default => 0
+      t.integer :author_id, :null => false, :default => 0
+      t.integer :t, :null => false, :default => 0
+
+      t.timestamps
+    end
+  end
+end
index f36053e..54352b9 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 
 describe ArtistsController do
   before do
+    SpeechBalloonTemplate.delete_all
     @admin = FactoryGirl.create :admin
     @demand_user = FactoryGirl.create :demand_user
     @sp = FactoryGirl.create :system_picture
index 53728ca..29773ca 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 
 describe AuthorsController do
   before do
+    SpeechBalloonTemplate.delete_all
     @admin =FactoryGirl.create :admin
     @sp = FactoryGirl.create :system_picture
     @lg = FactoryGirl.create :license_group
index dc92cb7..98a1be9 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 
 describe BalloonsController do
   before do
+    SpeechBalloonTemplate.delete_all
     @admin = FactoryGirl.create :admin
     @user = FactoryGirl.create( :user_yas)
     @author = FactoryGirl.create :author, :user_id => @user.id
index 147aecd..8c16640 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 
 describe GroundColorsController do
   before do
+    SpeechBalloonTemplate.delete_all
     @admin = FactoryGirl.create :admin
     @user = FactoryGirl.create( :user_yas)
     @sp = FactoryGirl.create :system_picture
index fbe9395..d46307c 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 
 describe GroundPicturesController do
   before do
+    SpeechBalloonTemplate.delete_all
     @admin = FactoryGirl.create :admin
     @user = FactoryGirl.create( :user_yas)
     @sp = FactoryGirl.create :system_picture
index b25fa9f..ae93441 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 
 describe HomeController do
   before do
+    SpeechBalloonTemplate.delete_all
     @admin =FactoryGirl.create :admin
     @sp = FactoryGirl.create :system_picture
     @lg = FactoryGirl.create :license_group
index 3185622..2107730 100644 (file)
@@ -3,6 +3,7 @@ require 'spec_helper'
 #コマ\r
 describe PanelsController do\r
   before do\r
+    SpeechBalloonTemplate.delete_all\r
     @admin = FactoryGirl.create :admin\r
     @sp = FactoryGirl.create :system_picture\r
     @lg = FactoryGirl.create :license_group\r
diff --git a/spec/controllers/sheet_panels_controller_spec.rb b/spec/controllers/sheet_panels_controller_spec.rb
new file mode 100644 (file)
index 0000000..918da85
--- /dev/null
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+describe SheetPanelsController do
+
+end
diff --git a/spec/controllers/sheets_controller_spec.rb b/spec/controllers/sheets_controller_spec.rb
new file mode 100644 (file)
index 0000000..8855b14
--- /dev/null
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+describe SheetsController do
+
+end
index 2fb447e..5173dff 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 describe SpeechBalloonTemplatesController do
 
   before do
+    SpeechBalloonTemplate.delete_all
     @admin = FactoryGirl.create :admin
     @sp = FactoryGirl.create :system_picture
     @lg = FactoryGirl.create :license_group
index 1685900..bb98036 100644 (file)
@@ -3,6 +3,7 @@
 require 'spec_helper'
 describe SpeechBalloonsController do
   before do
+    SpeechBalloonTemplate.delete_all
     @admin = FactoryGirl.create :admin
     @user = FactoryGirl.create( :user_yas)
     @author = FactoryGirl.create :author, :user_id => @user.id
index 10d1544..c3ee16b 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 
 describe SpeechesController do
   before do
+    SpeechBalloonTemplate.delete_all
     @admin = FactoryGirl.create :admin
     @user = FactoryGirl.create( :user_yas)
     @author = FactoryGirl.create :author, :user_id => @user.id
diff --git a/spec/controllers/story_sheets_controller_spec.rb b/spec/controllers/story_sheets_controller_spec.rb
new file mode 100644 (file)
index 0000000..1ee00c8
--- /dev/null
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+describe StorySheetsController do
+
+end
index b8bd139..3e7c726 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 
 describe SystemPicturesController do
   before do
+    SpeechBalloonTemplate.delete_all
     @admin = FactoryGirl.create :admin
     @demand_user = FactoryGirl.create :demand_user
     @user = FactoryGirl.create( :user_yas)
index 0f29ff8..c35f33c 100644 (file)
@@ -73,6 +73,7 @@ FactoryGirl.define do
     scroll.title "scroll"
     scroll.description 'scroll description'
     scroll.visible 1
+    scroll.author_id 1
   end
 
   factory :scroll_panel, :class => ScrollPanel do |scroll_panel|
@@ -82,6 +83,47 @@ FactoryGirl.define do
     scroll_panel.t 0
   end
 
+  factory :comic, :class => Comic do |comic|
+    comic.title "comic"
+    comic.description 'comic description'
+    comic.visible 1
+    comic.author_id 1
+  end
+
+  factory :story, :class => Story do |story|
+    story.comic_id 1
+    story.title "scroll"
+    story.description 'scroll description'
+    story.visible 1
+    story.t 0
+    story.author_id 1
+  end
+
+  factory :story_sheet, :class => StorySheet do |story_sheet|
+    story_sheet.sheet_id 1
+    story_sheet.story_id 1
+    story_sheet.t 0
+    story_sheet.author_id 1
+  end
+
+  factory :sheet, :class => Sheet do |sheet|
+    sheet.caption "sheet"
+    sheet.width 222
+    sheet.height 111
+    sheet.visible 1
+    sheet.author_id 1
+  end
+
+  factory :sheet_panel, :class => SheetPanel do |sheet_panel|
+    sheet_panel.sheet_id 1
+    sheet_panel.panel_id 1
+    sheet_panel.x 111
+    sheet_panel.y 200
+    sheet_panel.z 1
+    sheet_panel.t 0
+    sheet_panel.author_id 1
+  end
+
   factory :original_picture, :class => OriginalPicture do |op|
     op.ext 'png'
     op.width 222
diff --git a/spec/helpers/sheet_panels_helper_spec.rb b/spec/helpers/sheet_panels_helper_spec.rb
new file mode 100644 (file)
index 0000000..9792472
--- /dev/null
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+# Specs in this file have access to a helper object that includes
+# the SheetPanelsHelper. For example:
+#
+# describe SheetPanelsHelper do
+#   describe "string concat" do
+#     it "concats two strings with spaces" do
+#       helper.concat_strings("this","that").should == "this that"
+#     end
+#   end
+# end
+describe SheetPanelsHelper do
+  pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/helpers/sheets_helper_spec.rb b/spec/helpers/sheets_helper_spec.rb
new file mode 100644 (file)
index 0000000..7355f01
--- /dev/null
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+# Specs in this file have access to a helper object that includes
+# the SheetsHelper. For example:
+#
+# describe SheetsHelper do
+#   describe "string concat" do
+#     it "concats two strings with spaces" do
+#       helper.concat_strings("this","that").should == "this that"
+#     end
+#   end
+# end
+describe SheetsHelper do
+  pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/helpers/story_sheets_helper_spec.rb b/spec/helpers/story_sheets_helper_spec.rb
new file mode 100644 (file)
index 0000000..a64e8ee
--- /dev/null
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+# Specs in this file have access to a helper object that includes
+# the StorySheetsHelper. For example:
+#
+# describe StorySheetsHelper do
+#   describe "string concat" do
+#     it "concats two strings with spaces" do
+#       helper.concat_strings("this","that").should == "this that"
+#     end
+#   end
+# end
+describe StorySheetsHelper do
+  pending "add some examples to (or delete) #{__FILE__}"
+end
index e93b28f..493bd4e 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 
 describe Artist do
   before do
+    SpeechBalloonTemplate.delete_all
     @admin = FactoryGirl.create :admin
     @user = FactoryGirl.create( :user_yas)
     @author = FactoryGirl.create :author, :user_id => @user.id
index 73139cb..dd65266 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 
 describe Author do
   before do
+    SpeechBalloonTemplate.delete_all
     @admin = FactoryGirl.create :admin
     @user = FactoryGirl.create( :user_yas)
     @author = FactoryGirl.create :author, :user_id => @user.id
@@ -410,7 +411,7 @@ describe Author do
   describe 'マイリストページ制御パラメータに於いて' do
     before do
     end
-    context 'ã\82³ã\83\9fã\83\83ã\82¯page_size補正について' do
+    context 'ã\82¹ã\82¯ã\83­ã\83¼ã\83«page_size補正について' do
       it '文字列から数値に変換される' do
         Author.scroll_page_size('7').should eq 7
       end
@@ -424,7 +425,7 @@ describe Author do
         Author.scroll_page_size('1000').should eq Author.scroll_max_page_size
       end
     end
-    context 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼page_size補正について' do
+    context 'ã\82¹ã\82¯ã\82³ã\83\9epage_size補正について' do
       it '文字列から数値に変換される' do
         Author.scroll_panel_page_size('7').should eq 7
       end
@@ -438,6 +439,76 @@ describe Author do
         Author.scroll_panel_page_size('1000').should eq Author.scroll_panel_max_page_size
       end
     end
+    context 'コミックpage_size補正について' do
+      it '文字列から数値に変換される' do
+        Author.comic_page_size('7').should eq 7
+      end
+      it 'nilの場合はAuthor.default_comic_page_sizeになる' do
+        Author.comic_page_size().should eq Author.default_comic_page_size
+      end
+      it '0以下の場合はAuthor.default_comic_page_sizeになる' do
+        Author.comic_page_size('0').should eq Author.default_comic_page_size
+      end
+      it 'Author.comic_max_page_sizeを超えた場合はAuthor.comic_max_page_sizeになる' do
+        Author.comic_page_size('1000').should eq Author.comic_max_page_size
+      end
+    end
+    context 'ストーリーpage_size補正について' do
+      it '文字列から数値に変換される' do
+        Author.story_page_size('7').should eq 7
+      end
+      it 'nilの場合はAuthor.default_story_page_sizeになる' do
+        Author.story_page_size().should eq Author.default_story_page_size
+      end
+      it '0以下の場合はAuthor.default_story_page_sizeになる' do
+        Author.story_page_size('0').should eq Author.default_story_page_size
+      end
+      it 'Author.story_max_page_sizeを超えた場合はAuthor.story_max_page_sizeになる' do
+        Author.story_page_size('1000').should eq Author.story_max_page_size
+      end
+    end
+    context 'スト紙page_size補正について' do
+      it '文字列から数値に変換される' do
+        Author.story_sheet_page_size('7').should eq 7
+      end
+      it 'nilの場合はAuthor.default_story_sheet_page_sizeになる' do
+        Author.story_sheet_page_size().should eq Author.default_story_sheet_page_size
+      end
+      it '0以下の場合はAuthor.default_story_sheet_page_sizeになる' do
+        Author.story_sheet_page_size('0').should eq Author.default_story_sheet_page_size
+      end
+      it 'Author.story_sheet_max_page_sizeを超えた場合はAuthor.story_sheet_max_page_sizeになる' do
+        Author.story_sheet_page_size('1000').should eq Author.story_sheet_max_page_size
+      end
+    end
+    context '用紙page_size補正について' do
+      it '文字列から数値に変換される' do
+        Author.sheet_page_size('7').should eq 7
+      end
+      it 'nilの場合はAuthor.default_sheet_page_sizeになる' do
+        Author.sheet_page_size().should eq Author.default_sheet_page_size
+      end
+      it '0以下の場合はAuthor.default_sheet_page_sizeになる' do
+        Author.sheet_page_size('0').should eq Author.default_sheet_page_size
+      end
+      it 'Author.sheet_max_page_sizeを超えた場合はAuthor.sheet_max_page_sizeになる' do
+        Author.sheet_page_size('1000').should eq Author.sheet_max_page_size
+      end
+    end
+    context '紙コマpage_size補正について' do
+      it '文字列から数値に変換される' do
+        Author.sheet_panel_page_size('7').should eq 7
+      end
+      it 'nilの場合はAuthor.default_sheet_panel_page_sizeになる' do
+        Author.sheet_panel_page_size().should eq Author.default_sheet_panel_page_size
+      end
+      it '0以下の場合はAuthor.default_sheet_panel_page_sizeになる' do
+        Author.sheet_panel_page_size('0').should eq Author.default_sheet_panel_page_size
+      end
+      it 'Author.sheet_panel_max_page_sizeを超えた場合はAuthor.sheet_panel_max_page_sizeになる' do
+        Author.sheet_panel_page_size('1000').should eq Author.sheet_panel_max_page_size
+      end
+    end
     context 'コマpage_size補正について' do
       it '文字列から数値に変換される' do
         Author.panel_page_size('7').should eq 7
index cf82f80..a7f8a34 100644 (file)
@@ -3,6 +3,7 @@ require 'spec_helper'
 #フキダシ枠\r
 describe Balloon do\r
   before do\r
+    SpeechBalloonTemplate.delete_all\r
     @admin = FactoryGirl.create :admin\r
     @user = FactoryGirl.create( :user_yas)\r
     @author = FactoryGirl.create :author, :user_id => @user.id\r
@@ -14,6 +15,7 @@ describe Balloon do
     @lg = FactoryGirl.create :license_group\r
     @license = FactoryGirl.create :license, :license_group_id => @lg.id, :system_picture_id => @sp.id\r
     \r
+    SpeechBalloonTemplate.delete_all\r
     @speech_balloon_template = FactoryGirl.create :speech_balloon_template, "name" => "circle@pettan.com", "classname" => "CircleSpeechBalloon", "caption" => "cc",  "system_picture_id" => @sp.id, "settings" => '{}'\r
     @writing_format = FactoryGirl.create :writing_format\r
     @panel = FactoryGirl.create :panel, :author_id => @author.id\r
diff --git a/spec/models/comic_spec.rb b/spec/models/comic_spec.rb
new file mode 100644 (file)
index 0000000..ce70711
--- /dev/null
@@ -0,0 +1,816 @@
+# -*- encoding: utf-8 -*-
+require 'spec_helper'
+#コミック
+
+describe Comic do
+  before do
+    SpeechBalloonTemplate.delete_all
+    @admin = FactoryGirl.create :admin
+    @demand_user = FactoryGirl.create :demand_user
+    @sp = FactoryGirl.create :system_picture
+    @lg = FactoryGirl.create :license_group
+    @license = FactoryGirl.create :license, :license_group_id => @lg.id, :system_picture_id => @sp.id
+    @user = FactoryGirl.create( :user_yas)
+    @author = FactoryGirl.create :author, :user_id => @user.id
+    @artist = FactoryGirl.create :artist_yas, :author_id => @author.id
+    @other_user = FactoryGirl.create( :user_yas)
+    @other_author = FactoryGirl.create :author, :user_id => @other_user.id
+    @other_artist = FactoryGirl.create :artist_yas, :author_id => @other_author.id
+  end
+  
+  describe '検証に於いて' do
+    before do
+      @comic = FactoryGirl.build :comic, :author_id => @author.id
+    end
+    
+    context 'オーソドックスなデータのとき' do
+      it '下限データが通る' do
+        @comic.title = 'a'
+        @comic.visible = 0
+        @comic.should be_valid
+      end
+      it '上限データが通る' do
+        @comic.title = 'a'*100
+        @comic.visible = 1
+        @comic.should be_valid
+      end
+    end
+    
+    context 'titleを検証するとき' do
+      it 'nullなら失敗する' do
+        @comic.title = nil
+        @comic.should_not be_valid
+      end
+      it '100文字以上なら失敗する' do
+        @comic.title = 'a'*101
+        @comic.should_not be_valid
+      end
+    end
+    context 'visibleを検証するとき' do
+      it 'nullなら失敗する' do
+        @comic.visible = nil
+        @comic.should_not be_valid
+      end
+      it '負なら失敗する' do
+        @comic.visible = -1
+        @comic.should_not be_valid
+      end
+      it '2以上なら失敗する' do
+        @comic.visible = 2
+        @comic.should_not be_valid
+      end
+    end
+  end
+  
+  describe '文字コード検証に於いて' do
+    before do
+      @comic = FactoryGirl.build :comic, :author_id => @author.id
+    end
+    
+    context 'titleを検証するとき' do
+      it 'Shift JISなら失敗する' do
+        @comic.title = "\x83G\x83r\x83]\x83D"
+        lambda{
+          @comic.valid_encode
+        }.should raise_error(Pettanr::BadRequest)
+      end
+    end
+    
+    context 'descriptionを検証するとき' do
+      it 'Shift JISなら失敗する' do
+        @comic.description = "\x83G\x83r\x83]\x83D"
+        lambda{
+          @comic.valid_encode
+        }.should raise_error(Pettanr::BadRequest)
+      end
+    end
+  end
+  
+  describe 'デフォルト値補充に於いて' do
+    it 'visibleが0になっている' do
+      @comic = FactoryGirl.build :comic, :visible => nil
+      @comic.supply_default
+      @comic.visible.should eq 0
+    end
+  end
+  
+  describe '上書き補充に於いて' do
+    it '作家idが設定されている' do
+      @comic = FactoryGirl.build :comic, :author_id => nil
+      @comic.overwrite @author
+      @comic.author_id.should eq @author.id
+    end
+  end
+  
+  describe 'ロールリストからの作家取得に於いて' do
+    context 'ロールがユーザのとき' do
+      it 'ユーザから作家を取得して、それを返す' do
+        r = Comic.get_author_from_roles([@user])
+        r.should eq @author
+      end
+    end
+    context 'ロールが作家のとき' do
+      it '作家を返す' do
+        r = Comic.get_author_from_roles([@author])
+        r.should eq @author
+      end
+    end
+    context 'ロールが絵師のとき' do
+      it '絵師から作家を取得できれば、それをを返す' do
+        r = Comic.get_author_from_roles([@artist])
+        r.should eq @author
+      end
+      it '絵師から作家を取得できないときnilを返す' do
+        @artist.author_id = nil
+        @artist.save!
+        r = Comic.get_author_from_roles([@artist])
+        r.should be_nil
+      end
+    end
+    context 'ロールが管理者のとき' do
+      it 'nilを返す' do
+        r = Comic.get_author_from_roles([@admin])
+        r.should be_nil
+      end
+    end
+  end
+  
+  describe 'ロールリストからの絵師取得に於いて' do
+    context 'ロールがユーザのとき' do
+      it 'ユーザから作家を取得して、そこから絵師を取得できれば、それを返す' do
+        r = Comic.get_artist_from_roles([@user])
+        r.should eq @artist
+      end
+      it '作家から絵師を取得できないときnilを返す' do
+        @artist.author_id = nil
+        @artist.save!
+        r = Comic.get_artist_from_roles([@user])
+        r.should be_nil
+      end
+    end
+    context 'ロールが作家のとき' do
+      it 'そこから絵師を取得できれば、それを返す' do
+        r = Comic.get_artist_from_roles([@author])
+        r.should eq @artist
+      end
+      it '作家から絵師を取得できないときnilを返す' do
+        @artist.author_id = nil
+        @artist.save!
+        r = Comic.get_artist_from_roles([@author])
+        r.should be_nil
+      end
+    end
+    context 'ロールが絵師のとき' do
+      it 'それを返す' do
+        r = Comic.get_artist_from_roles([@artist])
+        r.should eq @artist
+      end
+    end
+    context 'ロールが管理者のとき' do
+      it 'nilを返す' do
+        r = Comic.get_artist_from_roles([@admin])
+        r.should be_nil
+      end
+    end
+  end
+  
+  describe '所持判定に於いて' do
+    before do
+      @comic = FactoryGirl.build :comic, :author_id => @author.id
+    end
+    context '事前チェックする' do
+      it '自身にロールリストからの作家取得を依頼している' do
+        Comic.should_receive(:get_author_from_roles).with(any_args).exactly(1)
+        r = @comic.own?([@author])
+      end
+    end
+    context 'ロール内作家が取得できるとき' do
+      before do
+      end
+      it 'ロール内作家のidが自身の作家idと一致するなら許可する' do
+        Comic.stub(:get_author_from_roles).with(any_args).and_return(@author)
+        r = @comic.own?([@author])
+        r.should be_true
+      end
+      it 'ロール内作家のidが自身の作家idと一致しないならno' do
+        Comic.stub(:get_author_from_roles).with(any_args).and_return(@other_author)
+        @comic.own?(@other_author).should be_false
+      end
+    end
+    context 'ロール内作家が取得できないとき' do
+      before do
+        Comic.stub(:get_author_from_roles).with(any_args).and_return(nil)
+      end
+      it 'Falseを返す' do
+        r = @comic.own?([@author])
+        r.should be_false
+      end
+    end
+  end
+  
+  describe '読者用ロールチェックに於いて' do
+    before do
+      @comic = FactoryGirl.build :comic, :author_id => @author.id
+    end
+    context 'ロールリストに作家が含まれるとき' do
+      it 'ロールリストがリストではないとき、リストにする trueを返す' do
+        r = @comic.reader_role_check(@author)
+        r.should be_true
+      end
+      it 'trueを返す' do
+        r = @comic.reader_role_check([@author])
+        r.should be_true
+      end
+    end
+    context 'ロールリストに絵師が含まれるとき' do
+      it 'trueを返す' do
+        r = @comic.reader_role_check([@artist])
+        r.should be_true
+      end
+    end
+    context 'ロールリストにユーザが含まれるとき' do
+      it 'trueを返す' do
+        r = @comic.reader_role_check([@user])
+        r.should be_true
+      end
+    end
+    context 'ロールリストに管理者が含まれるとき' do
+      it 'trueを返す' do
+        r = @comic.reader_role_check([@admin])
+        r.should be_true
+      end
+    end
+    context 'ロールリストにユーザ、管理者、作家、絵師が含まれないとき' do
+      it 'falseを返す' do
+        r = @comic.reader_role_check([nil])
+        r.should be_false
+      end
+    end
+  end
+  
+  describe '素材読者用ロールチェックに於いて' do
+    before do
+      @comic = FactoryGirl.build :comic, :author_id => @author.id
+    end
+    context 'ロールリストに作家が含まれるとき' do
+      it 'ロールリストがリストではないとき、リストにする trueを返す' do
+        r = @comic.resource_reader_role_check(@author)
+        r.should be_true
+      end
+      it 'trueを返す' do
+        r = @comic.resource_reader_role_check([@author])
+        r.should be_true
+      end
+    end
+    context 'ロールリストに絵師が含まれるとき' do
+      it 'trueを返す' do
+        r = @comic.resource_reader_role_check([@artist])
+        r.should be_true
+      end
+    end
+    context 'ロールリストにユーザが含まれるとき' do
+      it 'trueを返す' do
+        r = @comic.resource_reader_role_check([@user])
+        r.should be_true
+      end
+    end
+    context 'ロールリストに管理者が含まれるとき' do
+      it 'trueを返す' do
+        r = @comic.resource_reader_role_check([@admin])
+        r.should be_true
+      end
+    end
+    context 'ロールリストに借手が含まれるとき' do
+      it 'trueを返す' do
+        r = @comic.resource_reader_role_check([@demand_user])
+        r.should be_true
+      end
+    end
+    context 'ロールリストにユーザ、管理者、作家、絵師、借手が含まれないとき' do
+      it 'falseを返す' do
+        r = @comic.resource_reader_role_check([nil])
+        r.should be_false
+      end
+    end
+  end
+  
+  describe '閲覧許可に於いて' do
+    before do
+      @comic = FactoryGirl.build :comic, :author_id => @author.id
+    end
+    context 'オープンモードのとき' do
+      before do
+        MagicNumber['run_mode'] = 0
+      end
+      it '自身にゲスト用ロールチェックを問い合わせしている' do
+        Comic.any_instance.stub(:guest_role_check).and_return(true)
+        Comic.any_instance.should_receive(:guest_role_check).with(any_args).exactly(1)
+        r = @comic.visible?([@author])
+      end
+      it 'ゲスト用ロールチェックが失敗したとき、falseを返す' do
+        Comic.any_instance.stub(:guest_role_check).and_return(false)
+        r = @comic.visible?([@author])
+        r.should be_false
+      end
+    end
+    context 'クローズドモードのとき' do
+      before do
+        MagicNumber['run_mode'] = 1
+      end
+      it '自身に読者用ロールチェックを問い合わせしている' do
+        Comic.any_instance.stub(:reader_role_check).and_return(true)
+        Comic.any_instance.should_receive(:reader_role_check).with(any_args).exactly(1)
+        r = @comic.visible?([@author])
+      end
+      it '読者用ロールチェックが失敗したとき、falseを返す' do
+        Comic.any_instance.stub(:reader_role_check).and_return(false)
+        r = @comic.visible?([@author])
+        r.should be_false
+      end
+    end
+    context '事前チェックする' do
+      before do
+        MagicNumber['run_mode'] = 1
+        Comic.any_instance.stub(:reader_role_check).and_return(true)
+        Comic.any_instance.stub(:own?).and_return(true)
+      end
+      it '自身に所持判定を問い合わせしている' do
+        Comic.any_instance.should_receive(:own?).with(any_args).exactly(1)
+        r = @comic.visible?([@author])
+      end
+    end
+    context 'つつがなく終わるとき' do
+      before do
+        MagicNumber['run_mode'] = 1
+        Comic.any_instance.stub(:reader_role_check).and_return(true)
+      end
+      it '自分のコミックなら許可する' do
+        Comic.any_instance.stub(:own?).and_return(true)
+        Comic.any_instance.stub(:visible).and_return(0)
+        r = @comic.visible?([@author])
+        r.should be_true
+      end
+      it '他人の非公開コミックなら許可しない' do
+        Comic.any_instance.stub(:own?).and_return(false)
+        Comic.any_instance.stub(:visible).and_return(0)
+        r = @comic.visible?([@author])
+        r.should be_false
+      end
+      it '他人のコミックでも公開なら許可する' do
+        Comic.any_instance.stub(:own?).and_return(false)
+        Comic.any_instance.stub(:visible).and_return(1)
+        r = @comic.visible?([@author])
+        r.should be_true
+      end
+    end
+  end
+  
+  describe '一覧取得に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+    end
+    context 'page補正について' do
+      it '文字列から数値に変換される' do
+        Comic.page('8').should eq 8
+      end
+      it 'nilの場合は1になる' do
+        Comic.page().should eq 1
+      end
+      it '0以下の場合は1になる' do
+        Comic.page('0').should eq 1
+      end
+    end
+    context 'page_size補正について' do
+      it '文字列から数値に変換される' do
+        Comic.page_size('7').should eq 7
+      end
+      it 'nilの場合はComic.default_page_sizeになる' do
+        Comic.page_size().should eq Comic.default_page_size
+      end
+      it '0以下の場合はComic.default_page_sizeになる' do
+        Comic.page_size('0').should eq Comic.default_page_size
+      end
+      it 'Comic.max_page_sizeを超えた場合はComic.max_page_sizeになる' do
+        Comic.page_size('1000').should eq Comic.max_page_size
+      end
+    end
+    context 'つつがなく終わるとき' do
+      it '一覧取得オプションを利用している' do
+        Comic.stub(:list_opt).with(any_args).and_return({})
+        Comic.should_receive(:list_opt).with(any_args).exactly(1)
+        r = Comic.list
+      end
+    end
+    it 'リストを返す' do
+      c = Comic.list
+      c.should eq [@comic]
+    end
+    it '非公開コミックは(自分のコミックであっても)含んでいない' do
+      FactoryGirl.create :comic, :author_id => @author.id, :visible => 0
+      c = Comic.list
+      c.should eq [@comic]
+    end
+    it '時系列で並んでいる' do
+      #公開コミックは(他人のコミックであっても)含んでいる
+      v = FactoryGirl.create :comic, :author_id => @other_author.id, :updated_at => Time.now + 100
+      c = Comic.list
+      c.should eq [v, @comic]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @comic2 = FactoryGirl.create :comic, :author_id => @author.id, :updated_at => Time.now + 100
+        @comic3 = FactoryGirl.create :comic, :author_id => @author.id, :updated_at => Time.now + 200
+        @comic4 = FactoryGirl.create :comic, :author_id => @author.id, :updated_at => Time.now + 300
+        @comic5 = FactoryGirl.create :comic, :author_id => @author.id, :updated_at => Time.now + 400
+        Comic.stub(:default_page_size).and_return(2)
+      end
+      it '通常は2件を返す' do
+        c = Comic.list
+        c.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        c = Comic.list(1)
+        c.should eq [@comic5, @comic4]
+      end
+      it 'page=2なら中間2件を返す' do
+        c = Comic.list(2)
+        c.should eq [@comic3, @comic2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        c = Comic.list(3)
+        c.should eq [@comic]
+      end
+    end
+  end
+  
+  describe '自分のコミック一覧取得に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '一覧取得オプションを利用している' do
+        Comic.stub(:list_opt).with(any_args).and_return({})
+        Comic.should_receive(:list_opt).with(any_args).exactly(1)
+        r = Comic.mylist @author
+      end
+    end
+    it 'リストを返す' do
+      c = Comic.mylist @author
+      c.should eq [@comic]
+    end
+    it '時系列で並んでいる' do
+      nc = FactoryGirl.create :comic, :author_id => @author.id, :updated_at => Time.now + 100
+      cl = Comic.mylist @author
+      cl.should eq [nc, @comic]
+    end
+    it '他人のコミックは公開でも含まない' do
+      nc = FactoryGirl.create :comic, :author_id => @other_author.id, :visible => 1
+      cl = Comic.mylist @author
+      cl.should eq [@comic]
+    end
+    it '自分のコミックは非公開でも含んでいる' do
+      nc = FactoryGirl.create :comic, :author_id => @author.id, :visible => 0, :updated_at => Time.now + 100
+      cl = Comic.mylist @author
+      cl.should eq [nc, @comic]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @comic2 = FactoryGirl.create :comic, :author_id => @author.id, :updated_at => Time.now + 100
+        @comic3 = FactoryGirl.create :comic, :author_id => @author.id, :updated_at => Time.now + 200
+        @comic4 = FactoryGirl.create :comic, :author_id => @author.id, :updated_at => Time.now + 300
+        @comic5 = FactoryGirl.create :comic, :author_id => @author.id, :updated_at => Time.now + 400
+      end
+      it '通常は2件を返す' do
+        c = Comic.mylist @author, 1, 2
+        c.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        c = Comic.mylist(@author, 1, 2)
+        c.should eq [@comic5, @comic4]
+      end
+      it 'page=2なら中間2件を返す' do
+        c = Comic.mylist(@author, 2, 2)
+        c.should eq [@comic3, @comic2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        c = Comic.mylist(@author, 3, 2)
+        c.should eq [@comic]
+      end
+    end
+  end
+  
+  describe '他作家のコミック一覧取得に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @other_comic = FactoryGirl.create :comic, :author_id => @other_author.id, :visible => 1
+    end
+    context 'つつがなく終わるとき' do
+      it '一覧取得オプションを利用している' do
+        Comic.stub(:list_opt).with(any_args).and_return({})
+        Comic.should_receive(:list_opt).with(any_args).exactly(1)
+        r = Comic.himlist @other_author
+      end
+    end
+    it '指定した作家のリストを返す' do
+      r = Comic.himlist @other_author
+      r.should eq [@other_comic]
+    end
+    it '時系列で並んでいる' do
+      nc = FactoryGirl.create :comic, :author_id => @other_author.id, :updated_at => Time.now + 100
+      r = Comic.himlist @other_author
+      r.should eq [nc, @other_comic]
+    end
+    it '公開コミックに限る ' do
+      hidden = FactoryGirl.create :comic, :author_id => @other_author.id, :visible => 0
+      r = Comic.himlist @other_author
+      r.should eq [@other_comic]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @other_comic2 = FactoryGirl.create :comic, :author_id => @other_author.id, :updated_at => Time.now + 100
+        @other_comic3 = FactoryGirl.create :comic, :author_id => @other_author.id, :updated_at => Time.now + 200
+        @other_comic4 = FactoryGirl.create :comic, :author_id => @other_author.id, :updated_at => Time.now + 300
+        @other_comic5 = FactoryGirl.create :comic, :author_id => @other_author.id, :updated_at => Time.now + 400
+      end
+      it '通常は2件を返す' do
+        c = Comic.himlist @other_author, 1, 2
+        c.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        c = Comic.himlist(@other_author, 1, 2)
+        c.should eq [@other_comic5, @other_comic4]
+      end
+      it 'page=2なら中間2件を返す' do
+        c = Comic.himlist(@other_author, 2, 2)
+        c.should eq [@other_comic3, @other_comic2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        c = Comic.himlist(@other_author, 3, 2)
+        c.should eq [@other_comic]
+      end
+    end
+  end
+  
+  describe 'コミック一覧ページ制御に於いて' do
+    before do
+      Comic.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = Comic.list_paginate 
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it 'コミック一覧の取得条件を利用している' do
+      Comic.stub(:list_where).with(any_args).and_return('')
+      Comic.should_receive(:list_where).with(any_args).exactly(1)
+      r = Comic.list_paginate 
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = Comic.list_paginate 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe '自分のコミック一覧ページ制御に於いて' do
+    before do
+      Comic.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = Comic.mylist_paginate @author
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it '自分のコミック一覧の取得条件を利用している' do
+      Comic.stub(:mylist_where).with(any_args).and_return('')
+      Comic.should_receive(:mylist_where).with(any_args).exactly(1)
+      r = Comic.mylist_paginate @author
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = Comic.mylist_paginate @author, 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe '他作家のコミック一覧ページ制御に於いて' do
+    before do
+      Comic.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = Comic.himlist_paginate @other_author
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it '他作家のコミック一覧の取得条件を利用している' do
+      Comic.stub(:himlist_where).with(any_args).and_return('')
+      Comic.should_receive(:himlist_where).with(any_args).exactly(1)
+      r = Comic.himlist_paginate @other_author
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = Comic.himlist_paginate @other_author, 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe '一覧取得オプションに於いて' do
+    it '2つの項目を含んでいる' do
+      r = Comic.list_opt
+      r.should have(2).items
+    end
+    it 'ストーリーを含んでいる' do
+      r = Comic.list_opt
+      r.has_key?(:stories).should be_true
+    end
+    it '作家を含んでいる' do
+      r = Comic.list_opt
+      r.has_key?(:author).should be_true
+    end
+  end
+  describe 'json一覧出力オプションに於いて' do
+    before do
+      @op = FactoryGirl.create :original_picture, :artist_id => @artist.id
+      @p = FactoryGirl.create :picture, :original_picture_id => @op.id, :license_id => @license.id, :artist_id => @artist.id
+      @rp = FactoryGirl.create :resource_picture, :artist_id => @artist.id, :license_id => @license.id, :original_picture_id => @op.id, :picture_id => @p.id
+      @sbt = FactoryGirl.create :speech_balloon_template
+      @comic = FactoryGirl.create :comic, :author_id => @author.id, :visible => 1
+      @story = FactoryGirl.create :story, :author_id => @author.id, :comic_id => @comic.id
+    end
+    it 'ストーリーを含んでいる' do
+      r = Comic.list.to_json Comic.list_json_opt
+      j = JSON.parse r
+      i = j.first
+      i.has_key?('stories').should be_true
+    end
+    it '作家を含んでいる' do
+      r = Comic.list.to_json Comic.list_json_opt
+      j = JSON.parse r
+      i = j.first
+      i.has_key?('author').should be_true
+    end
+  end
+  
+  describe '単体取得に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '単体取得オプションを利用している' do
+        Comic.stub(:show_opt).with(any_args).and_return({})
+        Comic.should_receive(:show_opt).with(any_args).exactly(1)
+        r = Comic.show @comic.id, @author
+      end
+      it '閲覧許可を問い合わせている' do
+        Comic.any_instance.stub(:visible?).with(any_args).and_return(true)
+        Comic.any_instance.should_receive(:visible?).with(any_args).exactly(1)
+        r = Comic.show @comic.id, @author
+      end
+    end
+    it '指定のコミックを返す' do
+      c = Comic.show @comic.id, @author
+      c.should eq @comic
+    end
+    context '閲覧許可が出なかったとき' do
+      it '403Forbidden例外を返す' do
+        Comic.any_instance.stub(:visible?).and_return(false)
+        lambda{
+          Comic.show @comic.id, @author
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+    context '存在しないコミックを開こうとしたとき' do
+      it '404RecordNotFound例外を返す' do
+        lambda{
+          Comic.show 110, @author
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+    end
+  end
+  
+  describe '編集取得に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '単体取得オプションを利用している' do
+        Comic.stub(:show_opt).with(any_args).and_return({})
+        Comic.should_receive(:show_opt).with(any_args).exactly(1)
+        r = Comic.edit @comic.id, @author
+      end
+      it '所持判定を問い合わせている' do
+        Comic.any_instance.stub(:own?).with(any_args).and_return(true)
+        Comic.any_instance.should_receive(:own?).with(any_args).exactly(1)
+        r = Comic.edit @comic.id, @author
+      end
+    end
+    it '指定のコミックを返す' do
+      Comic.any_instance.stub(:own?).and_return(true)
+      c = Comic.edit @comic.id, @author.id
+      c.should eq @comic
+    end
+    context '他人のコミックを開こうとしたとき' do
+      it '403Forbidden例外を返す' do
+        Comic.any_instance.stub(:own?).and_return(false)
+        lambda{
+          Comic.edit @comic.id, @author
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+    context '存在しないコミックを開こうとしたとき' do
+      it '404RecordNotFound例外を返す' do
+        lambda{
+          Comic.edit 110, @author
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+    end
+  end
+  describe '単体取得オプションに於いて' do
+    it 'includeキーを含んでいる' do
+      r = Comic.show_opt
+      r.has_key?(:include).should be_true
+    end
+    it '2つの項目を含んでいる' do
+      r = Comic.show_opt[:include]
+      r.should have(2).items
+    end
+    it '作家を含んでいる' do
+      r = Comic.show_opt[:include]
+      r.has_key?(:author).should be_true
+    end
+    it 'ストーリーを含んでいる' do
+      r = Comic.show_opt[:include]
+      r.has_key?(:stories).should be_true
+    end
+  end
+  describe 'json単体出力オプションに於いて' do
+    before do
+      @op = FactoryGirl.create :original_picture, :artist_id => @artist.id
+      @p = FactoryGirl.create :picture, :original_picture_id => @op.id, :license_id => @license.id, :artist_id => @artist.id
+      @rp = FactoryGirl.create :resource_picture, :artist_id => @artist.id, :license_id => @license.id, :original_picture_id => @op.id, :picture_id => @p.id
+      @sbt = FactoryGirl.create :speech_balloon_template
+      @comic = FactoryGirl.create :comic, :author_id => @author.id, :visible => 1
+      @story = FactoryGirl.create :story, :author_id => @author.id, :comic_id => @comic.id
+    end
+    it 'ストーリーを含んでいる' do
+      r = Comic.show(@comic.id, @author).to_json Comic.show_json_opt
+      j = JSON.parse r
+      i = j
+      i.has_key?('stories').should be_true
+    end
+    it '作家を含んでいる' do
+      r = Comic.show(@comic.id, @author).to_json Comic.show_json_opt
+      j = JSON.parse r
+      i = j
+      i.has_key?('author').should be_true
+    end
+  end
+  
+  describe '削除に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :author_id => @author.id, :comic_id => @comic.id
+      @other_comic = FactoryGirl.create :comic, :author_id => @author.id
+      @other_story = FactoryGirl.create :story, :author_id => @author.id, :comic_id => @other_comic.id
+    end
+    context 'つつがなく終わるとき' do
+      it '自身を削除する' do
+        lambda {
+          r = @comic.destroy_with_story
+        }.should change(Comic, :count).by(-1)
+        lambda {
+          r = Comic.find @comic.id
+        }.should raise_error
+      end
+      it '自身にリンクしているストーリーをすべて削除する' do
+        lambda {
+          r = @comic.destroy_with_story
+        }.should change(Story, :count).by(-1)
+        lambda {
+          r = Story.find @story.id
+        }.should raise_error
+      end
+      it 'Trueを返す' do
+        r = @comic.destroy_with_story
+        r.should be_true
+      end
+    end
+    context '削除に失敗したとき' do
+      before do
+        Story.any_instance.stub(:destroy).with(any_args).and_return(false)
+      end
+      it 'Falseを返す' do
+        r = @comic.destroy_with_story
+        r.should be_false
+      end
+      it 'ロールバックしている' do
+        lambda {
+          r = @comic.destroy_with_story
+        }.should_not change(Comic, :count)
+        lambda {
+          r = @comic.destroy_with_story
+        }.should_not change(Story, :count)
+      end
+    end
+  end
+end
index 8debd82..7f0567e 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 
 describe GroundColor do
   before do
+    SpeechBalloonTemplate.delete_all
     @admin = FactoryGirl.create :admin
     @user = FactoryGirl.create( :user_yas)
     @author = FactoryGirl.create :author, :user_id => @user.id
index 680b503..886982a 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 
 describe GroundPicture do
   before do
+    SpeechBalloonTemplate.delete_all
     @admin = FactoryGirl.create :admin
     @user = FactoryGirl.create( :user_yas)
     @author = FactoryGirl.create :author, :user_id => @user.id
index 685c69a..482ecc6 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 
 describe LicenseGroup do
   before do
+    SpeechBalloonTemplate.delete_all
     #テストデータを用意してね
     @f = Rails.root + 'spec/json/license_group.json'
     @t = File.open(@f, 'r').read
index cc587fc..ede81d9 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 
 describe OriginalPicture do
   before do
+    SpeechBalloonTemplate.delete_all
     @admin = FactoryGirl.create :admin
     @user = FactoryGirl.create( :user_yas)
     @author = FactoryGirl.create :author, :user_id => @user.id
index 364a032..77eb490 100644 (file)
@@ -3,6 +3,7 @@ require 'spec_helper'
 #コマ絵
 describe PanelPicture do
   before do
+    SpeechBalloonTemplate.delete_all
     @admin = FactoryGirl.create :admin
     @user = FactoryGirl.create( :user_yas)
     @author = FactoryGirl.create :author, :user_id => @user.id
index a1582ee..416fb17 100644 (file)
@@ -3,6 +3,7 @@ require 'spec_helper'
 #コマ\r
 describe Panel do\r
   before do\r
+    SpeechBalloonTemplate.delete_all\r
     @admin = FactoryGirl.create :admin\r
     @sp = FactoryGirl.create :system_picture\r
     @lg = FactoryGirl.create :license_group\r
index fd3433b..cccfb5a 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 \r
 describe ResourcePicture do\r
   before do\r
+    SpeechBalloonTemplate.delete_all\r
     @admin = FactoryGirl.create :admin\r
     @user = FactoryGirl.create( :user_yas)\r
     @author = FactoryGirl.create :author, :user_id => @user.id\r
index bc3fcb2..c0927a1 100644 (file)
@@ -1,12 +1,13 @@
 # -*- encoding: utf-8 -*-
 require 'spec_helper'
-#ã\82¹ã\83\88ã\83¼ã\83ªã\83¼
+#ã\82¹ã\82¯ã\82³ã\83\9e
 describe ScrollPanel do
   before do
     @admin = FactoryGirl.create :admin
     @sp = FactoryGirl.create :system_picture
     @lg = FactoryGirl.create :license_group
     @license = FactoryGirl.create :license, :license_group_id => @lg.id, :system_picture_id => @sp.id
+    SpeechBalloonTemplate.delete_all
     @speech_balloon_template = FactoryGirl.create :speech_balloon_template, "name" => "circle@pettan.com", "classname" => "CircleSpeechBalloon", "caption" => "cc",  "system_picture_id" => @sp.id, "settings" => '{}'
     @writing_format = FactoryGirl.create :writing_format
     @user = FactoryGirl.create( :user_yas)
@@ -46,7 +47,7 @@ describe ScrollPanel do
         @scroll_panel.scroll_id = 'a'
         @scroll_panel.should_not be_valid
       end
-      it 'å­\98å\9c¨ã\81\99ã\82\8bã\82³ã\83\9fã\83\83ã\82¯でなければ失敗する' do
+      it 'å­\98å\9c¨ã\81\99ã\82\8bã\82¹ã\82¯ã\83­ã\83¼ã\83«でなければ失敗する' do
         @scroll_panel.scroll_id = 0
         @scroll_panel.should_not be_valid
       end
@@ -212,12 +213,12 @@ describe ScrollPanel do
         MagicNumber['run_mode'] = 1
         ScrollPanel.any_instance.stub(:reader_role_check).and_return(true)
       end
-      it 'è\87ªèº«ã\81®ã\82³ã\83\9fã\83\83ã\82¯に所持判定を問い合わせしている' do
+      it 'è\87ªèº«ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«に所持判定を問い合わせしている' do
         Scroll.any_instance.stub(:own?).and_return(true)
         Scroll.any_instance.should_receive(:own?).with(any_args).exactly(1)
         r = @scroll_panel.visible?([@author])
       end
-      it 'è\87ªèº«ã\81®ã\82³ã\83\9fã\83\83ã\82¯に閲覧許可を問い合わせしている' do
+      it 'è\87ªèº«ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«に閲覧許可を問い合わせしている' do
         Scroll.any_instance.stub(:own?).and_return(false)
         Scroll.any_instance.stub(:visible?).and_return(true)
         Scroll.any_instance.should_receive(:visible?).with(any_args).exactly(1)
@@ -229,19 +230,19 @@ describe ScrollPanel do
         MagicNumber['run_mode'] = 1
         ScrollPanel.any_instance.stub(:reader_role_check).and_return(true)
       end
-      it 'è\87ªå\88\86ã\81®ã\82³ã\83\9fã\83\83ã\82¯ã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼なら許可する' do
+      it 'è\87ªå\88\86ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«ã\81®ã\82¹ã\82¯ã\82³ã\83\9eなら許可する' do
         Scroll.any_instance.stub(:own?).and_return(true)
         Scroll.any_instance.stub(:visible).and_return(0)
         r = @scroll_panel.visible?([@author])
         r.should be_true
       end
-      it 'ä»\96人ã\81®é\9d\9eå\85¬é\96\8bã\82³ã\83\9fã\83\83ã\82¯ã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼なら許可しない' do
+      it 'ä»\96人ã\81®é\9d\9eå\85¬é\96\8bã\82¹ã\82¯ã\83­ã\83¼ã\83«ã\81®ã\82¹ã\82¯ã\82³ã\83\9eなら許可しない' do
         Scroll.any_instance.stub(:own?).and_return(false)
         Scroll.any_instance.stub(:visible).and_return(0)
         r = @scroll_panel.visible?([@author])
         r.should be_false
       end
-      it 'ä»\96人ã\81®ã\82³ã\83\9fã\83\83ã\82¯ã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼でも公開なら許可する' do
+      it 'ä»\96人ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«ã\81®ã\82¹ã\82¯ã\82³ã\83\9eでも公開なら許可する' do
         Scroll.any_instance.stub(:own?).and_return(false)
         Scroll.any_instance.stub(:visible).and_return(1)
         r = @scroll_panel.visible?([@author])
@@ -306,7 +307,7 @@ describe ScrollPanel do
       c.should eq [@scroll_panel]
     end
     it 't順で並んでいる' do
-      #å\85¬é\96\8bã\82³ã\83\9fã\83\83ã\82¯ã\81®å\85¬é\96\8bã\82³ã\83\9eã\81¯(ä»\96人ã\81®ã\82³ã\83\9fã\83\83ã\82¯であっても)含んでいる
+      #å\85¬é\96\8bã\82¹ã\82¯ã\83­ã\83¼ã\83«ã\81®å\85¬é\96\8bã\82³ã\83\9eã\81¯(ä»\96人ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«であっても)含んでいる
       v = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id, :t => 1
       c = ScrollPanel.play_list @scroll, @author
       c.should eq [ @scroll_panel, v]
@@ -378,12 +379,12 @@ describe ScrollPanel do
       c.should eq [@scroll_panel]
     end
     it '時系列で並んでいる' do
-      #å\85¬é\96\8bã\82³ã\83\9fã\83\83ã\82¯のScrollPanelは(他人のScrollPanelであっても)含んでいる
+      #å\85¬é\96\8bã\82¹ã\82¯ã\83­ã\83¼ã\83«のScrollPanelは(他人のScrollPanelであっても)含んでいる
       v = FactoryGirl.create :scroll_panel, :author_id => @other_author.id, :scroll_id => @other_scroll.id, :panel_id => @other_panel.id, :t => 0, :updated_at => Time.now + 100
       c = ScrollPanel.list 
       c.should eq [ v, @scroll_panel]
     end
-    it 'é\9d\9eå\85¬é\96\8bã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼ã\81¯(è\87ªå\88\86ã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼であっても)含まない' do
+    it 'é\9d\9eå\85¬é\96\8bã\81®ã\82¹ã\82¯ã\82³ã\83\9eã\81¯(è\87ªå\88\86ã\81®ã\82¹ã\82¯ã\82³ã\83\9eであっても)含まない' do
       h = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @hidden_scroll.id, :panel_id => @panel.id, :t => 0
       c = ScrollPanel.list 
       c.should eq [ @scroll_panel]
@@ -415,7 +416,7 @@ describe ScrollPanel do
     end
   end
   
-  describe 'è\87ªå\88\86ã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼一覧取得に於いて' do
+  describe 'è\87ªå\88\86ã\81®ã\82¹ã\82¯ã\82³ã\83\9e一覧取得に於いて' do
     before do
       @scroll = FactoryGirl.create :scroll, :author_id => @author.id, :visible => 1
       @panel = FactoryGirl.create :panel, :author_id => @author.id, :publish => 1
@@ -441,12 +442,12 @@ describe ScrollPanel do
       sl = ScrollPanel.mylist @author
       sl.should eq [ns, @scroll_panel]
     end
-    it 'ä»\96人ã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼ã\81¯ã\82³ã\83\9eã\82³ã\83\9fã\83\83ã\82¯ともに公開でも含まない' do
+    it 'ä»\96人ã\81®ã\82¹ã\82¯ã\82³ã\83\9eã\81¯ã\82³ã\83\9eã\82¹ã\82¯ã\83­ã\83¼ã\83«ともに公開でも含まない' do
       so = FactoryGirl.create :scroll_panel, :author_id => @other_author.id, :scroll_id => @other_scroll.id, :panel_id => @other_panel.id
       sl = ScrollPanel.mylist @author
       sl.should eq [@scroll_panel]
     end
-    it 'è\87ªå\88\86ã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼ã\81¯ã\82³ã\83\9eã\82³ã\83\9fã\83\83ã\82¯ともに非公開でも含んでいる' do
+    it 'è\87ªå\88\86ã\81®ã\82¹ã\82¯ã\82³ã\83\9eã\81¯ã\82³ã\83\9eã\82¹ã\82¯ã\83­ã\83¼ã\83«ともに非公開でも含んでいる' do
       hs = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @hscroll.id, :panel_id => @hpanel.id, :updated_at => Time.now + 100
       sl = ScrollPanel.mylist @author
       sl.should eq [hs, @scroll_panel]
@@ -479,7 +480,7 @@ describe ScrollPanel do
     end
   end
   
-  describe 'ä»\96ä½\9c家ã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼一覧取得に於いて' do
+  describe 'ä»\96ä½\9c家ã\81®ã\82¹ã\82¯ã\82³ã\83\9e一覧取得に於いて' do
     before do
       @scroll = FactoryGirl.create :scroll, :author_id => @author.id, :visible => 1
       @panel = FactoryGirl.create :panel, :author_id => @author.id, :publish => 1
@@ -497,7 +498,7 @@ describe ScrollPanel do
       r = ScrollPanel.himlist @other_author
       r.should eq [ns, @other_scroll_panel]
     end
-    it 'å\85¬é\96\8bã\82³ã\83\9fã\83\83ã\82¯ã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼' do
+    it 'å\85¬é\96\8bã\82¹ã\82¯ã\83­ã\83¼ã\83«ã\81®ã\82¹ã\82¯ã\82³ã\83\9e' do
       @hscroll = FactoryGirl.create :scroll, :author_id => @other_author.id, :visible => 0
       ns = FactoryGirl.create :scroll_panel, :author_id => @other_author.id, :scroll_id => @hscroll.id, :panel_id => @other_panel.id, :t => 1, :updated_at => Time.now + 100
       r = ScrollPanel.himlist @other_author
@@ -531,7 +532,7 @@ describe ScrollPanel do
     end
   end
   
-  describe 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼一覧ページ制御に於いて' do
+  describe 'ã\82¹ã\82¯ã\82³ã\83\9e一覧ページ制御に於いて' do
     before do
       ScrollPanel.stub(:count).with(any_args).and_return(100)
     end
@@ -539,7 +540,7 @@ describe ScrollPanel do
       r = ScrollPanel.list_paginate 
       r.is_a?(Kaminari::PaginatableArray).should be_true
     end
-    it 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼一覧の取得条件を利用している' do
+    it 'ã\82¹ã\82¯ã\82³ã\83\9e一覧の取得条件を利用している' do
       ScrollPanel.stub(:list_where).with(any_args).and_return('')
       ScrollPanel.should_receive(:list_where).with(any_args).exactly(1)
       r = ScrollPanel.list_paginate 
@@ -551,7 +552,7 @@ describe ScrollPanel do
     end
   end
   
-  describe 'è\87ªå\88\86ã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼一覧ページ制御に於いて' do
+  describe 'è\87ªå\88\86ã\81®ã\82¹ã\82¯ã\82³ã\83\9e一覧ページ制御に於いて' do
     before do
       ScrollPanel.stub(:count).with(any_args).and_return(100)
     end
@@ -559,7 +560,7 @@ describe ScrollPanel do
       r = ScrollPanel.mylist_paginate @author
       r.is_a?(Kaminari::PaginatableArray).should be_true
     end
-    it 'è\87ªå\88\86ã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼一覧の取得条件を利用している' do
+    it 'è\87ªå\88\86ã\81®ã\82¹ã\82¯ã\82³ã\83\9e一覧の取得条件を利用している' do
       ScrollPanel.stub(:mylist_where).with(any_args).and_return('')
       ScrollPanel.should_receive(:mylist_where).with(any_args).exactly(1)
       r = ScrollPanel.mylist_paginate @author
@@ -571,7 +572,7 @@ describe ScrollPanel do
     end
   end
   
-  describe 'ä»\96ä½\9c家ã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼一覧ページ制御に於いて' do
+  describe 'ä»\96ä½\9c家ã\81®ã\82¹ã\82¯ã\82³ã\83\9e一覧ページ制御に於いて' do
     before do
       ScrollPanel.stub(:count).with(any_args).and_return(100)
     end
@@ -579,7 +580,7 @@ describe ScrollPanel do
       r = ScrollPanel.himlist_paginate @other_author
       r.is_a?(Kaminari::PaginatableArray).should be_true
     end
-    it 'ä»\96ä½\9c家ã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼一覧の取得条件を利用している' do
+    it 'ä»\96ä½\9c家ã\81®ã\82¹ã\82¯ã\82³ã\83\9e一覧の取得条件を利用している' do
       ScrollPanel.stub(:himlist_where).with(any_args).and_return('')
       ScrollPanel.should_receive(:himlist_where).with(any_args).exactly(1)
       r = ScrollPanel.himlist_paginate @other_author
@@ -596,11 +597,11 @@ describe ScrollPanel do
       r = ScrollPanel.list_opt
       r.should have(3).items
     end
-    it 'ã\82³ã\83\9fã\83\83ã\82¯を含んでいる' do
+    it 'ã\82¹ã\82¯ã\83­ã\83¼ã\83«を含んでいる' do
       r = ScrollPanel.list_opt
       r.has_key?(:scroll).should be_true
     end
-      it 'ã\82³ã\83\9fã\83\83ã\82¯は作家を含んでいる' do
+      it 'ã\82¹ã\82¯ã\83­ã\83¼ã\83«は作家を含んでいる' do
         r = ScrollPanel.list_opt
         r[:scroll].has_key?(:author).should be_true
       end
@@ -666,11 +667,11 @@ describe ScrollPanel do
         r = ScrollPanel.show @scroll_panel.id, @author
       end
     end
-    it 'æ\8c\87å®\9aã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼を返す' do
+    it 'æ\8c\87å®\9aã\81®ã\82¹ã\82¯ã\82³ã\83\9eを返す' do
       l = ScrollPanel.show @scroll_panel.id, @author
       l.should eq @scroll_panel
     end
-    context 'ä»\96人ã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼を開こうとしたとき' do
+    context 'ä»\96人ã\81®ã\82¹ã\82¯ã\82³ã\83\9eを開こうとしたとき' do
       it '403Forbidden例外を返す' do
         ScrollPanel.any_instance.stub(:visible?).with(@other_author).and_return(false)
         lambda{
@@ -678,7 +679,7 @@ describe ScrollPanel do
         }.should raise_error(ActiveRecord::Forbidden)
       end
     end
-    context 'å­\98å\9c¨ã\81\97ã\81ªã\81\84ã\82¹ã\83\88ã\83¼ã\83ªã\83¼を開こうとしたとき' do
+    context 'å­\98å\9c¨ã\81\97ã\81ªã\81\84ã\82¹ã\82¯ã\82³ã\83\9eを開こうとしたとき' do
       it '404RecordNotFound例外を返す' do
         lambda{
           ScrollPanel.show 110, @author
@@ -705,11 +706,11 @@ describe ScrollPanel do
         r = ScrollPanel.edit @scroll_panel.id, @author
       end
     end
-    it 'æ\8c\87å®\9aã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼を返す' do
+    it 'æ\8c\87å®\9aã\81®ã\82¹ã\82¯ã\82³ã\83\9eを返す' do
       l = ScrollPanel.edit @scroll_panel.id, @author
       l.should eq @scroll_panel
     end
-    context 'ä»\96人ã\81®ã\82¹ã\83\88ã\83¼ã\83ªã\83¼を開こうとしたとき' do
+    context 'ä»\96人ã\81®ã\82¹ã\82¯ã\82³ã\83\9eを開こうとしたとき' do
       it '403Forbidden例外を返す' do
         ScrollPanel.any_instance.stub(:own?).and_return(false)
         lambda{
@@ -717,7 +718,7 @@ describe ScrollPanel do
         }.should raise_error(ActiveRecord::Forbidden)
       end
     end
-    context 'å­\98å\9c¨ã\81\97ã\81ªã\81\84ã\82¹ã\83\88ã\83¼ã\83ªã\83¼を開こうとしたとき' do
+    context 'å­\98å\9c¨ã\81\97ã\81ªã\81\84ã\82¹ã\82¯ã\82³ã\83\9eを開こうとしたとき' do
       it '404RecordNotFound例外を返す' do
         lambda{
           ScrollPanel.edit 110, @author
@@ -735,11 +736,11 @@ describe ScrollPanel do
       r = ScrollPanel.show_opt[:include]
       r.should have(3).items
     end
-    it 'ã\82³ã\83\9fã\83\83ã\82¯を含んでいる' do
+    it 'ã\82¹ã\82¯ã\83­ã\83¼ã\83«を含んでいる' do
       r = ScrollPanel.show_opt[:include]
       r.has_key?(:scroll).should be_true
     end
-      it 'ã\82³ã\83\9fã\83\83ã\82¯は作家を含んでいる' do
+      it 'ã\82¹ã\82¯ã\83­ã\83¼ã\83«は作家を含んでいる' do
         r = ScrollPanel.show_opt[:include]
         r[:scroll].has_key?(:author).should be_true
       end
@@ -787,7 +788,7 @@ describe ScrollPanel do
   describe 'json単体取得オプションに於いて' do
   end
   
-  describe 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼のjson出力に於いて' do
+  describe 'ã\82¹ã\82¯ã\82³ã\83\9eのjson出力に於いて' do
     before do
       #コマを作成しておく。
       @panel = FactoryGirl.create :panel, :author_id => @author.id
@@ -854,7 +855,7 @@ describe ScrollPanel do
     end
   end
   
-  describe 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼リストのjson出力に於いて' do
+  describe 'ã\82¹ã\82¯ã\82³ã\83\9eリストのjson出力に於いて' do
     before do
       @panel = FactoryGirl.create :panel, :author_id => @author.id
       @scroll = FactoryGirl.create :scroll, :author_id => @author.id
@@ -862,7 +863,7 @@ describe ScrollPanel do
       ScrollPanel.any_instance.stub(:scroll_panel_as_json).with(@author).and_return('{"s": 5}')
     end
     context 'つつがなく終わるとき' do
-      it 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼のjson出力を依頼している' do
+      it 'ã\82¹ã\82¯ã\82³ã\83\9eのjson出力を依頼している' do
         ScrollPanel.any_instance.should_receive(:scroll_panel_as_json).with(@author).exactly(1)
         r = ScrollPanel.list_as_json_text [@scroll_panel], @author
       end
@@ -872,7 +873,7 @@ describe ScrollPanel do
       j = JSON.parse r
       j.is_a?(Array).should be_true
     end
-    it 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼を含んでいる' do
+    it 'ã\82¹ã\82¯ã\82³ã\83\9eを含んでいる' do
       r = ScrollPanel.list_as_json_text [@scroll_panel], @author
       j = JSON.parse r
       j.first.has_key?('s').should be_true
@@ -927,7 +928,7 @@ describe ScrollPanel do
       @panel = FactoryGirl.create :panel, :author_id => @author.id
     end
     
-    context 'ã\82³ã\83\9fã\83\83ã\82¯初のコマなら' do
+    context 'ã\82¹ã\82¯ã\83­ã\83¼ã\83«初のコマなら' do
       it '0を補充値とする' do
         @scroll_panel = FactoryGirl.build :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id
         @scroll_panel.t = nil
@@ -935,7 +936,7 @@ describe ScrollPanel do
         r.should eq 0
       end
     end
-    context 'ã\82³ã\83\9fã\83\83ã\82¯に一個コマがあるとき' do
+    context 'ã\82¹ã\82¯ã\83­ã\83¼ã\83«に一個コマがあるとき' do
       it '1を補充値とする' do
         FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id, :t => 0
         @scroll_panel = FactoryGirl.build :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id
@@ -944,7 +945,7 @@ describe ScrollPanel do
         r.should eq 1
       end
     end
-    context 'ã\82³ã\83\9fã\83\83ã\82¯に2個コマがあるとき' do
+    context 'ã\82¹ã\82¯ã\83­ã\83¼ã\83«に2個コマがあるとき' do
       it '2を補充値とする' do
         FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id, :t => 0
         FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id, :t => 1
@@ -994,20 +995,20 @@ describe ScrollPanel do
       @c2scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll2.id, :panel_id => @panel.id, :author_id => @author.id
     end
     context 'つつがなく終わるとき' do
-      it 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼ã\81\8bã\82\89å\90\8cä¸\80ã\82³ã\83\9fã\83\83ã\82¯のtだけを収集している' do
+      it 'ã\82¹ã\82¯ã\82³ã\83\9eã\81\8bã\82\89å\90\8cä¸\80ã\82¹ã\82¯ã\83­ã\83¼ã\83«のtだけを収集している' do
         r = ScrollPanel.collect_t @scroll_panel
         r.should eq [0]
       end
     end
     context '複数コマのとき' do
-      it 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼ã\81\8bã\82\89å\90\8cä¸\80ã\82³ã\83\9fã\83\83ã\82¯のtだけを収集している' do
+      it 'ã\82¹ã\82¯ã\82³ã\83\9eã\81\8bã\82\89å\90\8cä¸\80ã\82¹ã\82¯ã\83­ã\83¼ã\83«のtだけを収集している' do
         @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
         r = ScrollPanel.collect_t @scroll_panel
         r.sort.should eq [0, 1]
       end
     end
-    context 'è¤\87æ\95°ã\82³ã\83\9eã\81§ã\83¨ã\82½ã\81®ã\82³ã\83\9fã\83\83ã\82¯も混じっているとき' do
-      it 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼ã\81\8bã\82\89å\90\8cä¸\80ã\82³ã\83\9fã\83\83ã\82¯のtだけを収集している' do
+    context 'è¤\87æ\95°ã\82³ã\83\9eã\81§ã\83¨ã\82½ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«も混じっているとき' do
+      it 'ã\82¹ã\82¯ã\82³ã\83\9eã\81\8bã\82\89å\90\8cä¸\80ã\82¹ã\82¯ã\83­ã\83¼ã\83«のtだけを収集している' do
         @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
         r = ScrollPanel.collect_t @scroll_panel
         r.sort.should eq [0, 1]
@@ -1109,7 +1110,7 @@ describe ScrollPanel do
         l.sort.should eq [0, 1, 3, 4, 5]
       end
     end
-    context 'å\85\88ã\81»ã\81©ã\81®ã\82±ã\83¼ã\82¹+ä»\96ã\81®ã\82³ã\83\9fã\83\83ã\82¯1件で挿入したとき' do
+    context 'å\85\88ã\81»ã\81©ã\81®ã\82±ã\83¼ã\82¹+ä»\96ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«1件で挿入したとき' do
       before do
         @scroll2 = FactoryGirl.create :scroll, :author_id => @author.id
         @scroll_panelc2 = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll2.id, :panel_id => @panel.id, :author_id => @author.id
@@ -1125,7 +1126,7 @@ describe ScrollPanel do
         l = ScrollPanel.find(:all, :conditions => ['scroll_id = ?', @scroll.id]).map {|s| s.t }
         l.sort.should eq [0, 1, 3, 4, 5]
       end
-      it 'ä»\96ã\81®ã\82³ã\83\9fã\83\83ã\82¯に影響がない' do
+      it 'ä»\96ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«に影響がない' do
         ot = @scroll_panelc2.t
         @scroll_panel6.insert_shift
         @scroll_panelc2.reload
@@ -1225,7 +1226,7 @@ describe ScrollPanel do
         @scroll_panel3.t.should eq 3
       end
     end
-    context 'å\85\88ã\81»ã\81©ã\81®ã\82±ã\83¼ã\82¹+ä»\96ã\81®ã\82³ã\83\9fã\83\83ã\82¯1件で挿入したとき' do
+    context 'å\85\88ã\81»ã\81©ã\81®ã\82±ã\83¼ã\82¹+ä»\96ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«1件で挿入したとき' do
       before do
         @scroll2 = FactoryGirl.create :scroll, :author_id => @author.id
         @scroll_panelc2 = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll2.id, :panel_id => @panel.id, :author_id => @author.id
@@ -1262,7 +1263,7 @@ describe ScrollPanel do
         @scroll_panel3.reload
         @scroll_panel3.t.should eq 3
       end
-      it 'ä»\96ã\81®ã\82³ã\83\9fã\83\83ã\82¯に影響がない' do
+      it 'ä»\96ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«に影響がない' do
         @scroll_panel4.lesser_shift @ot
         @scroll_panelc2.reload
         @scroll_panelc2.t.should eq 0
@@ -1386,7 +1387,7 @@ describe ScrollPanel do
         @scroll_panel4.t.should eq 2
       end
     end
-    context 'å\85\88ã\81»ã\81©ã\81®ã\82±ã\83¼ã\82¹+ä»\96ã\81®ã\82³ã\83\9fã\83\83ã\82¯1件で挿入したとき' do
+    context 'å\85\88ã\81»ã\81©ã\81®ã\82±ã\83¼ã\82¹+ä»\96ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«1件で挿入したとき' do
       before do
         @scroll2 = FactoryGirl.create :scroll, :author_id => @author.id
         @scroll_panelc2 = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll2.id, :panel_id => @panel.id, :author_id => @author.id
@@ -1424,7 +1425,7 @@ describe ScrollPanel do
         @scroll_panel4.reload
         @scroll_panel4.t.should eq 2
       end
-      it 'ä»\96ã\81®ã\82³ã\83\9fã\83\83ã\82¯に影響がない' do
+      it 'ä»\96ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«に影響がない' do
         @scroll_panel2.higher_shift @ot
         @scroll_panelc2.reload
         @scroll_panelc2.t.should eq 0
@@ -1533,7 +1534,7 @@ describe ScrollPanel do
         r.should be_true
       end
     end
-    context 'ã\82³ã\83\9fã\83\83ã\82¯で引っかかるとき' do
+    context 'ã\82¹ã\82¯ã\83­ã\83¼ã\83«で引っかかるとき' do
       it 'falseを返す' do
         Panel.any_instance.stub(:usable?).with(any_args).and_return(true)
         Scroll.any_instance.stub(:own?).with(any_args).and_return(false)
@@ -1549,7 +1550,7 @@ describe ScrollPanel do
         r.should be_false
       end
     end
-    context 'ã\82³ã\83\9fã\83\83ã\82¯またはコマが指定されていなかったとき' do
+    context 'ã\82¹ã\82¯ã\83­ã\83¼ã\83«またはコマが指定されていなかったとき' do
       it 'nilを返す' do
         Scroll.any_instance.stub(:own?).with(any_args).and_return(true)
         @scroll_panel.panel_id = nil
@@ -1599,7 +1600,7 @@ describe ScrollPanel do
       end
     end
     #入れ替えテストと同じテストを実施。こちらはシフトだけでなく本尊も更新されている
-    context 'ã\83\86ã\83¼ã\83\96ã\83«ã\81«5件(t:0,1,2,3,4)+ä»\96ã\81®ã\82³ã\83\9fã\83\83ã\82¯1件で2に挿入したとき' do
+    context 'ã\83\86ã\83¼ã\83\96ã\83«ã\81«5件(t:0,1,2,3,4)+ä»\96ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«1件で2に挿入したとき' do
       before do
         @scroll2 = FactoryGirl.create :scroll, :author_id => @author.id
         @scroll_panelc2 = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll2.id, :panel_id => @panel.id, :author_id => @author.id
@@ -1640,14 +1641,14 @@ describe ScrollPanel do
         @scroll_panel6.reload
         @scroll_panel6.t.should eq 2
       end
-      it 'ä»\96ã\81®ã\82³ã\83\9fã\83\83ã\82¯に影響がない' do
+      it 'ä»\96ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«に影響がない' do
         @ot = @scroll_panelc2.t
         @scroll_panel6.store
         @scroll_panelc2.reload
         @scroll_panelc2.t.should eq @ot
       end
     end
-    context 'ã\83\86ã\83¼ã\83\96ã\83«ã\81«5件(t:0,1,2,3,4)+ä»\96ã\81®ã\82³ã\83\9fã\83\83ã\82¯1件で3を1に移動したとき' do
+    context 'ã\83\86ã\83¼ã\83\96ã\83«ã\81«5件(t:0,1,2,3,4)+ä»\96ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«1件で3を1に移動したとき' do
       before do
         @scroll2 = FactoryGirl.create :scroll, :author_id => @author.id
         @scroll_panelc2 = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll2.id, :panel_id => @panel.id, :author_id => @author.id
@@ -1684,13 +1685,13 @@ describe ScrollPanel do
         @scroll_panel4.reload
         @scroll_panel4.t.should eq 1
       end
-      it 'ä»\96ã\81®ã\82³ã\83\9fã\83\83ã\82¯に影響がない' do
+      it 'ä»\96ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«に影響がない' do
         @scroll_panel4.store @ot
         @scroll_panelc2.reload
         @scroll_panelc2.t.should eq 0
       end
     end
-    context 'ã\83\86ã\83¼ã\83\96ã\83«ã\81«5件(t:0,1,2,3,4)+ä»\96ã\81®ã\82³ã\83\9fã\83\83ã\82¯1件で1を3に移動したとき' do
+    context 'ã\83\86ã\83¼ã\83\96ã\83«ã\81«5件(t:0,1,2,3,4)+ä»\96ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«1件で1を3に移動したとき' do
       before do
         @scroll2 = FactoryGirl.create :scroll, :author_id => @author.id
         @scroll_panelc2 = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll2.id, :panel_id => @panel.id, :author_id => @author.id
@@ -1727,14 +1728,14 @@ describe ScrollPanel do
         @scroll_panel4.reload
         @scroll_panel4.t.should eq 2
       end
-      it 'ä»\96ã\81®ã\82³ã\83\9fã\83\83ã\82¯に影響がない' do
+      it 'ä»\96ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«に影響がない' do
         @scroll_panel2.store @ot
         @scroll_panelc2.reload
         @scroll_panelc2.t.should eq 0
       end
     end
     #ロールバックテスト。入れ替えが直接DBをいじるので、すべてのケースで確実にロールバックを確認する
-    context 'ã\83\86ã\83¼ã\83\96ã\83«ã\81«5件(t:0,1,2,3,4)+ä»\96ã\81®ã\82³ã\83\9fã\83\83ã\82¯1件で2に挿入したが保存に失敗したとき' do
+    context 'ã\83\86ã\83¼ã\83\96ã\83«ã\81«5件(t:0,1,2,3,4)+ä»\96ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«1件で2に挿入したが保存に失敗したとき' do
       before do
         ScrollPanel.any_instance.stub(:save).with(any_args).and_return(false)
         @scroll2 = FactoryGirl.create :scroll, :author_id => @author.id
@@ -1766,7 +1767,7 @@ describe ScrollPanel do
         r.should be_false
       end
     end
-    context 'ã\83\86ã\83¼ã\83\96ã\83«ã\81«5件(t:0,1,2,3,4)+ä»\96ã\81®ã\82³ã\83\9fã\83\83ã\82¯1件で3を1に移動したがシリアルチェックに失敗したとき' do
+    context 'ã\83\86ã\83¼ã\83\96ã\83«ã\81«5件(t:0,1,2,3,4)+ä»\96ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«1件で3を1に移動したがシリアルチェックに失敗したとき' do
       before do
         ScrollPanel.stub(:validate_t).with(any_args).and_return(false)
         @scroll2 = FactoryGirl.create :scroll, :author_id => @author.id
index 3b76b14..7479689 100644 (file)
@@ -1,9 +1,10 @@
 # -*- encoding: utf-8 -*-
 require 'spec_helper'
-#ã\82³ã\83\9fã\83\83ã\82¯
+#ã\82¹ã\82¯ã\83­ã\83¼ã\83«
 
 describe Scroll do
   before do
+    SpeechBalloonTemplate.delete_all
     @admin = FactoryGirl.create :admin
     @demand_user = FactoryGirl.create :demand_user
     @sp = FactoryGirl.create :system_picture
@@ -343,19 +344,19 @@ describe Scroll do
         MagicNumber['run_mode'] = 1
         Scroll.any_instance.stub(:reader_role_check).and_return(true)
       end
-      it 'è\87ªå\88\86ã\81®ã\82³ã\83\9fã\83\83ã\82¯なら許可する' do
+      it 'è\87ªå\88\86ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«なら許可する' do
         Scroll.any_instance.stub(:own?).and_return(true)
         Scroll.any_instance.stub(:visible).and_return(0)
         r = @scroll.visible?([@author])
         r.should be_true
       end
-      it 'ä»\96人ã\81®é\9d\9eå\85¬é\96\8bã\82³ã\83\9fã\83\83ã\82¯なら許可しない' do
+      it 'ä»\96人ã\81®é\9d\9eå\85¬é\96\8bã\82¹ã\82¯ã\83­ã\83¼ã\83«なら許可しない' do
         Scroll.any_instance.stub(:own?).and_return(false)
         Scroll.any_instance.stub(:visible).and_return(0)
         r = @scroll.visible?([@author])
         r.should be_false
       end
-      it 'ä»\96人ã\81®ã\82³ã\83\9fã\83\83ã\82¯でも公開なら許可する' do
+      it 'ä»\96人ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«でも公開なら許可する' do
         Scroll.any_instance.stub(:own?).and_return(false)
         Scroll.any_instance.stub(:visible).and_return(1)
         r = @scroll.visible?([@author])
@@ -404,13 +405,13 @@ describe Scroll do
       c = Scroll.list
       c.should eq [@scroll]
     end
-    it 'é\9d\9eå\85¬é\96\8bã\82³ã\83\9fã\83\83ã\82¯ã\81¯(è\87ªå\88\86ã\81®ã\82³ã\83\9fã\83\83ã\82¯であっても)含んでいない' do
+    it 'é\9d\9eå\85¬é\96\8bã\82¹ã\82¯ã\83­ã\83¼ã\83«ã\81¯(è\87ªå\88\86ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«であっても)含んでいない' do
       FactoryGirl.create :scroll, :author_id => @author.id, :visible => 0
       c = Scroll.list
       c.should eq [@scroll]
     end
     it '時系列で並んでいる' do
-      #å\85¬é\96\8bã\82³ã\83\9fã\83\83ã\82¯ã\81¯(ä»\96人ã\81®ã\82³ã\83\9fã\83\83ã\82¯であっても)含んでいる
+      #å\85¬é\96\8bã\82¹ã\82¯ã\83­ã\83¼ã\83«ã\81¯(ä»\96人ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«であっても)含んでいる
       v = FactoryGirl.create :scroll, :author_id => @other_author.id, :updated_at => Time.now + 100
       c = Scroll.list
       c.should eq [v, @scroll]
@@ -443,7 +444,7 @@ describe Scroll do
     end
   end
   
-  describe 'è\87ªå\88\86ã\81®ã\82³ã\83\9fã\83\83ã\82¯一覧取得に於いて' do
+  describe 'è\87ªå\88\86ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«一覧取得に於いて' do
     before do
       @scroll = FactoryGirl.create :scroll, :author_id => @author.id
     end
@@ -463,12 +464,12 @@ describe Scroll do
       cl = Scroll.mylist @author
       cl.should eq [nc, @scroll]
     end
-    it 'ä»\96人ã\81®ã\82³ã\83\9fã\83\83ã\82¯は公開でも含まない' do
+    it 'ä»\96人ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«は公開でも含まない' do
       nc = FactoryGirl.create :scroll, :author_id => @other_author.id, :visible => 1
       cl = Scroll.mylist @author
       cl.should eq [@scroll]
     end
-    it 'è\87ªå\88\86ã\81®ã\82³ã\83\9fã\83\83ã\82¯は非公開でも含んでいる' do
+    it 'è\87ªå\88\86ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«は非公開でも含んでいる' do
       nc = FactoryGirl.create :scroll, :author_id => @author.id, :visible => 0, :updated_at => Time.now + 100
       cl = Scroll.mylist @author
       cl.should eq [nc, @scroll]
@@ -500,7 +501,7 @@ describe Scroll do
     end
   end
   
-  describe 'ä»\96ä½\9c家ã\81®ã\82³ã\83\9fã\83\83ã\82¯一覧取得に於いて' do
+  describe 'ä»\96ä½\9c家ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«一覧取得に於いて' do
     before do
       @scroll = FactoryGirl.create :scroll, :author_id => @author.id
       @other_scroll = FactoryGirl.create :scroll, :author_id => @other_author.id, :visible => 1
@@ -521,7 +522,7 @@ describe Scroll do
       r = Scroll.himlist @other_author
       r.should eq [nc, @other_scroll]
     end
-    it 'å\85¬é\96\8bã\82³ã\83\9fã\83\83ã\82¯に限る ' do
+    it 'å\85¬é\96\8bã\82¹ã\82¯ã\83­ã\83¼ã\83«に限る ' do
       hidden = FactoryGirl.create :scroll, :author_id => @other_author.id, :visible => 0
       r = Scroll.himlist @other_author
       r.should eq [@other_scroll]
@@ -553,7 +554,7 @@ describe Scroll do
     end
   end
   
-  describe 'ã\82³ã\83\9fã\83\83ã\82¯一覧ページ制御に於いて' do
+  describe 'ã\82¹ã\82¯ã\83­ã\83¼ã\83«一覧ページ制御に於いて' do
     before do
       Scroll.stub(:count).with(any_args).and_return(100)
     end
@@ -561,7 +562,7 @@ describe Scroll do
       r = Scroll.list_paginate 
       r.is_a?(Kaminari::PaginatableArray).should be_true
     end
-    it 'ã\82³ã\83\9fã\83\83ã\82¯一覧の取得条件を利用している' do
+    it 'ã\82¹ã\82¯ã\83­ã\83¼ã\83«一覧の取得条件を利用している' do
       Scroll.stub(:list_where).with(any_args).and_return('')
       Scroll.should_receive(:list_where).with(any_args).exactly(1)
       r = Scroll.list_paginate 
@@ -573,7 +574,7 @@ describe Scroll do
     end
   end
   
-  describe 'è\87ªå\88\86ã\81®ã\82³ã\83\9fã\83\83ã\82¯一覧ページ制御に於いて' do
+  describe 'è\87ªå\88\86ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«一覧ページ制御に於いて' do
     before do
       Scroll.stub(:count).with(any_args).and_return(100)
     end
@@ -581,7 +582,7 @@ describe Scroll do
       r = Scroll.mylist_paginate @author
       r.is_a?(Kaminari::PaginatableArray).should be_true
     end
-    it 'è\87ªå\88\86ã\81®ã\82³ã\83\9fã\83\83ã\82¯一覧の取得条件を利用している' do
+    it 'è\87ªå\88\86ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«一覧の取得条件を利用している' do
       Scroll.stub(:mylist_where).with(any_args).and_return('')
       Scroll.should_receive(:mylist_where).with(any_args).exactly(1)
       r = Scroll.mylist_paginate @author
@@ -593,7 +594,7 @@ describe Scroll do
     end
   end
   
-  describe 'ä»\96ä½\9c家ã\81®ã\82³ã\83\9fã\83\83ã\82¯一覧ページ制御に於いて' do
+  describe 'ä»\96ä½\9c家ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«一覧ページ制御に於いて' do
     before do
       Scroll.stub(:count).with(any_args).and_return(100)
     end
@@ -601,7 +602,7 @@ describe Scroll do
       r = Scroll.himlist_paginate @other_author
       r.is_a?(Kaminari::PaginatableArray).should be_true
     end
-    it 'ä»\96ä½\9c家ã\81®ã\82³ã\83\9fã\83\83ã\82¯一覧の取得条件を利用している' do
+    it 'ä»\96ä½\9c家ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«一覧の取得条件を利用している' do
       Scroll.stub(:himlist_where).with(any_args).and_return('')
       Scroll.should_receive(:himlist_where).with(any_args).exactly(1)
       r = Scroll.himlist_paginate @other_author
@@ -618,11 +619,11 @@ describe Scroll do
       r = Scroll.list_opt
       r.should have(2).items
     end
-    it 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼を含んでいる' do
+    it 'ã\82¹ã\82¯ã\82³ã\83\9eを含んでいる' do
       r = Scroll.list_opt
       r.has_key?(:scroll_panels).should be_true
     end
-      it 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼はコマを含んでいる' do
+      it 'ã\82¹ã\82¯ã\82³ã\83\9eはコマを含んでいる' do
         r = Scroll.list_opt
         r[:scroll_panels].has_key?(:panel).should be_true
       end
@@ -641,13 +642,13 @@ describe Scroll do
       @panel = FactoryGirl.create :panel, :author_id => @author.id, :publish => 1
       @scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id
     end
-    it 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼を含んでいる' do
+    it 'ã\82¹ã\82¯ã\82³ã\83\9eを含んでいる' do
       r = Scroll.list.to_json Scroll.list_json_opt
       j = JSON.parse r
       i = j.first
       i.has_key?('scroll_panels').should be_true
     end
-      it 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼はコマを含んでいる' do
+      it 'ã\82¹ã\82¯ã\82³ã\83\9eはコマを含んでいる' do
         r = Scroll.list.to_json Scroll.list_json_opt
         j = JSON.parse r
         i = j.first
@@ -678,7 +679,7 @@ describe Scroll do
         r = Scroll.show @scroll.id, @author
       end
     end
-    it 'æ\8c\87å®\9aã\81®ã\82³ã\83\9fã\83\83ã\82¯を返す' do
+    it 'æ\8c\87å®\9aã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«を返す' do
       c = Scroll.show @scroll.id, @author
       c.should eq @scroll
     end
@@ -690,7 +691,7 @@ describe Scroll do
         }.should raise_error(ActiveRecord::Forbidden)
       end
     end
-    context 'å­\98å\9c¨ã\81\97ã\81ªã\81\84ã\82³ã\83\9fã\83\83ã\82¯を開こうとしたとき' do
+    context 'å­\98å\9c¨ã\81\97ã\81ªã\81\84ã\82¹ã\82¯ã\83­ã\83¼ã\83«を開こうとしたとき' do
       it '404RecordNotFound例外を返す' do
         lambda{
           Scroll.show 110, @author
@@ -715,12 +716,12 @@ describe Scroll do
         r = Scroll.edit @scroll.id, @author
       end
     end
-    it 'æ\8c\87å®\9aã\81®ã\82³ã\83\9fã\83\83ã\82¯を返す' do
+    it 'æ\8c\87å®\9aã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«を返す' do
       Scroll.any_instance.stub(:own?).and_return(true)
       c = Scroll.edit @scroll.id, @author.id
       c.should eq @scroll
     end
-    context 'ä»\96人ã\81®ã\82³ã\83\9fã\83\83ã\82¯を開こうとしたとき' do
+    context 'ä»\96人ã\81®ã\82¹ã\82¯ã\83­ã\83¼ã\83«を開こうとしたとき' do
       it '403Forbidden例外を返す' do
         Scroll.any_instance.stub(:own?).and_return(false)
         lambda{
@@ -728,7 +729,7 @@ describe Scroll do
         }.should raise_error(ActiveRecord::Forbidden)
       end
     end
-    context 'å­\98å\9c¨ã\81\97ã\81ªã\81\84ã\82³ã\83\9fã\83\83ã\82¯を開こうとしたとき' do
+    context 'å­\98å\9c¨ã\81\97ã\81ªã\81\84ã\82¹ã\82¯ã\83­ã\83¼ã\83«を開こうとしたとき' do
       it '404RecordNotFound例外を返す' do
         lambda{
           Scroll.edit 110, @author
@@ -749,11 +750,11 @@ describe Scroll do
       r = Scroll.show_opt[:include]
       r.has_key?(:author).should be_true
     end
-    it 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼を含んでいる' do
+    it 'ã\82¹ã\82¯ã\82³ã\83\9eを含んでいる' do
       r = Scroll.show_opt[:include]
       r.has_key?(:scroll_panels).should be_true
     end
-      it 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼はコマを含んでいる' do
+      it 'ã\82¹ã\82¯ã\82³ã\83\9eはコマを含んでいる' do
         r = Scroll.show_opt[:include]
         r[:scroll_panels].has_key?(:panel).should be_true
       end
@@ -768,13 +769,13 @@ describe Scroll do
       @panel = FactoryGirl.create :panel, :author_id => @author.id, :publish => 1
       @scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id
     end
-    it 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼を含んでいる' do
+    it 'ã\82¹ã\82¯ã\82³ã\83\9eを含んでいる' do
       r = Scroll.show(@scroll.id, @author).to_json Scroll.show_json_opt
       j = JSON.parse r
       i = j
       i.has_key?('scroll_panels').should be_true
     end
-      it 'ã\82¹ã\83\88ã\83¼ã\83ªã\83¼はコマを含んでいる' do
+      it 'ã\82¹ã\82¯ã\82³ã\83\9eはコマを含んでいる' do
         r = Scroll.show(@scroll.id, @author).to_json Scroll.show_json_opt
         j = JSON.parse r
         i = j
@@ -806,7 +807,7 @@ describe Scroll do
           r = Scroll.find @scroll.id
         }.should raise_error
       end
-      it 'è\87ªèº«ã\81«ã\83ªã\83³ã\82¯ã\81\97ã\81¦ã\81\84ã\82\8bã\82¹ã\83\88ã\83¼ã\83ªã\83¼をすべて削除する' do
+      it 'è\87ªèº«ã\81«ã\83ªã\83³ã\82¯ã\81\97ã\81¦ã\81\84ã\82\8bã\82¹ã\82¯ã\82³ã\83\9eをすべて削除する' do
         lambda {
           r = @scroll.destroy_with_scroll_panel
         }.should change(ScrollPanel, :count).by(-1)
diff --git a/spec/models/sheet_panel_spec.rb b/spec/models/sheet_panel_spec.rb
new file mode 100644 (file)
index 0000000..33cba57
--- /dev/null
@@ -0,0 +1,2002 @@
+# -*- encoding: utf-8 -*-
+require 'spec_helper'
+#紙コマ
+describe SheetPanel do
+  before do
+    SpeechBalloonTemplate.delete_all
+    @admin = FactoryGirl.create :admin
+    @sp = FactoryGirl.create :system_picture
+    @lg = FactoryGirl.create :license_group
+    @license = FactoryGirl.create :license, :license_group_id => @lg.id, :system_picture_id => @sp.id
+    SpeechBalloonTemplate.delete_all
+    @speech_balloon_template = FactoryGirl.create(:speech_balloon_template, "name" => "circle@pettan.com", "classname" => "CircleSpeechBalloon", "caption" => "cc",  "system_picture_id" => @sp.id, "settings" => '{}')
+    @writing_format = FactoryGirl.create :writing_format
+    @user = FactoryGirl.create( :user_yas)
+    @author = FactoryGirl.create :author, :user_id => @user.id
+    @artist = FactoryGirl.create :artist_yas, :author_id => @author.id
+    @other_user = FactoryGirl.create( :user_yas)
+    @other_author = FactoryGirl.create :author, :user_id => @other_user.id
+    @op = FactoryGirl.create :original_picture, :artist_id => @artist.id
+    @p = FactoryGirl.create :picture, :original_picture_id => @op.id, :license_id => @license.id, :artist_id => @artist.id
+    @rp = FactoryGirl.create :resource_picture, :artist_id => @artist.id, :license_id => @license.id, :original_picture_id => @op.id, :picture_id => @p.id
+  end
+  
+  describe '検証に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @sheet_panel = FactoryGirl.build :sheet_panel, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+    end
+    
+    context 'オーソドックスなデータのとき' do
+      it '下限データが通る' do
+        @sheet_panel.x = -99999
+        @sheet_panel.y = -99999
+        @sheet_panel.z = 1
+        @sheet_panel.t = 0
+        @sheet_panel.should be_valid
+      end
+      it '上限データが通る' do
+        @sheet_panel.x = 99999
+        @sheet_panel.y = 99999
+        @sheet_panel.z = 99999
+        @sheet_panel.t = 99999
+        @sheet_panel.should be_valid
+      end
+    end
+    
+    context 'sheet_idを検証するとき' do
+      it 'nullなら失敗する' do
+        @sheet_panel.sheet_id = nil
+        @sheet_panel.should_not be_valid
+      end
+      it '数値でなければ失敗する' do
+        @sheet_panel.sheet_id = 'a'
+        @sheet_panel.should_not be_valid
+      end
+      it '存在する用紙でなければ失敗する' do
+        @sheet_panel.sheet_id = 0
+        @sheet_panel.should_not be_valid
+      end
+    end
+    
+    context 'panel_idを検証するとき' do
+      it 'nullなら失敗する' do
+        @sheet_panel.panel_id = nil
+        @sheet_panel.should_not be_valid
+      end
+      it '数値でなければ失敗する' do
+        @sheet_panel.panel_id = 'a'
+        @sheet_panel.should_not be_valid
+      end
+      it '存在するコマでなければ失敗する' do
+        @sheet_panel.panel_id = 0
+        @sheet_panel.should_not be_valid
+      end
+    end
+    
+    context 'xを検証するとき' do
+      it 'nullなら失敗する' do
+        @sheet_panel.x = nil
+        @sheet_panel.should_not be_valid
+      end
+      it '数値でなければ失敗する' do
+        @sheet_panel.x = 'a'
+        @sheet_panel.should_not be_valid
+      end
+      it '0なら通る' do
+        @sheet_panel.x = '0'
+        @sheet_panel.should be_valid
+      end
+      it '負でも通る' do
+        @sheet_panel.x = -1
+        @sheet_panel.should be_valid
+      end
+    end
+    context 'yを検証するとき' do
+      it 'nullなら失敗する' do
+        @sheet_panel.y = nil
+        @sheet_panel.should_not be_valid
+      end
+      it '数値でなければ失敗する' do
+        @sheet_panel.y = 'a'
+        @sheet_panel.should_not be_valid
+      end
+      it '0なら通る' do
+        @sheet_panel.y = '0'
+        @sheet_panel.should be_valid
+      end
+      it '負でも通る' do
+        @sheet_panel.y = -1
+        @sheet_panel.should be_valid
+      end
+    end
+    context 'zを検証するとき' do
+      it 'nullなら失敗する' do
+        @sheet_panel.z = nil
+        @sheet_panel.should_not be_valid
+      end
+      it '数値でなければ失敗する' do
+        @sheet_panel.z = 'a'
+        @sheet_panel.should_not be_valid
+      end
+      it '負なら失敗する' do
+        @sheet_panel.z = -1
+        @sheet_panel.should_not be_valid
+      end
+      it '0なら失敗する' do
+        @sheet_panel.z = 0
+        @sheet_panel.should_not be_valid
+      end
+    end
+    context 'tを検証するとき' do
+      it 'nullなら失敗する' do
+        @sheet_panel.t = nil
+        @sheet_panel.should_not be_valid
+      end
+      it '数値でなければ失敗する' do
+        @sheet_panel.t = 'a'
+        @sheet_panel.should_not be_valid
+      end
+      it '負なら失敗する' do
+        @sheet_panel.t = -1
+        @sheet_panel.should_not be_valid
+      end
+    end
+    
+    context 'author_idを検証するとき' do
+      it 'nullなら失敗する' do
+        @sheet_panel.author_id = nil
+        @sheet_panel.should_not be_valid
+      end
+      it '数値でなければ失敗する' do
+        @sheet_panel.author_id = 'a'
+        @sheet_panel.should_not be_valid
+      end
+      it '存在する作家でなければ失敗する' do
+        @sheet_panel.author_id = 0
+        @sheet_panel.should_not be_valid
+      end
+    end
+    context '全体を検証するとき' do
+    end
+  end
+  
+  describe 'デフォルト値補充に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+    end
+    
+    #dbのデフォルト値が0だから明示的にnilにしないと追加ができない
+    it 'tをnilにする' do
+      @sheet_panel = FactoryGirl.build :sheet_panel, :sheet_id => @sheet.id, :panel_id => @panel.id
+      @sheet_panel.supply_default
+      @sheet_panel.t.should be_nil
+    end
+    
+  end
+  
+  describe '上書き補充に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+    end
+    
+    context 'author_idを補充' do
+      it '問答無用でauthor_idを補充する' do
+        @sheet_panel = FactoryGirl.build :sheet_panel, :sheet_id => @sheet.id, :panel_id => @panel.id
+        @sheet_panel.author_id = nil
+        @sheet_panel.overwrite @author
+        @sheet_panel.author_id.should eq @author.id
+      end
+    end
+    
+  end
+  
+  describe '所持判定に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @sheet_panel = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id
+      @sheeto = FactoryGirl.create :sheet, :author_id => @other_author.id
+      @panelo = FactoryGirl.create :panel, :author_id => @other_author.id
+      @sheet_panelo = FactoryGirl.create :sheet_panel, :author_id => @other_author.id, :sheet_id => @sheeto.id, :panel_id => @panelo.id
+    end
+    context '事前チェックする' do
+      it '自身にロールリストからの作家取得を依頼している' do
+        SheetPanel.should_receive(:get_author_from_roles).with(any_args).exactly(1)
+        r = @sheet_panel.own?([@author])
+      end
+    end
+    context 'ロール内作家が取得できるとき' do
+      before do
+      end
+      it 'ロール内作家のidが自身の作家idと一致するなら許可する' do
+        SheetPanel.stub(:get_author_from_roles).with(any_args).and_return(@author)
+        r = @sheet_panel.own?([@author])
+        r.should be_true
+      end
+      it 'ロール内作家のidが自身の作家idと一致しないならno' do
+        SheetPanel.stub(:get_author_from_roles).with(any_args).and_return(@other_author)
+        @sheet_panel.own?(@other_author).should be_false
+      end
+    end
+    context 'ロール内作家が取得できないとき' do
+      before do
+        SheetPanel.stub(:get_author_from_roles).with(any_args).and_return(nil)
+      end
+      it 'Falseを返す' do
+        r = @sheet_panel.own?([@author])
+        r.should be_false
+      end
+    end
+  end
+  
+  describe '閲覧許可に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @sheet_panel = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id
+    end
+    context 'オープンモードのとき' do
+      before do
+        MagicNumber['run_mode'] = 0
+      end
+      it '自身にゲスト用ロールチェックを問い合わせしている' do
+        SheetPanel.any_instance.stub(:guest_role_check).and_return(true)
+        SheetPanel.any_instance.should_receive(:guest_role_check).with(any_args).exactly(1)
+        r = @sheet_panel.visible?([@author])
+      end
+      it 'ゲスト用ロールチェックが失敗したとき、falseを返す' do
+        SheetPanel.any_instance.stub(:guest_role_check).and_return(false)
+        r = @sheet_panel.visible?([@author])
+        r.should be_false
+      end
+    end
+    context 'クローズドモードのとき' do
+      before do
+        MagicNumber['run_mode'] = 1
+      end
+      it '自身に読者用ロールチェックを問い合わせしている' do
+        SheetPanel.any_instance.stub(:reader_role_check).and_return(true)
+        SheetPanel.any_instance.should_receive(:reader_role_check).with(any_args).exactly(1)
+        r = @sheet_panel.visible?([@author])
+      end
+      it '読者用ロールチェックが失敗したとき、falseを返す' do
+        SheetPanel.any_instance.stub(:reader_role_check).and_return(false)
+        r = @sheet_panel.visible?([@author])
+        r.should be_false
+      end
+    end
+    context '事前チェックする' do
+      before do
+        MagicNumber['run_mode'] = 1
+        SheetPanel.any_instance.stub(:reader_role_check).and_return(true)
+      end
+      it '自身の用紙に所持判定を問い合わせしている' do
+        Sheet.any_instance.stub(:own?).and_return(true)
+        Sheet.any_instance.should_receive(:own?).with(any_args).exactly(1)
+        r = @sheet_panel.visible?([@author])
+      end
+      it '自身の用紙に閲覧許可を問い合わせしている' do
+        Sheet.any_instance.stub(:own?).and_return(false)
+        Sheet.any_instance.stub(:visible?).and_return(true)
+        Sheet.any_instance.should_receive(:visible?).with(any_args).exactly(1)
+        r = @sheet_panel.visible?([@author])
+      end
+    end
+    context 'つつがなく終わるとき' do
+      before do
+        MagicNumber['run_mode'] = 1
+        SheetPanel.any_instance.stub(:reader_role_check).and_return(true)
+      end
+      it '自分の用紙の紙コマなら許可する' do
+        Sheet.any_instance.stub(:own?).and_return(true)
+        Sheet.any_instance.stub(:visible).and_return(0)
+        r = @sheet_panel.visible?([@author])
+        r.should be_true
+      end
+      it '他人の非公開用紙の紙コマなら許可しない' do
+        Sheet.any_instance.stub(:own?).and_return(false)
+        Sheet.any_instance.stub(:visible).and_return(0)
+        r = @sheet_panel.visible?([@author])
+        r.should be_false
+      end
+      it '他人の用紙の紙コマでも公開なら許可する' do
+        Sheet.any_instance.stub(:own?).and_return(false)
+        Sheet.any_instance.stub(:visible).and_return(1)
+        r = @sheet_panel.visible?([@author])
+        r.should be_true
+      end
+    end
+  end
+  
+  describe 'プレイリスト取得に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @sheet_panel = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id
+      @panel2 = FactoryGirl.create :panel, :author_id => @author.id, :publish => 0
+      @other_sheet = FactoryGirl.create :sheet, :author_id => @other_author.id, :visible => 1
+      @other_panel = FactoryGirl.create :panel, :author_id => @other_author.id, :publish => 1
+    end
+    context 'offset補正について' do
+      it '文字列から数値に変換される' do
+        SheetPanel.offset(100, '8').should eq 8
+      end
+      it 'nilの場合は0になる' do
+        SheetPanel.offset(100).should eq 0
+      end
+      #投稿されたコマ数以上の値が指定されたときは、最後のコマだけになる
+      #最後のコマとは、コマ数‐1.
+      it '1件のときオフセット1なら0になる' do
+        SheetPanel.offset(1, '1').should eq 0
+      end
+      it '5件のときオフセット5なら4になる' do
+        SheetPanel.offset(5, '5').should eq 4
+      end
+      # 負の値が指定されたときは、最後のコマから数えてコマを飛ばして表示する。
+      #-4のときは、最後から4つのコマを表示する。 
+      it '2件のときオフセット-1なら1になる' do
+        SheetPanel.offset(2, '-1').should eq 1
+      end
+      it '5件のときオフセット-2なら3になる' do
+        SheetPanel.offset(5, '-2').should eq 3
+      end
+      # 最終的なが負になるなど、不正な値が入ったときは0となる。 
+      it '2件のときオフセット-5なら0になる' do
+        SheetPanel.offset(2, '-5').should eq 0
+      end
+    end
+    context 'panel_count補正について' do
+      it '文字列から数値に変換される' do
+        SheetPanel.panel_count(100, '7').should eq 7
+      end
+      it 'nilの場合はSheetPanel.default_panel_sizeになる' do
+        SheetPanel.panel_count(100).should eq SheetPanel.default_panel_size
+      end
+      it '0以下の場合はSheetPanel.default_panel_sizeになる' do
+        SheetPanel.panel_count(100, '0').should eq SheetPanel.default_panel_size
+      end
+      it 'SheetPanel.max_panel_sizeを超えた場合はSheetPanel.max_panel_sizeになる' do
+        SheetPanel.panel_count(100, '1000').should eq SheetPanel.max_panel_size
+      end
+    end
+    it 'リストを返す' do
+      c = SheetPanel.play_list @sheet, @author
+      c.should eq [@sheet_panel]
+    end
+    it 't順で並んでいる' do
+      #公開用紙の公開コマは(他人の用紙であっても)含んでいる
+      v = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id, :t => 1
+      c = SheetPanel.play_list @sheet, @author
+      c.should eq [ @sheet_panel, v]
+    end
+    it '非公開のコマは含んでいる' do
+      h = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel2.id, :t => 1
+      c = SheetPanel.play_list @sheet, @author
+      c.should eq [ @sheet_panel, h]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id, :t => 1
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id, :t => 2
+        @sheet_panel4 = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id, :t => 3
+        @sheet_panel5 = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id, :t => 4
+      end
+      it 'offset=0なら末尾2件を返す' do
+        #時系列で並んでいる
+        c = SheetPanel.play_list( @sheet, @author, 0, 2)
+        c.should eq [@sheet_panel, @sheet_panel2]
+      end
+      it 'offset=2なら中間2件を返す' do
+        c = SheetPanel.play_list(@sheet, @author, 2, 2)
+        c.should eq [@sheet_panel3, @sheet_panel4]
+      end
+      it 'offset=4なら先頭1件を返す' do
+        c = SheetPanel.play_list(@sheet, @author, 4, 2)
+        c.should eq [@sheet_panel5]
+      end
+    end
+  end
+  
+  describe '一覧取得に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id, :visible => 1
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @sheet_panel = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id
+      @other_sheet = FactoryGirl.create :sheet, :author_id => @other_author.id, :visible => 1
+      @other_panel = FactoryGirl.create :panel, :author_id => @other_author.id, :publish => 1
+      @hidden_sheet = FactoryGirl.create :sheet, :author_id => @author.id, :visible => 0
+    end
+    context 'page補正について' do
+      it '文字列から数値に変換される' do
+        SheetPanel.page('8').should eq 8
+      end
+      it 'nilの場合は1になる' do
+        SheetPanel.page().should eq 1
+      end
+      it '0以下の場合は1になる' do
+        SheetPanel.page('0').should eq 1
+      end
+    end
+    context 'page_size補正について' do
+      it '文字列から数値に変換される' do
+        SheetPanel.page_size('7').should eq 7
+      end
+      it 'nilの場合はSheetPanel.default_page_sizeになる' do
+        SheetPanel.page_size().should eq SheetPanel.default_page_size
+      end
+      it '0以下の場合はSheetPanel.default_page_sizeになる' do
+        SheetPanel.page_size('0').should eq SheetPanel.default_page_size
+      end
+      it 'SheetPanel.max_page_sizeを超えた場合はSheetPanel.max_page_sizeになる' do
+        SheetPanel.page_size('1000').should eq SheetPanel.max_page_size
+      end
+    end
+    it 'リストを返す' do
+      c = SheetPanel.list
+      c.should eq [@sheet_panel]
+    end
+    it '時系列で並んでいる' do
+      #公開用紙のSheetPanelは(他人のSheetPanelであっても)含んでいる
+      v = FactoryGirl.create :sheet_panel, :author_id => @other_author.id, :sheet_id => @other_sheet.id, :panel_id => @other_panel.id, :t => 0, :updated_at => Time.now + 100
+      c = SheetPanel.list 
+      c.should eq [ v, @sheet_panel]
+    end
+    it '非公開の紙コマは(自分の紙コマであっても)含まない' do
+      h = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @hidden_sheet.id, :panel_id => @panel.id, :t => 0
+      c = SheetPanel.list 
+      c.should eq [ @sheet_panel]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id, :t => 1, :updated_at => Time.now + 100
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id, :t => 2, :updated_at => Time.now + 200
+        @sheet_panel4 = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id, :t => 3, :updated_at => Time.now + 300
+        @sheet_panel5 = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id, :t => 4, :updated_at => Time.now + 400
+      end
+      it '通常は2件を返す' do
+        l = SheetPanel.list 1, 2
+        l.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        l = SheetPanel.list 1, 2
+        l.should eq [@sheet_panel5, @sheet_panel4]
+      end
+      it 'page=2なら中間2件を返す' do
+        l = SheetPanel.list 2, 2
+        l.should eq [@sheet_panel3, @sheet_panel2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        l = SheetPanel.list 3, 2
+        l.should eq [@sheet_panel]
+      end
+    end
+  end
+  
+  describe '自分の紙コマ一覧取得に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id, :visible => 1
+      @panel = FactoryGirl.create :panel, :author_id => @author.id, :publish => 1
+      @sheet_panel = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id
+      @other_sheet = FactoryGirl.create :sheet, :author_id => @other_author.id, :visible => 1
+      @other_panel = FactoryGirl.create :panel, :author_id => @other_author.id, :publish => 1
+      @hsheet = FactoryGirl.create :sheet, :author_id => @author.id, :visible => 0
+      @hpanel = FactoryGirl.create :panel, :author_id => @author.id, :publish => 0
+    end
+    context 'つつがなく終わるとき' do
+      it '一覧取得オプションを利用している' do
+        SheetPanel.stub(:list_opt).with(any_args).and_return({})
+        SheetPanel.should_receive(:list_opt).with(any_args).exactly(1)
+        r = SheetPanel.mylist @author
+      end
+    end
+    it 'リストを返す' do
+      s = SheetPanel.mylist @author
+      s.should eq [@sheet_panel]
+    end
+    it '時系列で並んでいる' do
+      ns = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id, :t => 1, :updated_at => Time.now + 100
+      sl = SheetPanel.mylist @author
+      sl.should eq [ns, @sheet_panel]
+    end
+    it '他人の紙コマはコマ用紙ともに公開でも含まない' do
+      so = FactoryGirl.create :sheet_panel, :author_id => @other_author.id, :sheet_id => @other_sheet.id, :panel_id => @other_panel.id
+      sl = SheetPanel.mylist @author
+      sl.should eq [@sheet_panel]
+    end
+    it '自分の紙コマはコマ用紙ともに非公開でも含んでいる' do
+      hs = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @hsheet.id, :panel_id => @hpanel.id, :updated_at => Time.now + 100
+      sl = SheetPanel.mylist @author
+      sl.should eq [hs, @sheet_panel]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id, :t => 1, :updated_at => Time.now + 100
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id, :t => 2, :updated_at => Time.now + 200
+        @sheet_panel4 = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id, :t => 3, :updated_at => Time.now + 300
+        @sheet_panel5 = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id, :t => 4, :updated_at => Time.now + 400
+        SheetPanel.stub(:default_page_size).and_return(2)
+      end
+      it '通常は2件を返す' do
+        l = SheetPanel.mylist @author, 1, 2
+        l.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        l = SheetPanel.mylist @author, 1, 2
+        l.should eq [@sheet_panel5, @sheet_panel4]
+      end
+      it 'page=2なら中間2件を返す' do
+        l = SheetPanel.mylist @author, 2, 2
+        l.should eq [@sheet_panel3, @sheet_panel2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        l = SheetPanel.mylist @author, 3, 2
+        l.should eq [@sheet_panel]
+      end
+    end
+  end
+  
+  describe '他作家の紙コマ一覧取得に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id, :visible => 1
+      @panel = FactoryGirl.create :panel, :author_id => @author.id, :publish => 1
+      @sheet_panel = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id
+      @other_sheet = FactoryGirl.create :sheet, :author_id => @other_author.id, :visible => 1
+      @other_panel = FactoryGirl.create :panel, :author_id => @other_author.id, :publish => 1
+      @other_sheet_panel = FactoryGirl.create :sheet_panel, :author_id => @other_author.id, :sheet_id => @other_sheet.id, :panel_id => @other_panel.id
+    end
+    it 'リストを返す' do
+      r = SheetPanel.himlist @other_author
+      r.should eq [@other_sheet_panel]
+    end
+    it '時系列で並んでいる' do
+      ns = FactoryGirl.create :sheet_panel, :author_id => @other_author.id, :sheet_id => @other_sheet.id, :panel_id => @other_panel.id, :t => 1, :updated_at => Time.now + 100
+      r = SheetPanel.himlist @other_author
+      r.should eq [ns, @other_sheet_panel]
+    end
+    it '公開用紙の紙コマ' do
+      @hsheet = FactoryGirl.create :sheet, :author_id => @other_author.id, :visible => 0
+      ns = FactoryGirl.create :sheet_panel, :author_id => @other_author.id, :sheet_id => @hsheet.id, :panel_id => @other_panel.id, :t => 1, :updated_at => Time.now + 100
+      r = SheetPanel.himlist @other_author
+      r.should eq [@other_sheet_panel]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @other_sheet_panel2 = FactoryGirl.create :sheet_panel, :author_id => @other_author.id, :sheet_id => @other_sheet.id, :panel_id => @other_panel.id, :t => 1, :updated_at => Time.now + 100
+        @other_sheet_panel3 = FactoryGirl.create :sheet_panel, :author_id => @other_author.id, :sheet_id => @other_sheet.id, :panel_id => @other_panel.id, :t => 2, :updated_at => Time.now + 200
+        @other_sheet_panel4 = FactoryGirl.create :sheet_panel, :author_id => @other_author.id, :sheet_id => @other_sheet.id, :panel_id => @other_panel.id, :t => 3, :updated_at => Time.now + 300
+        @other_sheet_panel5 = FactoryGirl.create :sheet_panel, :author_id => @other_author.id, :sheet_id => @other_sheet.id, :panel_id => @other_panel.id, :t => 4, :updated_at => Time.now + 400
+        SheetPanel.stub(:default_page_size).and_return(2)
+      end
+      it '通常は2件を返す' do
+        l = SheetPanel.himlist @other_author, 1, 2
+        l.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        l = SheetPanel.himlist @other_author, 1, 2
+        l.should eq [@other_sheet_panel5, @other_sheet_panel4]
+      end
+      it 'page=2なら中間2件を返す' do
+        l = SheetPanel.himlist @other_author, 2, 2
+        l.should eq [@other_sheet_panel3, @other_sheet_panel2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        l = SheetPanel.himlist @other_author, 3, 2
+        l.should eq [@other_sheet_panel]
+      end
+    end
+  end
+  
+  describe '紙コマ一覧ページ制御に於いて' do
+    before do
+      SheetPanel.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = SheetPanel.list_paginate 
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it '紙コマ一覧の取得条件を利用している' do
+      SheetPanel.stub(:list_where).with(any_args).and_return('')
+      SheetPanel.should_receive(:list_where).with(any_args).exactly(1)
+      r = SheetPanel.list_paginate 
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = SheetPanel.list_paginate 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe '自分の紙コマ一覧ページ制御に於いて' do
+    before do
+      SheetPanel.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = SheetPanel.mylist_paginate @author
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it '自分の紙コマ一覧の取得条件を利用している' do
+      SheetPanel.stub(:mylist_where).with(any_args).and_return('')
+      SheetPanel.should_receive(:mylist_where).with(any_args).exactly(1)
+      r = SheetPanel.mylist_paginate @author
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = SheetPanel.mylist_paginate @author, 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe '他作家の紙コマ一覧ページ制御に於いて' do
+    before do
+      SheetPanel.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = SheetPanel.himlist_paginate @other_author
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it '他作家の紙コマ一覧の取得条件を利用している' do
+      SheetPanel.stub(:himlist_where).with(any_args).and_return('')
+      SheetPanel.should_receive(:himlist_where).with(any_args).exactly(1)
+      r = SheetPanel.himlist_paginate @other_author
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = SheetPanel.himlist_paginate @other_author, 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe 'list関連テーブルプションに於いて' do
+    it '3つの項目を含んでいる' do
+      r = SheetPanel.list_opt
+      r.should have(3).items
+    end
+    it '用紙を含んでいる' do
+      r = SheetPanel.list_opt
+      r.has_key?(:sheet).should be_true
+    end
+      it '用紙は作家を含んでいる' do
+        r = SheetPanel.list_opt
+        r[:sheet].has_key?(:author).should be_true
+      end
+    it '作家を含んでいる' do
+      r = SheetPanel.list_opt
+      r.has_key?(:author).should be_true
+    end
+    it 'コマを含んでいる' do
+      r = SheetPanel.list_opt
+      r.has_key?(:panel).should be_true
+    end
+      it 'コマは作家を含んでいる' do
+        r = SheetPanel.list_opt
+        r[:panel].has_key?(:author).should be_true
+      end
+      it 'コマはコマ絵を含んでいる' do
+        r = SheetPanel.list_opt
+        r[:panel].has_key?(:panel_pictures).should be_true
+      end
+        it 'コマ絵は実素材を含んでいる' do
+          r = SheetPanel.list_opt
+          r[:panel][:panel_pictures].has_key?(:picture).should be_true
+        end
+          it '実素材は絵師を含んでいる' do
+            r = SheetPanel.list_opt
+            r[:panel][:panel_pictures][:picture].has_key?(:artist).should be_true
+          end
+          it '実素材はライセンスを含んでいる' do
+            r = SheetPanel.list_opt
+            r[:panel][:panel_pictures][:picture].has_key?(:license).should be_true
+          end
+      it 'コマはフキダシを含んでいる' do
+        r = SheetPanel.list_opt
+        r[:panel].has_key?(:speech_balloons).should be_true
+      end
+        it 'フキダシはフキダシ枠を含んでいる' do
+          r = SheetPanel.list_opt
+          r[:panel][:speech_balloons].has_key?(:balloon).should be_true
+        end
+        it 'フキダシはセリフを含んでいる' do
+          r = SheetPanel.list_opt
+          r[:panel][:speech_balloons].has_key?(:speech).should be_true
+        end
+  end
+  describe 'json一覧出力オプションに於いて' do
+  end
+  
+  describe '単体取得に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @sheet_panel = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id
+    end
+    context 'つつがなく終わるとき' do
+      it '単体取得オプションを利用している' do
+        SheetPanel.stub(:show_opt).with(any_args).and_return({})
+        SheetPanel.should_receive(:show_opt).with(any_args).exactly(1)
+        r = SheetPanel.show @sheet_panel.id, @author
+      end
+      it '閲覧許可を問い合わせている' do
+        SheetPanel.any_instance.stub(:visible?).with(@author).and_return(true)
+        SheetPanel.any_instance.should_receive(:visible?).with(@author).exactly(1)
+        r = SheetPanel.show @sheet_panel.id, @author
+      end
+    end
+    it '指定の紙コマを返す' do
+      l = SheetPanel.show @sheet_panel.id, @author
+      l.should eq @sheet_panel
+    end
+    context '他人の紙コマを開こうとしたとき' do
+      it '403Forbidden例外を返す' do
+        SheetPanel.any_instance.stub(:visible?).with(@other_author).and_return(false)
+        lambda{
+          SheetPanel.show @sheet_panel.id, @other_author
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+    context '存在しない紙コマを開こうとしたとき' do
+      it '404RecordNotFound例外を返す' do
+        lambda{
+          SheetPanel.show 110, @author
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+    end
+  end
+  
+  describe '編集取得に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @sheet_panel = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id
+    end
+    context 'つつがなく終わるとき' do
+      it '単体取得オプションを利用している' do
+        SheetPanel.stub(:show_opt).with(any_args).and_return({})
+        SheetPanel.should_receive(:show_opt).with(any_args).exactly(1)
+        r = SheetPanel.edit @sheet_panel.id, @author
+      end
+      it '所持判定を問い合わせている' do
+        SheetPanel.any_instance.stub(:own?).with(any_args).and_return(true)
+        SheetPanel.any_instance.should_receive(:own?).with(any_args).exactly(1)
+        r = SheetPanel.edit @sheet_panel.id, @author
+      end
+    end
+    it '指定の紙コマを返す' do
+      l = SheetPanel.edit @sheet_panel.id, @author
+      l.should eq @sheet_panel
+    end
+    context '他人の紙コマを開こうとしたとき' do
+      it '403Forbidden例外を返す' do
+        SheetPanel.any_instance.stub(:own?).and_return(false)
+        lambda{
+          SheetPanel.edit @sheet_panel.id, @author
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+    context '存在しない紙コマを開こうとしたとき' do
+      it '404RecordNotFound例外を返す' do
+        lambda{
+          SheetPanel.edit 110, @author
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+    end
+  end
+  
+  describe '単体取得オプションに於いて' do
+    it 'includeキーを含んでいる' do
+      r = SheetPanel.show_opt
+      r.has_key?(:include).should be_true
+    end
+    it '3つの項目を含んでいる' do
+      r = SheetPanel.show_opt[:include]
+      r.should have(3).items
+    end
+    it '用紙を含んでいる' do
+      r = SheetPanel.show_opt[:include]
+      r.has_key?(:sheet).should be_true
+    end
+      it '用紙は作家を含んでいる' do
+        r = SheetPanel.show_opt[:include]
+        r[:sheet].has_key?(:author).should be_true
+      end
+    it '作家を含んでいる' do
+      r = SheetPanel.show_opt[:include]
+      r.has_key?(:author).should be_true
+    end
+    it 'コマを含んでいる' do
+      r = SheetPanel.show_opt[:include]
+      r.has_key?(:panel).should be_true
+    end
+      it 'コマは作家を含んでいる' do
+        r = SheetPanel.show_opt[:include]
+        r[:panel].has_key?(:author).should be_true
+      end
+      it 'コマはコマ絵を含んでいる' do
+        r = SheetPanel.show_opt[:include]
+        r[:panel].has_key?(:panel_pictures).should be_true
+      end
+        it 'コマ絵は実素材を含んでいる' do
+          r = SheetPanel.show_opt[:include]
+          r[:panel][:panel_pictures].has_key?(:picture).should be_true
+        end
+          it '実素材は絵師を含んでいる' do
+            r = SheetPanel.show_opt[:include]
+            r[:panel][:panel_pictures][:picture].has_key?(:artist).should be_true
+          end
+          it '実素材はライセンスを含んでいる' do
+            r = SheetPanel.show_opt[:include]
+            r[:panel][:panel_pictures][:picture].has_key?(:license).should be_true
+          end
+      it 'コマはフキダシを含んでいる' do
+        r = SheetPanel.show_opt[:include]
+        r[:panel].has_key?(:speech_balloons).should be_true
+      end
+        it 'フキダシはフキダシ枠を含んでいる' do
+          r = SheetPanel.show_opt[:include]
+          r[:panel][:speech_balloons].has_key?(:balloon).should be_true
+        end
+        it 'フキダシはセリフを含んでいる' do
+          r = SheetPanel.show_opt[:include]
+          r[:panel][:speech_balloons].has_key?(:speech).should be_true
+        end
+  end
+  describe 'json単体取得オプションに於いて' do
+  end
+  
+  describe '紙コマのjson出力に於いて' do
+    before do
+      #コマを作成しておく。
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @pp = FactoryGirl.create :panel_picture, :panel_id => @panel.id, :t => 1, :width => @p.width, :height => @p.height
+      @sb = FactoryGirl.build :speech_balloon, :panel_id => @panel.id, :speech_balloon_template_id => @speech_balloon_template.id
+      @speech = @sb.build_speech(
+        FactoryGirl.attributes_for(:speech, :writing_format_id => @writing_format.id)
+      )
+      @balloon = @sb.build_balloon(
+        FactoryGirl.attributes_for(:balloon, :system_picture_id => @sp.id)
+      )
+      @sb.boost
+      @sb.save!
+      @gc = @panel.ground_colors.create(
+        FactoryGirl.attributes_for(:ground_color, :panel_id => @panel.id)
+      )
+      @gp = @panel.ground_pictures.create(
+        FactoryGirl.attributes_for(:ground_picture, :panel_id => @panel.id, :picture_id => @p.id)
+      )
+      @panel.reload
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @sheet_panel = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id
+    end
+    context '事前チェックする' do
+      before do
+        Panel.any_instance.stub(:elements).and_return('{}')
+      end
+      it 'コマ要素のjson出力を依頼している' do
+        Panel.any_instance.stub(:visible?).with(any_args).and_return(true)
+        Panel.any_instance.should_receive(:elements).with(any_args).exactly(1)
+        r = @sheet_panel.sheet_panel_as_json @author
+      end
+    end
+    it 'json textを返している' do
+      r = JSON.parse @sheet_panel.sheet_panel_as_json(@author)
+      r.is_a?(Hash).should be_true
+    end
+    it 'sheet,author,panel,コマ要素を含んでいる' do
+      r = JSON.parse @sheet_panel.sheet_panel_as_json(@author)
+      r.has_key?('sheet').should be_true
+      r['sheet'].has_key?('author').should be_true
+      r.has_key?('author').should be_true
+      r.has_key?('panel').should be_true
+      r['panel'].has_key?('author').should be_true
+    end
+    context 'コマ閲覧許可のとき' do
+      before do
+        Panel.any_instance.stub(:visible?).with(any_args).and_return(true)
+      end
+      it 'コマ要素にコマ要素を追加している' do
+        r = JSON.parse @sheet_panel.sheet_panel_as_json(@author)
+        r['panel'].has_key?('elements').should be_true
+        r['panel']['elements'].should_not be_empty
+      end
+    end
+    context 'コマ閲覧不許可のとき' do
+      before do
+        Panel.any_instance.stub(:visible?).with(any_args).and_return(false)
+      end
+      it 'コマ要素にデータを含ませない' do
+        r = JSON.parse @sheet_panel.sheet_panel_as_json(@author)
+        r['panel'].has_key?('elements').should be_false
+      end
+    end
+  end
+  
+  describe '紙コマリストのjson出力に於いて' do
+    before do
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @sheet_panel = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id
+      SheetPanel.any_instance.stub(:sheet_panel_as_json).with(@author).and_return('{"s": 5}')
+    end
+    context 'つつがなく終わるとき' do
+      it '紙コマのjson出力を依頼している' do
+        SheetPanel.any_instance.should_receive(:sheet_panel_as_json).with(@author).exactly(1)
+        r = SheetPanel.list_as_json_text [@sheet_panel], @author
+      end
+    end
+    it 'json textを返している' do
+      r = SheetPanel.list_as_json_text [@sheet_panel], @author
+      j = JSON.parse r
+      j.is_a?(Array).should be_true
+    end
+    it '紙コマを含んでいる' do
+      r = SheetPanel.list_as_json_text [@sheet_panel], @author
+      j = JSON.parse r
+      j.first.has_key?('s').should be_true
+    end
+  end
+  
+  describe 'ライセンス素材に於いて' do
+    before do
+      #コマを作成しておく。
+      @op2 = FactoryGirl.create :original_picture, :artist_id => @artist.id
+      @p2 = FactoryGirl.create :picture, :original_picture_id => @op2.id, :license_id => @license.id, :artist_id => @artist.id
+      @rp2 = FactoryGirl.create :resource_picture, :artist_id => @artist.id, :license_id => @license.id, :original_picture_id => @op2.id, :picture_id => @p2.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @pp = FactoryGirl.create :panel_picture, :panel_id => @panel.id, :t => 0, :width => @p.width, :height => @p.height, :picture_id => @p.id
+      @panel.reload
+      @panel2 = FactoryGirl.create :panel, :author_id => @author.id
+      @pp2= FactoryGirl.create :panel_picture, :panel_id => @panel2.id, :t => 0, :width => @p2.width, :height => @p2.height, :picture_id => @p2.id
+      @panel2.reload
+      
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @sheet_panel = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id
+      @sheet_panel2 = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel2.id
+    end
+    context '事前チェック' do
+    end
+    context 'つつがなく終わるとき' do
+    end
+    it '連想配列(値は実素材、キーは実素材id)を返している' do
+      r = SheetPanel.licensed_pictures [@sheet_panel, @sheet_panel2]
+      r.is_a?(Hash).should be_true
+      r.should have(2).items
+      r[@pp.picture_id].should eq @p
+      r[@pp2.picture_id].should eq @p2
+    end
+    context 'コマが削除されているときき' do
+      before do
+        @panel2.destroy
+      end
+      it '削除されているコマは無視する' do
+        r = SheetPanel.licensed_pictures [@sheet_panel, @sheet_panel2]
+        r.is_a?(Hash).should be_true
+        r.should have(1).items
+        r[@pp.picture_id].should eq @p
+        r[@pp2.picture_id].should be_nil
+      end
+    end
+  end
+  
+  describe 't補充値に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+    end
+    
+    context '用紙初のコマなら' do
+      it '0を補充値とする' do
+        @sheet_panel = FactoryGirl.build :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id
+        @sheet_panel.t = nil
+        r = SheetPanel.new_t @sheet_panel.sheet_id
+        r.should eq 0
+      end
+    end
+    context '用紙に一個コマがあるとき' do
+      it '1を補充値とする' do
+        FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id, :t => 0
+        @sheet_panel = FactoryGirl.build :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id
+        @sheet_panel.t = nil
+        r = SheetPanel.new_t @sheet_panel.sheet_id
+        r.should eq 1
+      end
+    end
+    context '用紙に2個コマがあるとき' do
+      it '2を補充値とする' do
+        FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id, :t => 0
+        FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id, :t => 1
+        @sheet_panel = FactoryGirl.build :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id
+        @sheet_panel.t = nil
+        r = SheetPanel.new_t @sheet_panel.sheet_id
+        r.should eq 2
+      end
+    end
+  end
+  describe 'シリアライズチェックに於いて' do
+    context 'つつがなく終わるとき' do
+      it '0からシリアライズされているならTrueを返す' do
+        r = SheetPanel.serial? [0, 1, 2]
+        r.should be_true
+      end
+      it '見た目はシリアライズされてなくてもソート結果が無事ならtrueを返す' do
+        r = SheetPanel.serial? [0, 2, 1]
+        r.should be_true
+      end
+      it '見た目はシリアライズされてなくてもソート結果が無事ならtrueを返す' do
+        r = SheetPanel.serial? [ 2, 1, 4, 3, 0]
+        r.should be_true
+      end
+    end
+    context '異常なとき' do
+      it '0から始まらないならFalseを返す' do
+        r = SheetPanel.serial? [1, 2, 3]
+        r.should be_false
+      end
+      it '連続していないならFalseを返す' do
+        r = SheetPanel.serial? [0, 1, 2, 4]
+        r.should be_false
+      end
+      it '連続していないならFalseを返す' do
+        r = SheetPanel.serial? [0, 1, 2, 4, 5]
+        r.should be_false
+      end
+    end
+  end
+  describe 't収集に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+      @sheet2 = FactoryGirl.create :sheet, :author_id => @author.id
+      @c2sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet2.id, :panel_id => @panel.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '紙コマから同一用紙のtだけを収集している' do
+        r = SheetPanel.collect_t @sheet_panel
+        r.should eq [0]
+      end
+    end
+    context '複数コマのとき' do
+      it '紙コマから同一用紙のtだけを収集している' do
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        r = SheetPanel.collect_t @sheet_panel
+        r.sort.should eq [0, 1]
+      end
+    end
+    context '複数コマでヨソの用紙も混じっているとき' do
+      it '紙コマから同一用紙のtだけを収集している' do
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        r = SheetPanel.collect_t @sheet_panel
+        r.sort.should eq [0, 1]
+      end
+    end
+  end
+  describe 'tチェックに於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @sheet_panel = FactoryGirl.build :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it 't収集を依頼している' do
+        SheetPanel.should_receive(:collect_t).with(any_args).exactly(1)
+        SheetPanel.stub(:collect_t).with(any_args).and_return([])
+        SheetPanel.stub(:serial?).with(any_args).and_return(true)
+        r = SheetPanel.validate_t @sheet_panel
+      end
+      it '収集したtをシリアライズチェック依頼している' do
+        SheetPanel.stub(:collect_t).with(any_args).and_return([])
+        SheetPanel.should_receive(:serial?).with(any_args).exactly(1)
+        SheetPanel.stub(:serial?).with(any_args).and_return(true)
+        r = SheetPanel.validate_t @sheet_panel
+      end
+    end
+    #実データでチェック
+    #依頼チェックだけでは不安なので最低限のチェックを
+    context '新規のとき' do
+      it '一件だけで正常通過している' do
+        @sheet_panel = FactoryGirl.build :sheet_panel, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id, :t => 0
+        r = SheetPanel.validate_t @sheet_panel
+        r.should be_true 
+      end
+    end
+    context '既存のとき' do
+      it '2件目を作っても正常通過している' do
+        @sheet_panel = FactoryGirl.create :sheet_panel, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id, :t => 0
+        @sheet_panel2 = FactoryGirl.build :sheet_panel, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id, :t => 1
+        r = SheetPanel.validate_t @sheet_panel2
+        r.should be_true 
+      end
+    end
+  end
+  describe '挿入シフトに於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+    end
+    context '依頼チェック' do
+      #テーブルが空で0に挿入
+      it 'Updateを依頼している' do
+        SheetPanel.stub(:update_all).with(any_args)
+        SheetPanel.should_receive(:update_all).with(any_args).exactly(1)
+        @sheet_panel = FactoryGirl.build :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel.insert_shift
+      end
+    end
+    context 'テーブルに1件(t:0)で0に挿入したとき' do
+      before do
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.build :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '既存の行を1にシフトしている' do
+        @sheet_panel2.insert_shift
+        l = SheetPanel.find :all
+        l.first.t.should eq 1
+      end
+      it 'これから挿入するt(0)が欠番になっている' do
+        @sheet_panel2.insert_shift
+        l = SheetPanel.find(:all).map {|s| s.t }
+        l.include?(0).should_not be_true
+      end
+    end
+    context 'テーブルに2件(t:0,1)で1に挿入したとき' do
+      before do
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel3 = FactoryGirl.build :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '既存のt1を2にシフトしてこれから挿入するt(1)が欠番になっている' do
+        @sheet_panel3.insert_shift
+        l = SheetPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 2]
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)で2に挿入したとき' do
+      before do
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel4 = FactoryGirl.create :sheet_panel, :t => 3, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel5 = FactoryGirl.create :sheet_panel, :t => 4, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel6 = FactoryGirl.build :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '既存のt1を2にシフトしてこれから挿入するt(1)が欠番になっている' do
+        @sheet_panel6.insert_shift
+        l = SheetPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 1, 3, 4, 5]
+      end
+    end
+    context '先ほどのケース+他の用紙1件で挿入したとき' do
+      before do
+        @sheet2 = FactoryGirl.create :sheet, :author_id => @author.id
+        @sheet_panelc2 = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet2.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel4 = FactoryGirl.create :sheet_panel, :t => 3, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel5 = FactoryGirl.create :sheet_panel, :t => 4, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel6 = FactoryGirl.build :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '既存のt1を2にシフトしてこれから挿入するt(1)が欠番になっている' do
+        @sheet_panel6.insert_shift
+        l = SheetPanel.find(:all, :conditions => ['sheet_id = ?', @sheet.id]).map {|s| s.t }
+        l.sort.should eq [0, 1, 3, 4, 5]
+      end
+      it '他の用紙に影響がない' do
+        ot = @sheet_panelc2.t
+        @sheet_panel6.insert_shift
+        @sheet_panelc2.reload
+        @sheet_panelc2.t.should eq ot
+      end
+    end
+  end
+  describe '少ない方に移動に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+    end
+    context '依頼チェック' do
+      it 'Updateを依頼している' do
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        SheetPanel.stub(:update_all).with(any_args)
+        SheetPanel.should_receive(:update_all).with(any_args).exactly(1)
+        ot = @sheet_panel2.t
+        @sheet_panel2.t = 0
+        @sheet_panel2.lesser_shift ot
+      end
+    end
+    context 'テーブルに2件(t:0,1)で1を0に移動したとき' do
+      before do
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @sheet_panel2.t
+        @sheet_panel2.t = 0
+      end
+      it '既存のt0を1にシフトしてこれから挿入するt(0)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @sheet_panel2.lesser_shift @ot
+        l = SheetPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [1, 1]
+      end
+      it '既存のt0を1にシフトしている' do
+        @sheet_panel2.lesser_shift @ot
+        @sheet_panel.reload
+        @sheet_panel.t.should eq 1
+      end
+    end
+    context 'テーブルに3件(t:0,1,2)で2を1に移動したとき' do
+      before do
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @sheet_panel3.t
+        @sheet_panel3.t = 1
+      end
+      it '既存のt1を2にシフトしてこれから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @sheet_panel3.lesser_shift @ot
+        l = SheetPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 2, 2]
+      end
+      it '既存のt1を2にシフトしている' do
+        @sheet_panel3.lesser_shift @ot
+        @sheet_panel2.reload
+        @sheet_panel2.t.should eq 2
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)で3を1に移動したとき' do
+      before do
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel4 = FactoryGirl.create :sheet_panel, :t => 3, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel5 = FactoryGirl.create :sheet_panel, :t => 4, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @sheet_panel4.t
+        @sheet_panel4.t = 1
+      end
+      it 'これから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @sheet_panel4.lesser_shift @ot
+        l = SheetPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 2, 3, 3, 4]
+      end
+      it '既存のt0には変化がない' do
+        @sheet_panel4.lesser_shift @ot
+        @sheet_panel.reload
+        @sheet_panel.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @sheet_panel4.lesser_shift @ot
+        @sheet_panel5.reload
+        @sheet_panel5.t.should eq 4
+      end
+      it '既存のt1を2にシフトしている' do
+        @sheet_panel4.lesser_shift @ot
+        @sheet_panel2.reload
+        @sheet_panel2.t.should eq 2
+      end
+      it '既存のt2を3にシフトしている' do
+        @sheet_panel4.lesser_shift @ot
+        @sheet_panel3.reload
+        @sheet_panel3.t.should eq 3
+      end
+    end
+    context '先ほどのケース+他の用紙1件で挿入したとき' do
+      before do
+        @sheet2 = FactoryGirl.create :sheet, :author_id => @author.id
+        @sheet_panelc2 = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet2.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel4 = FactoryGirl.create :sheet_panel, :t => 3, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel5 = FactoryGirl.create :sheet_panel, :t => 4, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @sheet_panel4.t
+        @sheet_panel4.t = 1
+      end
+      it 'これから挿入するt(1)が欠番になっている' do
+        @sheet_panel4.lesser_shift @ot
+        l = SheetPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0, 2, 3, 3, 4]
+      end
+      it '既存のt0には変化がない' do
+        @sheet_panel4.lesser_shift @ot
+        @sheet_panel.reload
+        @sheet_panel.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @sheet_panel4.lesser_shift @ot
+        @sheet_panel5.reload
+        @sheet_panel5.t.should eq 4
+      end
+      it '既存のt1を2にシフトしている' do
+        @sheet_panel4.lesser_shift @ot
+        @sheet_panel2.reload
+        @sheet_panel2.t.should eq 2
+      end
+      it '既存のt2を3にシフトしている' do
+        @sheet_panel4.lesser_shift @ot
+        @sheet_panel3.reload
+        @sheet_panel3.t.should eq 3
+      end
+      it '他の用紙に影響がない' do
+        @sheet_panel4.lesser_shift @ot
+        @sheet_panelc2.reload
+        @sheet_panelc2.t.should eq 0
+      end
+    end
+    #例外ケース。
+    #負のときは0として正常扱い
+    context 'テーブルに2件(t:0,1)で1を-1に移動したとき' do
+      before do
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @sheet_panel2.t
+        @sheet_panel2.t = -1
+      end
+      it '既存のt0を1にシフトしてこれから挿入するt(0)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @sheet_panel2.lesser_shift @ot
+        l = SheetPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [1, 1]
+      end
+      it '既存のt0を1にシフトしている' do
+        @sheet_panel2.lesser_shift @ot
+        @sheet_panel.reload
+        @sheet_panel.t.should eq 1
+      end
+      it '既存のt1は0に補正されている' do
+        @sheet_panel2.lesser_shift @ot
+        @sheet_panel2.t.should eq 0
+      end
+    end
+  end
+  describe '大きい方に移動に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+    end
+    context '依頼チェック' do
+      it 'Updateを依頼している' do
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        SheetPanel.stub(:update_all).with(any_args)
+        SheetPanel.should_receive(:update_all).with(any_args).exactly(1)
+        ot = @sheet_panel.t
+        @sheet_panel.t = 1
+        @sheet_panel.higher_shift ot
+      end
+    end
+    context 'テーブルに2件(t:0,1)で0を1に移動したとき' do
+      before do
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @sheet_panel.t
+        @sheet_panel.t = 1
+      end
+      it '既存のt1を0にシフトしてこれから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @sheet_panel.higher_shift @ot
+        l = SheetPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0]
+      end
+      it '既存のt1を0にシフトしている' do
+        @sheet_panel.higher_shift @ot
+        @sheet_panel2.reload
+        @sheet_panel2.t.should eq 0
+      end
+    end
+    context 'テーブルに3件(t:0,1,2)で0を1に移動したとき' do
+      before do
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @sheet_panel.t
+        @sheet_panel.t = 1
+      end
+      it '既存のt1を0にシフトしてこれから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @sheet_panel.higher_shift @ot
+        l = SheetPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0, 2]
+      end
+      it '既存のt1を0にシフトしている' do
+        @sheet_panel.higher_shift @ot
+        @sheet_panel2.reload
+        @sheet_panel2.t.should eq 0
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)で1を3に移動したとき' do
+      before do
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel4 = FactoryGirl.create :sheet_panel, :t => 3, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel5 = FactoryGirl.create :sheet_panel, :t => 4, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @sheet_panel2.t
+        @sheet_panel2.t = 3
+      end
+      it 'これから挿入するt(3)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @sheet_panel2.higher_shift @ot
+        l = SheetPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 1, 1, 2, 4]
+      end
+      it '既存のt0には変化がない' do
+        @sheet_panel2.higher_shift @ot
+        @sheet_panel.reload
+        @sheet_panel.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @sheet_panel2.higher_shift @ot
+        @sheet_panel5.reload
+        @sheet_panel5.t.should eq 4
+      end
+      it '既存のt2を1にシフトしている' do
+        @sheet_panel2.higher_shift @ot
+        @sheet_panel3.reload
+        @sheet_panel3.t.should eq 1
+      end
+      it '既存のt3を2にシフトしている' do
+        @sheet_panel2.higher_shift @ot
+        @sheet_panel4.reload
+        @sheet_panel4.t.should eq 2
+      end
+    end
+    context '先ほどのケース+他の用紙1件で挿入したとき' do
+      before do
+        @sheet2 = FactoryGirl.create :sheet, :author_id => @author.id
+        @sheet_panelc2 = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet2.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel4 = FactoryGirl.create :sheet_panel, :t => 3, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel5 = FactoryGirl.create :sheet_panel, :t => 4, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @sheet_panel2.t
+        @sheet_panel2.t = 3
+      end
+      it 'これから挿入するt(3)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @sheet_panel2.higher_shift @ot
+        l = SheetPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0, 1, 1, 2, 4]
+      end
+      it '既存のt0には変化がない' do
+        @sheet_panel2.higher_shift @ot
+        @sheet_panel.reload
+        @sheet_panel.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @sheet_panel2.higher_shift @ot
+        @sheet_panel5.reload
+        @sheet_panel5.t.should eq 4
+      end
+      it '既存のt2を1にシフトしている' do
+        @sheet_panel2.higher_shift @ot
+        @sheet_panel3.reload
+        @sheet_panel3.t.should eq 1
+      end
+      it '既存のt3を2にシフトしている' do
+        @sheet_panel2.higher_shift @ot
+        @sheet_panel4.reload
+        @sheet_panel4.t.should eq 2
+      end
+      it '他の用紙に影響がない' do
+        @sheet_panel2.higher_shift @ot
+        @sheet_panelc2.reload
+        @sheet_panelc2.t.should eq 0
+      end
+    end
+    #例外ケース。
+    #max超えたときはmaxとして正常扱い
+    context 'テーブルに2件(t:0,1)で0を2に移動したとき' do
+      before do
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @sheet_panel.t
+        @sheet_panel.t = 2
+      end
+      it '既存のt1を0にシフトしてこれから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @sheet_panel.higher_shift @ot
+        l = SheetPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0]
+      end
+      it '既存のt1を0にシフトしている' do
+        @sheet_panel.higher_shift @ot
+        @sheet_panel2.reload
+        @sheet_panel2.t.should eq 0
+      end
+      it '既存のt0は1に補正されている' do
+        @sheet_panel.higher_shift @ot
+        @sheet_panel.t.should eq 1
+      end
+    end
+  end
+  describe '入れ替えに於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+      @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+    end
+    context '新tが旧tより小さいとき' do
+      it '少ない方に移動を依頼している' do
+        SheetPanel.any_instance.stub(:lesser_shift).with(any_args)
+        SheetPanel.any_instance.should_receive(:lesser_shift).with(any_args).exactly(1)
+        ot = @sheet_panel2.t
+        @sheet_panel2.t = 0
+        @sheet_panel2.update_shift ot
+      end
+    end
+    context '新tが旧tより大きいとき' do
+      it '大きい方に移動を依頼している' do
+        SheetPanel.any_instance.stub(:higher_shift).with(any_args)
+        SheetPanel.any_instance.should_receive(:higher_shift).with(any_args).exactly(1)
+        ot = @sheet_panel.t
+        @sheet_panel.t = 1
+        @sheet_panel.update_shift ot
+      end
+    end
+  end
+  describe '順序入れ替えに於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+    end
+    context 'オブジェクトが新規でtが空のとき' do
+      it '末尾追加としてtを補充依頼している' do
+        @sheet_panel = FactoryGirl.build :sheet_panel, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        SheetPanel.stub(:new_t).with(any_args).and_return(0)
+        SheetPanel.should_receive(:new_t).with(any_args).exactly(1)
+        @sheet_panel.t = nil
+        r = @sheet_panel.rotate
+      end
+    end
+    context 'オブジェクトが新規でtが設定されているとき' do
+      it '挿入追加として挿入シフトを依頼している' do
+        @sheet_panel = FactoryGirl.build :sheet_panel, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        SheetPanel.any_instance.stub(:insert_shift).with(any_args)
+        SheetPanel.any_instance.should_receive(:insert_shift).with(any_args).exactly(1)
+        @sheet_panel.t = 0
+        r = @sheet_panel.rotate
+      end
+    end
+    context 'オブジェクトが新規でなくtが設定されているとき' do
+      it 'コマ移動として入れ替えを依頼している' do
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        SheetPanel.any_instance.stub(:update_shift).with(any_args)
+        SheetPanel.any_instance.should_receive(:update_shift).with(1).exactly(1)
+        @sheet_panel2.t = 0
+        r = @sheet_panel.rotate 1
+      end
+    end
+    context 'オブジェクトが新規でなくtが空のとき' do
+      it '入れ替えもシフトもせず、tを空のままにしている' do
+        #結果、tに欠番が生じてシリアライズチェックでひっかかる
+      end
+    end
+  end
+  describe '編集許可に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @sheet_panel = FactoryGirl.build :sheet_panel, :t => nil, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it 'trueを返す' do
+        r = @sheet_panel.allow?
+        r.should be_true
+      end
+    end
+    context '用紙で引っかかるとき' do
+      it 'falseを返す' do
+        Panel.any_instance.stub(:usable?).with(any_args).and_return(true)
+        Sheet.any_instance.stub(:own?).with(any_args).and_return(false)
+        r = @sheet_panel.allow?
+        r.should be_false
+      end
+    end
+    context 'コマで引っかかるとき' do
+      it 'falseを返す' do
+        Sheet.any_instance.stub(:own?).with(any_args).and_return(true)
+        Panel.any_instance.stub(:usable?).with(any_args).and_return(false)
+        r = @sheet_panel.allow?
+        r.should be_false
+      end
+    end
+    context '用紙またはコマが指定されていなかったとき' do
+      it 'nilを返す' do
+        Sheet.any_instance.stub(:own?).with(any_args).and_return(true)
+        @sheet_panel.panel_id = nil
+        r = @sheet_panel.allow?
+        r.should eq nil
+      end
+      it 'nilを返す' do
+        Panel.any_instance.stub(:usable?).with(any_args).and_return(true)
+        @sheet_panel.sheet_id = nil
+        r = @sheet_panel.allow?
+        r.should eq nil
+      end
+    end
+  end
+  describe '保存に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @sheet_panel = FactoryGirl.build :sheet_panel, :t => nil, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '編集許可チェックを依頼している' do
+        SheetPanel.any_instance.stub(:allow?).with(any_args).and_return(true)
+        SheetPanel.any_instance.should_receive(:allow?).with(any_args).exactly(1)
+        r = @sheet_panel.store
+      end
+      it '順序入れ替えを依頼している' do
+        SheetPanel.any_instance.stub(:rotate).with(any_args).and_return(0)
+        SheetPanel.any_instance.should_receive(:rotate).with(any_args).exactly(1)
+        SheetPanel.any_instance.stub(:save).with(any_args).and_return(true)
+        SheetPanel.stub(:validate_t).with(any_args).and_return(true)
+        r = @sheet_panel.store 
+      end
+      it '保存を依頼している' do
+        SheetPanel.stub(:new_t).with(any_args).and_return(0)
+        SheetPanel.any_instance.stub(:save).with(any_args).and_return(true)
+        SheetPanel.any_instance.should_receive(:save).with(any_args).exactly(1)
+        SheetPanel.stub(:validate_t).with(any_args).and_return(true)
+        r = @sheet_panel.store
+      end
+      it 'tのシリアライズチェックを依頼している' do
+        SheetPanel.stub(:new_t).with(any_args).and_return(0)
+        SheetPanel.any_instance.stub(:save).with(any_args).and_return(true)
+        SheetPanel.stub(:validate_t).with(any_args).and_return(true)
+        SheetPanel.should_receive(:validate_t).with(any_args).exactly(1)
+        r = @sheet_panel.store
+      end
+    end
+    #入れ替えテストと同じテストを実施。こちらはシフトだけでなく本尊も更新されている
+    context 'テーブルに5件(t:0,1,2,3,4)+他の用紙1件で2に挿入したとき' do
+      before do
+        @sheet2 = FactoryGirl.create :sheet, :author_id => @author.id
+        @sheet_panelc2 = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet2.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel4 = FactoryGirl.create :sheet_panel, :t => 3, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel5 = FactoryGirl.create :sheet_panel, :t => 4, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel6 = FactoryGirl.build :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '既存のt0には変化がない' do
+        @sheet_panel6.store
+        @sheet_panel.reload
+        @sheet_panel.t.should eq 0
+      end
+      it '既存のt1には変化がない' do
+        @sheet_panel6.store
+        @sheet_panel2.reload
+        @sheet_panel2.t.should eq 1
+      end
+      it '既存のt2を3にシフトしている' do
+        @sheet_panel6.store
+        @sheet_panel3.reload
+        @sheet_panel3.t.should eq 3
+      end
+      it '既存のt3を4にシフトしている' do
+        @sheet_panel6.store
+        @sheet_panel4.reload
+        @sheet_panel4.t.should eq 4
+      end
+      it '既存のt5を5にシフトしている' do
+        @sheet_panel6.store
+        @sheet_panel5.reload
+        @sheet_panel5.t.should eq 5
+      end
+      it '新規のt2が作成されている' do
+        @sheet_panel6.store
+        @sheet_panel6.reload
+        @sheet_panel6.t.should eq 2
+      end
+      it '他の用紙に影響がない' do
+        @ot = @sheet_panelc2.t
+        @sheet_panel6.store
+        @sheet_panelc2.reload
+        @sheet_panelc2.t.should eq @ot
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)+他の用紙1件で3を1に移動したとき' do
+      before do
+        @sheet2 = FactoryGirl.create :sheet, :author_id => @author.id
+        @sheet_panelc2 = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet2.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel4 = FactoryGirl.create :sheet_panel, :t => 3, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel5 = FactoryGirl.create :sheet_panel, :t => 4, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @sheet_panel4.t
+        @sheet_panel4.t = 1
+      end
+      it '既存のt0には変化がない' do
+        @sheet_panel4.store @ot
+        @sheet_panel.reload
+        @sheet_panel.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @sheet_panel4.store @ot
+        @sheet_panel5.reload
+        @sheet_panel5.t.should eq 4
+      end
+      it '既存のt1を2にシフトしている' do
+        @sheet_panel4.store @ot
+        @sheet_panel2.reload
+        @sheet_panel2.t.should eq 2
+      end
+      it '既存のt2を3にシフトしている' do
+        @sheet_panel4.store @ot
+        @sheet_panel3.reload
+        @sheet_panel3.t.should eq 3
+      end
+      it '既存のt3を1にシフトしている' do
+        @sheet_panel4.store @ot
+        @sheet_panel4.reload
+        @sheet_panel4.t.should eq 1
+      end
+      it '他の用紙に影響がない' do
+        @sheet_panel4.store @ot
+        @sheet_panelc2.reload
+        @sheet_panelc2.t.should eq 0
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)+他の用紙1件で1を3に移動したとき' do
+      before do
+        @sheet2 = FactoryGirl.create :sheet, :author_id => @author.id
+        @sheet_panelc2 = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet2.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel4 = FactoryGirl.create :sheet_panel, :t => 3, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel5 = FactoryGirl.create :sheet_panel, :t => 4, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @sheet_panel2.t
+        @sheet_panel2.t = 3
+      end
+      it '既存のt0には変化がない' do
+        @sheet_panel2.store @ot
+        @sheet_panel.reload
+        @sheet_panel.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @sheet_panel2.store @ot
+        @sheet_panel5.reload
+        @sheet_panel5.t.should eq 4
+      end
+      it '既存のt1を3にシフトしている' do
+        @sheet_panel2.store @ot
+        @sheet_panel2.reload
+        @sheet_panel2.t.should eq 3
+      end
+      it '既存のt2を1にシフトしている' do
+        @sheet_panel2.store @ot
+        @sheet_panel3.reload
+        @sheet_panel3.t.should eq 1
+      end
+      it '既存のt3を2にシフトしている' do
+        @sheet_panel2.store @ot
+        @sheet_panel4.reload
+        @sheet_panel4.t.should eq 2
+      end
+      it '他の用紙に影響がない' do
+        @sheet_panel2.store @ot
+        @sheet_panelc2.reload
+        @sheet_panelc2.t.should eq 0
+      end
+    end
+    #ロールバックテスト。入れ替えが直接DBをいじるので、すべてのケースで確実にロールバックを確認する
+    context 'テーブルに5件(t:0,1,2,3,4)+他の用紙1件で2に挿入したが保存に失敗したとき' do
+      before do
+        SheetPanel.any_instance.stub(:save).with(any_args).and_return(false)
+        @sheet2 = FactoryGirl.create :sheet, :author_id => @author.id
+        @sheet_panelc2 = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet2.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel4 = FactoryGirl.create :sheet_panel, :t => 3, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel5 = FactoryGirl.create :sheet_panel, :t => 4, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel6 = FactoryGirl.build :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '既存のtに変化がない' do
+        @sheet_panel6.store
+        @sheet_panel.reload
+        @sheet_panel.t.should eq 0
+        @sheet_panel2.reload
+        @sheet_panel2.t.should eq 1
+        @sheet_panel3.reload
+        @sheet_panel3.t.should eq 2
+        @sheet_panel4.reload
+        @sheet_panel4.t.should eq 3
+        @sheet_panel5.reload
+        @sheet_panel5.t.should eq 4
+        @sheet_panelc2.reload
+        @sheet_panelc2.t.should eq 0
+      end
+      it 'falseを返す' do
+        r = @sheet_panel6.store
+        r.should be_false
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)+他の用紙1件で3を1に移動したがシリアルチェックに失敗したとき' do
+      before do
+        SheetPanel.stub(:validate_t).with(any_args).and_return(false)
+        @sheet2 = FactoryGirl.create :sheet, :author_id => @author.id
+        @sheet_panelc2 = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet2.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel4 = FactoryGirl.create :sheet_panel, :t => 3, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel5 = FactoryGirl.create :sheet_panel, :t => 4, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @sheet_panel4.t
+        @sheet_panel4.t = 1
+      end
+      it '既存のtに変化がない' do
+        @sheet_panel4.store @ot
+        @sheet_panel.reload
+        @sheet_panel.t.should eq 0
+        @sheet_panel2.reload
+        @sheet_panel2.t.should eq 1
+        @sheet_panel3.reload
+        @sheet_panel3.t.should eq 2
+        @sheet_panel4.reload
+        @sheet_panel4.t.should eq 3
+        @sheet_panel5.reload
+        @sheet_panel5.t.should eq 4
+        @sheet_panelc2.reload
+        @sheet_panelc2.t.should eq 0
+      end
+      it 'falseを返す' do
+        r = @sheet_panel4.store @ot
+        r.should be_false
+      end
+      it 'tにエラーメッセージが入っている' do
+        @sheet_panel4.store @ot
+        @sheet_panel4.errors[:t].should_not be_empty
+        @sheet_panel4.valid?.should be_true
+      end
+    end
+    context '編集不可だったとき' do
+      before do
+        @sheet_panel = FactoryGirl.build :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        SheetPanel.any_instance.stub(:allow?).and_return(false)
+      end
+      it '403Forbidden例外を返す' do
+        lambda{
+          @sheet_panel.store
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+  end
+  describe '切り詰め処理つき削除に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @sheet_panel = FactoryGirl.create :sheet_panel, :t => 0, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '削除される' do
+        lambda{
+          @sheet_panel.destroy_and_shorten
+        }.should change(SheetPanel, :count ).by(-1)
+      end
+      it 'Trueを返す' do
+        r = @sheet_panel.destroy_and_shorten
+        r.should be_true 
+      end
+    end
+    context '削除に失敗したとき' do
+      before do
+        SheetPanel.any_instance.stub(:destroy).and_return(false)
+      end
+      it 'ロールバックされる' do
+        lambda{
+          @sheet_panel.destroy_and_shorten
+        }.should_not change(SheetPanel, :count )
+      end
+      it 'Falseを返す' do
+        r = @sheet_panel.destroy_and_shorten
+        r.should be_false
+      end
+    end
+    #連携テスト。切り詰めが直接DBをいじる
+    context '2件で先頭を削除したとき' do
+      before do
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '行が削除される' do
+        lambda{
+          @sheet_panel.destroy_and_shorten
+        }.should change(SheetPanel, :count ).by(-1)
+      end
+      it '先頭は削除される' do
+        @sheet_panel.destroy_and_shorten
+        lambda{
+          SheetPanel.find @sheet_panel.id
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+      it '2件目は前に詰められる' do
+        @sheet_panel.destroy_and_shorten
+        @sheet_panel2.reload
+        @sheet_panel2.t.should eq 0
+      end
+    end
+    context '3件で先頭を削除したとき' do
+      before do
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '行が削除される' do
+        lambda{
+          @sheet_panel.destroy_and_shorten
+        }.should change(SheetPanel, :count ).by(-1)
+      end
+      it '先頭は削除される' do
+        @sheet_panel.destroy_and_shorten
+        lambda{
+          SheetPanel.find @sheet_panel.id
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+      it '2件目は前に詰められる' do
+        @sheet_panel.destroy_and_shorten
+        @sheet_panel2.reload
+        @sheet_panel2.t.should eq 0
+      end
+      it '3件目は前に詰められる' do
+        @sheet_panel.destroy_and_shorten
+        @sheet_panel3.reload
+        @sheet_panel3.t.should eq 1
+      end
+    end
+    context '5件で3件目を削除したとき' do
+      before do
+        @sheet_panel2 = FactoryGirl.create :sheet_panel, :t => 1, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel3 = FactoryGirl.create :sheet_panel, :t => 2, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel4 = FactoryGirl.create :sheet_panel, :t => 3, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+        @sheet_panel5 = FactoryGirl.create :sheet_panel, :t => 4, :sheet_id => @sheet.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '行が削除される' do
+        lambda{
+          @sheet_panel3.destroy_and_shorten
+        }.should change(SheetPanel, :count ).by(-1)
+      end
+      it '1件目は変化がない' do
+        @sheet_panel3.destroy_and_shorten
+        @sheet_panel.reload
+        @sheet_panel.t.should eq 0
+      end
+      it '2件目は変化がない' do
+        @sheet_panel3.destroy_and_shorten
+        @sheet_panel2.reload
+        @sheet_panel2.t.should eq 1
+      end
+      it '3件目は削除される' do
+        @sheet_panel3.destroy_and_shorten
+        lambda{
+          SheetPanel.find @sheet_panel3.id
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+      it '4件目は前に詰められる' do
+        @sheet_panel3.destroy_and_shorten
+        @sheet_panel4.reload
+        @sheet_panel4.t.should eq 2
+      end
+      it '5件目は前に詰められる' do
+        @sheet_panel3.destroy_and_shorten
+        @sheet_panel5.reload
+        @sheet_panel5.t.should eq 3
+      end
+    end
+    #ロールバックテスト。切り詰めが直接DBをいじるので、すべてのケースで確実にロールバックを確認する
+  end
+end
diff --git a/spec/models/sheet_spec.rb b/spec/models/sheet_spec.rb
new file mode 100644 (file)
index 0000000..2076823
--- /dev/null
@@ -0,0 +1,675 @@
+# -*- encoding: utf-8 -*-
+require 'spec_helper'
+#用紙
+
+describe Sheet do
+  before do
+    SpeechBalloonTemplate.delete_all
+    @admin = FactoryGirl.create :admin
+    @demand_user = FactoryGirl.create :demand_user
+    @sp = FactoryGirl.create :system_picture
+    @lg = FactoryGirl.create :license_group
+    @license = FactoryGirl.create :license, :license_group_id => @lg.id, :system_picture_id => @sp.id
+    @user = FactoryGirl.create( :user_yas)
+    @author = FactoryGirl.create :author, :user_id => @user.id
+    @artist = FactoryGirl.create :artist_yas, :author_id => @author.id
+    @other_user = FactoryGirl.create( :user_yas)
+    @other_author = FactoryGirl.create :author, :user_id => @other_user.id
+    @other_artist = FactoryGirl.create :artist_yas, :author_id => @other_author.id
+  end
+  
+  describe '検証に於いて' do
+    before do
+      @sheet = FactoryGirl.build :sheet, :author_id => @author.id
+    end
+    
+    context 'オーソドックスなデータのとき' do
+      it '下限データが通る' do
+        @sheet.caption = 'a'
+        @sheet.visible = 0
+        @sheet.should be_valid
+      end
+      it '上限データが通る' do
+        @sheet.caption = 'a'*100
+        @sheet.visible = 1
+        @sheet.should be_valid
+      end
+    end
+    
+    context 'captionを検証するとき' do
+      it 'nullなら失敗する' do
+        @sheet.caption = nil
+        @sheet.should_not be_valid
+      end
+      it '100文字以上なら失敗する' do
+        @sheet.caption = 'a'*101
+        @sheet.should_not be_valid
+      end
+    end
+    context 'visibleを検証するとき' do
+      it 'nullなら失敗する' do
+        @sheet.visible = nil
+        @sheet.should_not be_valid
+      end
+      it '負なら失敗する' do
+        @sheet.visible = -1
+        @sheet.should_not be_valid
+      end
+      it '2以上なら失敗する' do
+        @sheet.visible = 2
+        @sheet.should_not be_valid
+      end
+    end
+  end
+  
+  describe '文字コード検証に於いて' do
+    before do
+      @sheet = FactoryGirl.build :sheet, :author_id => @author.id
+    end
+    
+    context 'captionを検証するとき' do
+      it 'Shift JISなら失敗する' do
+        @sheet.caption = "\x83G\x83r\x83]\x83D"
+        lambda{
+          @sheet.valid_encode
+        }.should raise_error(Pettanr::BadRequest)
+      end
+    end
+    
+  end
+  
+  describe 'デフォルト値補充に於いて' do
+    it 'visibleが0になっている' do
+      @sheet = FactoryGirl.build :sheet, :visible => nil
+      @sheet.supply_default
+      @sheet.visible.should eq 0
+    end
+  end
+  
+  describe '上書き補充に於いて' do
+    it '作家idが設定されている' do
+      @sheet = FactoryGirl.build :sheet, :author_id => nil
+      @sheet.overwrite @author
+      @sheet.author_id.should eq @author.id
+    end
+  end
+  
+  describe '所持判定に於いて' do
+    before do
+      @sheet = FactoryGirl.build :sheet, :author_id => @author.id
+    end
+    context '事前チェックする' do
+      it '自身にロールリストからの作家取得を依頼している' do
+        Sheet.should_receive(:get_author_from_roles).with(any_args).exactly(1)
+        r = @sheet.own?([@author])
+      end
+    end
+    context 'ロール内作家が取得できるとき' do
+      before do
+      end
+      it 'ロール内作家のidが自身の作家idと一致するなら許可する' do
+        Sheet.stub(:get_author_from_roles).with(any_args).and_return(@author)
+        r = @sheet.own?([@author])
+        r.should be_true
+      end
+      it 'ロール内作家のidが自身の作家idと一致しないならno' do
+        Sheet.stub(:get_author_from_roles).with(any_args).and_return(@other_author)
+        @sheet.own?(@other_author).should be_false
+      end
+    end
+    context 'ロール内作家が取得できないとき' do
+      before do
+        Sheet.stub(:get_author_from_roles).with(any_args).and_return(nil)
+      end
+      it 'Falseを返す' do
+        r = @sheet.own?([@author])
+        r.should be_false
+      end
+    end
+  end
+  
+  describe '閲覧許可に於いて' do
+    before do
+      @sheet = FactoryGirl.build :sheet, :author_id => @author.id
+    end
+    context 'オープンモードのとき' do
+      before do
+        MagicNumber['run_mode'] = 0
+      end
+      it '自身にゲスト用ロールチェックを問い合わせしている' do
+        Sheet.any_instance.stub(:guest_role_check).and_return(true)
+        Sheet.any_instance.should_receive(:guest_role_check).with(any_args).exactly(1)
+        r = @sheet.visible?([@author])
+      end
+      it 'ゲスト用ロールチェックが失敗したとき、falseを返す' do
+        Sheet.any_instance.stub(:guest_role_check).and_return(false)
+        r = @sheet.visible?([@author])
+        r.should be_false
+      end
+    end
+    context 'クローズドモードのとき' do
+      before do
+        MagicNumber['run_mode'] = 1
+      end
+      it '自身に読者用ロールチェックを問い合わせしている' do
+        Sheet.any_instance.stub(:reader_role_check).and_return(true)
+        Sheet.any_instance.should_receive(:reader_role_check).with(any_args).exactly(1)
+        r = @sheet.visible?([@author])
+      end
+      it '読者用ロールチェックが失敗したとき、falseを返す' do
+        Sheet.any_instance.stub(:reader_role_check).and_return(false)
+        r = @sheet.visible?([@author])
+        r.should be_false
+      end
+    end
+    context '事前チェックする' do
+      before do
+        MagicNumber['run_mode'] = 1
+        Sheet.any_instance.stub(:reader_role_check).and_return(true)
+        Sheet.any_instance.stub(:own?).and_return(true)
+      end
+      it '自身に所持判定を問い合わせしている' do
+        Sheet.any_instance.should_receive(:own?).with(any_args).exactly(1)
+        r = @sheet.visible?([@author])
+      end
+    end
+    context 'つつがなく終わるとき' do
+      before do
+        MagicNumber['run_mode'] = 1
+        Sheet.any_instance.stub(:reader_role_check).and_return(true)
+      end
+      it '自分の用紙なら許可する' do
+        Sheet.any_instance.stub(:own?).and_return(true)
+        Sheet.any_instance.stub(:visible).and_return(0)
+        r = @sheet.visible?([@author])
+        r.should be_true
+      end
+      it '他人の非公開用紙なら許可しない' do
+        Sheet.any_instance.stub(:own?).and_return(false)
+        Sheet.any_instance.stub(:visible).and_return(0)
+        r = @sheet.visible?([@author])
+        r.should be_false
+      end
+      it '他人の用紙でも公開なら許可する' do
+        Sheet.any_instance.stub(:own?).and_return(false)
+        Sheet.any_instance.stub(:visible).and_return(1)
+        r = @sheet.visible?([@author])
+        r.should be_true
+      end
+    end
+  end
+  
+  describe '一覧取得に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+    end
+    context 'page補正について' do
+      it '文字列から数値に変換される' do
+        Sheet.page('8').should eq 8
+      end
+      it 'nilの場合は1になる' do
+        Sheet.page().should eq 1
+      end
+      it '0以下の場合は1になる' do
+        Sheet.page('0').should eq 1
+      end
+    end
+    context 'page_size補正について' do
+      it '文字列から数値に変換される' do
+        Sheet.page_size('7').should eq 7
+      end
+      it 'nilの場合はSheet.default_page_sizeになる' do
+        Sheet.page_size().should eq Sheet.default_page_size
+      end
+      it '0以下の場合はSheet.default_page_sizeになる' do
+        Sheet.page_size('0').should eq Sheet.default_page_size
+      end
+      it 'Sheet.max_page_sizeを超えた場合はSheet.max_page_sizeになる' do
+        Sheet.page_size('1000').should eq Sheet.max_page_size
+      end
+    end
+    context 'つつがなく終わるとき' do
+      it '一覧取得オプションを利用している' do
+        Sheet.stub(:list_opt).with(any_args).and_return({})
+        Sheet.should_receive(:list_opt).with(any_args).exactly(1)
+        r = Sheet.list
+      end
+    end
+    it 'リストを返す' do
+      c = Sheet.list
+      c.should eq [@sheet]
+    end
+    it '非公開用紙は(自分の用紙であっても)含んでいない' do
+      FactoryGirl.create :sheet, :author_id => @author.id, :visible => 0
+      c = Sheet.list
+      c.should eq [@sheet]
+    end
+    it '時系列で並んでいる' do
+      #公開用紙は(他人の用紙であっても)含んでいる
+      v = FactoryGirl.create :sheet, :author_id => @other_author.id, :updated_at => Time.now + 100
+      c = Sheet.list
+      c.should eq [v, @sheet]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @sheet2 = FactoryGirl.create :sheet, :author_id => @author.id, :updated_at => Time.now + 100
+        @sheet3 = FactoryGirl.create :sheet, :author_id => @author.id, :updated_at => Time.now + 200
+        @sheet4 = FactoryGirl.create :sheet, :author_id => @author.id, :updated_at => Time.now + 300
+        @sheet5 = FactoryGirl.create :sheet, :author_id => @author.id, :updated_at => Time.now + 400
+        Sheet.stub(:default_page_size).and_return(2)
+      end
+      it '通常は2件を返す' do
+        c = Sheet.list
+        c.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        c = Sheet.list(1)
+        c.should eq [@sheet5, @sheet4]
+      end
+      it 'page=2なら中間2件を返す' do
+        c = Sheet.list(2)
+        c.should eq [@sheet3, @sheet2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        c = Sheet.list(3)
+        c.should eq [@sheet]
+      end
+    end
+  end
+  
+  describe '自分の用紙一覧取得に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '一覧取得オプションを利用している' do
+        Sheet.stub(:list_opt).with(any_args).and_return({})
+        Sheet.should_receive(:list_opt).with(any_args).exactly(1)
+        r = Sheet.mylist @author
+      end
+    end
+    it 'リストを返す' do
+      c = Sheet.mylist @author
+      c.should eq [@sheet]
+    end
+    it '時系列で並んでいる' do
+      nc = FactoryGirl.create :sheet, :author_id => @author.id, :updated_at => Time.now + 100
+      cl = Sheet.mylist @author
+      cl.should eq [nc, @sheet]
+    end
+    it '他人の用紙は公開でも含まない' do
+      nc = FactoryGirl.create :sheet, :author_id => @other_author.id, :visible => 1
+      cl = Sheet.mylist @author
+      cl.should eq [@sheet]
+    end
+    it '自分の用紙は非公開でも含んでいる' do
+      nc = FactoryGirl.create :sheet, :author_id => @author.id, :visible => 0, :updated_at => Time.now + 100
+      cl = Sheet.mylist @author
+      cl.should eq [nc, @sheet]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @sheet2 = FactoryGirl.create :sheet, :author_id => @author.id, :updated_at => Time.now + 100
+        @sheet3 = FactoryGirl.create :sheet, :author_id => @author.id, :updated_at => Time.now + 200
+        @sheet4 = FactoryGirl.create :sheet, :author_id => @author.id, :updated_at => Time.now + 300
+        @sheet5 = FactoryGirl.create :sheet, :author_id => @author.id, :updated_at => Time.now + 400
+      end
+      it '通常は2件を返す' do
+        c = Sheet.mylist @author, 1, 2
+        c.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        c = Sheet.mylist(@author, 1, 2)
+        c.should eq [@sheet5, @sheet4]
+      end
+      it 'page=2なら中間2件を返す' do
+        c = Sheet.mylist(@author, 2, 2)
+        c.should eq [@sheet3, @sheet2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        c = Sheet.mylist(@author, 3, 2)
+        c.should eq [@sheet]
+      end
+    end
+  end
+  
+  describe '他作家の用紙一覧取得に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @other_sheet = FactoryGirl.create :sheet, :author_id => @other_author.id, :visible => 1
+    end
+    context 'つつがなく終わるとき' do
+      it '一覧取得オプションを利用している' do
+        Sheet.stub(:list_opt).with(any_args).and_return({})
+        Sheet.should_receive(:list_opt).with(any_args).exactly(1)
+        r = Sheet.himlist @other_author
+      end
+    end
+    it '指定した作家のリストを返す' do
+      r = Sheet.himlist @other_author
+      r.should eq [@other_sheet]
+    end
+    it '時系列で並んでいる' do
+      nc = FactoryGirl.create :sheet, :author_id => @other_author.id, :updated_at => Time.now + 100
+      r = Sheet.himlist @other_author
+      r.should eq [nc, @other_sheet]
+    end
+    it '公開用紙に限る ' do
+      hidden = FactoryGirl.create :sheet, :author_id => @other_author.id, :visible => 0
+      r = Sheet.himlist @other_author
+      r.should eq [@other_sheet]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @other_sheet2 = FactoryGirl.create :sheet, :author_id => @other_author.id, :updated_at => Time.now + 100
+        @other_sheet3 = FactoryGirl.create :sheet, :author_id => @other_author.id, :updated_at => Time.now + 200
+        @other_sheet4 = FactoryGirl.create :sheet, :author_id => @other_author.id, :updated_at => Time.now + 300
+        @other_sheet5 = FactoryGirl.create :sheet, :author_id => @other_author.id, :updated_at => Time.now + 400
+      end
+      it '通常は2件を返す' do
+        c = Sheet.himlist @other_author, 1, 2
+        c.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        c = Sheet.himlist(@other_author, 1, 2)
+        c.should eq [@other_sheet5, @other_sheet4]
+      end
+      it 'page=2なら中間2件を返す' do
+        c = Sheet.himlist(@other_author, 2, 2)
+        c.should eq [@other_sheet3, @other_sheet2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        c = Sheet.himlist(@other_author, 3, 2)
+        c.should eq [@other_sheet]
+      end
+    end
+  end
+  
+  describe '用紙一覧ページ制御に於いて' do
+    before do
+      Sheet.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = Sheet.list_paginate 
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it '用紙一覧の取得条件を利用している' do
+      Sheet.stub(:list_where).with(any_args).and_return('')
+      Sheet.should_receive(:list_where).with(any_args).exactly(1)
+      r = Sheet.list_paginate 
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = Sheet.list_paginate 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe '自分の用紙一覧ページ制御に於いて' do
+    before do
+      Sheet.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = Sheet.mylist_paginate @author
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it '自分の用紙一覧の取得条件を利用している' do
+      Sheet.stub(:mylist_where).with(any_args).and_return('')
+      Sheet.should_receive(:mylist_where).with(any_args).exactly(1)
+      r = Sheet.mylist_paginate @author
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = Sheet.mylist_paginate @author, 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe '他作家の用紙一覧ページ制御に於いて' do
+    before do
+      Sheet.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = Sheet.himlist_paginate @other_author
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it '他作家の用紙一覧の取得条件を利用している' do
+      Sheet.stub(:himlist_where).with(any_args).and_return('')
+      Sheet.should_receive(:himlist_where).with(any_args).exactly(1)
+      r = Sheet.himlist_paginate @other_author
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = Sheet.himlist_paginate @other_author, 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe '一覧取得オプションに於いて' do
+    it '2つの項目を含んでいる' do
+      r = Sheet.list_opt
+      r.should have(2).items
+    end
+    it 'スクコマを含んでいる' do
+      r = Sheet.list_opt
+      r.has_key?(:sheet_panels).should be_true
+    end
+      it 'スクコマはコマを含んでいる' do
+        r = Sheet.list_opt
+        r[:sheet_panels].has_key?(:panel).should be_true
+      end
+    it '作家を含んでいる' do
+      r = Sheet.list_opt
+      r.has_key?(:author).should be_true
+    end
+  end
+  describe 'json一覧出力オプションに於いて' do
+    before do
+      @op = FactoryGirl.create :original_picture, :artist_id => @artist.id
+      @p = FactoryGirl.create :picture, :original_picture_id => @op.id, :license_id => @license.id, :artist_id => @artist.id
+      @rp = FactoryGirl.create :resource_picture, :artist_id => @artist.id, :license_id => @license.id, :original_picture_id => @op.id, :picture_id => @p.id
+      @sbt = FactoryGirl.create :speech_balloon_template
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id, :visible => 1
+      @panel = FactoryGirl.create :panel, :author_id => @author.id, :publish => 1
+      @sheet_panel = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id
+    end
+    it 'スクコマを含んでいる' do
+      r = Sheet.list.to_json Sheet.list_json_opt
+      j = JSON.parse r
+      i = j.first
+      i.has_key?('sheet_panels').should be_true
+    end
+      it 'スクコマはコマを含んでいる' do
+        r = Sheet.list.to_json Sheet.list_json_opt
+        j = JSON.parse r
+        i = j.first
+        s = i['sheet_panels'].first
+        s.has_key?('panel').should be_true
+      end
+    it '作家を含んでいる' do
+      r = Sheet.list.to_json Sheet.list_json_opt
+      j = JSON.parse r
+      i = j.first
+      i.has_key?('author').should be_true
+    end
+  end
+  
+  describe '単体取得に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '単体取得オプションを利用している' do
+        Sheet.stub(:show_opt).with(any_args).and_return({})
+        Sheet.should_receive(:show_opt).with(any_args).exactly(1)
+        r = Sheet.show @sheet.id, @author
+      end
+      it '閲覧許可を問い合わせている' do
+        Sheet.any_instance.stub(:visible?).with(any_args).and_return(true)
+        Sheet.any_instance.should_receive(:visible?).with(any_args).exactly(1)
+        r = Sheet.show @sheet.id, @author
+      end
+    end
+    it '指定の用紙を返す' do
+      c = Sheet.show @sheet.id, @author
+      c.should eq @sheet
+    end
+    context '閲覧許可が出なかったとき' do
+      it '403Forbidden例外を返す' do
+        Sheet.any_instance.stub(:visible?).and_return(false)
+        lambda{
+          Sheet.show @sheet.id, @author
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+    context '存在しない用紙を開こうとしたとき' do
+      it '404RecordNotFound例外を返す' do
+        lambda{
+          Sheet.show 110, @author
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+    end
+  end
+  
+  describe '編集取得に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '単体取得オプションを利用している' do
+        Sheet.stub(:show_opt).with(any_args).and_return({})
+        Sheet.should_receive(:show_opt).with(any_args).exactly(1)
+        r = Sheet.edit @sheet.id, @author
+      end
+      it '所持判定を問い合わせている' do
+        Sheet.any_instance.stub(:own?).with(any_args).and_return(true)
+        Sheet.any_instance.should_receive(:own?).with(any_args).exactly(1)
+        r = Sheet.edit @sheet.id, @author
+      end
+    end
+    it '指定の用紙を返す' do
+      Sheet.any_instance.stub(:own?).and_return(true)
+      c = Sheet.edit @sheet.id, @author.id
+      c.should eq @sheet
+    end
+    context '他人の用紙を開こうとしたとき' do
+      it '403Forbidden例外を返す' do
+        Sheet.any_instance.stub(:own?).and_return(false)
+        lambda{
+          Sheet.edit @sheet.id, @author
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+    context '存在しない用紙を開こうとしたとき' do
+      it '404RecordNotFound例外を返す' do
+        lambda{
+          Sheet.edit 110, @author
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+    end
+  end
+  describe '単体取得オプションに於いて' do
+    it 'includeキーを含んでいる' do
+      r = Sheet.show_opt
+      r.has_key?(:include).should be_true
+    end
+    it '2つの項目を含んでいる' do
+      r = Sheet.show_opt[:include]
+      r.should have(2).items
+    end
+    it '作家を含んでいる' do
+      r = Sheet.show_opt[:include]
+      r.has_key?(:author).should be_true
+    end
+    it 'スクコマを含んでいる' do
+      r = Sheet.show_opt[:include]
+      r.has_key?(:sheet_panels).should be_true
+    end
+      it 'スクコマはコマを含んでいる' do
+        r = Sheet.show_opt[:include]
+        r[:sheet_panels].has_key?(:panel).should be_true
+      end
+  end
+  describe 'json単体出力オプションに於いて' do
+    before do
+      @op = FactoryGirl.create :original_picture, :artist_id => @artist.id
+      @p = FactoryGirl.create :picture, :original_picture_id => @op.id, :license_id => @license.id, :artist_id => @artist.id
+      @rp = FactoryGirl.create :resource_picture, :artist_id => @artist.id, :license_id => @license.id, :original_picture_id => @op.id, :picture_id => @p.id
+      @sbt = FactoryGirl.create :speech_balloon_template
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id, :visible => 1
+      @panel = FactoryGirl.create :panel, :author_id => @author.id, :publish => 1
+      @sheet_panel = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id
+    end
+    it 'スクコマを含んでいる' do
+      r = Sheet.show(@sheet.id, @author).to_json Sheet.show_json_opt
+      j = JSON.parse r
+      i = j
+      i.has_key?('sheet_panels').should be_true
+    end
+      it 'スクコマはコマを含んでいる' do
+        r = Sheet.show(@sheet.id, @author).to_json Sheet.show_json_opt
+        j = JSON.parse r
+        i = j
+        s = i['sheet_panels'].first
+        s.has_key?('panel').should be_true
+      end
+    it '作家を含んでいる' do
+      r = Sheet.show(@sheet.id, @author).to_json Sheet.show_json_opt
+      j = JSON.parse r
+      i = j
+      i.has_key?('author').should be_true
+    end
+  end
+  
+  describe '削除に於いて' do
+    before do
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id, :publish => 1
+      @sheet_panel = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @sheet.id, :panel_id => @panel.id
+      @other_sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @other_sheet_panel = FactoryGirl.create :sheet_panel, :author_id => @author.id, :sheet_id => @other_sheet.id, :panel_id => @panel.id
+    end
+    context 'つつがなく終わるとき' do
+      it '自身を削除する' do
+        lambda {
+          r = @sheet.destroy_with_sheet_panel
+        }.should change(Sheet, :count).by(-1)
+        lambda {
+          r = Sheet.find @sheet.id
+        }.should raise_error
+      end
+      it '自身にリンクしているスクコマをすべて削除する' do
+        lambda {
+          r = @sheet.destroy_with_sheet_panel
+        }.should change(SheetPanel, :count).by(-1)
+        lambda {
+          r = SheetPanel.find @sheet_panel.id
+        }.should raise_error
+      end
+      it 'Trueを返す' do
+        r = @sheet.destroy_with_sheet_panel
+        r.should be_true
+      end
+    end
+    context '削除に失敗したとき' do
+      before do
+        SheetPanel.any_instance.stub(:destroy).with(any_args).and_return(false)
+      end
+      it 'Falseを返す' do
+        r = @sheet.destroy_with_sheet_panel
+        r.should be_false
+      end
+      it 'ロールバックしている' do
+        lambda {
+          r = @sheet.destroy_with_sheet_panel
+        }.should_not change(Sheet, :count)
+        lambda {
+          r = @sheet.destroy_with_sheet_panel
+        }.should_not change(SheetPanel, :count)
+      end
+    end
+  end
+end
index 4d63aca..da4ea56 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 
 describe SpeechBalloon do
   before do
+    SpeechBalloonTemplate.delete_all
     @admin = FactoryGirl.create :admin
     @user = FactoryGirl.create( :user_yas)
     @author = FactoryGirl.create :author, :user_id => @user.id
index 544879a..6797ae5 100644 (file)
@@ -3,6 +3,7 @@ require 'spec_helper'
 #フキダシテンプレート
 describe SpeechBalloonTemplate do
   before do
+    SpeechBalloonTemplate.delete_all
     #テストデータを用意してね
     @f = Rails.root + 'spec/json/speech_balloon_template.json'
     @t = File.open(@f, 'r').read
index 3e18d1e..51af8e1 100644 (file)
@@ -4,6 +4,7 @@ require 'spec_helper'
 \r
 describe Speech do\r
   before do\r
+    SpeechBalloonTemplate.delete_all\r
     @admin = FactoryGirl.create :admin\r
     @user = FactoryGirl.create( :user_yas)\r
     @author = FactoryGirl.create :author, :user_id => @user.id\r
@@ -15,6 +16,7 @@ describe Speech do
     @license = FactoryGirl.create :license, :license_group_id => @lg.id, :system_picture_id => @sp.id\r
     \r
     @writing_format = FactoryGirl.create :writing_format\r
+    SpeechBalloonTemplate.delete_all\r
     @speech_balloon_template = FactoryGirl.create :speech_balloon_template, "name" => "circle@pettan.com", "classname" => "CircleSpeechBalloon", "caption" => "cc",  "system_picture_id" => @sp.id, "settings" => '{}'\r
     @panel = FactoryGirl.create :panel, :author_id => @author.id\r
   end\r
@@ -429,37 +431,37 @@ describe Speech do
       before do\r
         @sb2 = FactoryGirl.build :speech_balloon, :panel_id => @panel.id, :speech_balloon_template_id => @speech_balloon_template.id\r
         @speech2 = @sb2.build_speech(\r
-          FactoryGirl.attributes_for(:speech, :writing_format_id => @writing_format.id)\r
+          FactoryGirl.attributes_for(:speech, :writing_format_id => @writing_format.id, :updated_at => Time.now + 100)\r
         )\r
         @balloon2 = @sb2.build_balloon(\r
-          FactoryGirl.attributes_for(:balloon, :system_picture_id => @sp.id, :updated_at => Time.now + 100)\r
+          FactoryGirl.attributes_for(:balloon, :system_picture_id => @sp.id)\r
         )\r
         @sb2.boost\r
         @sb2.save!\r
         @sb3 = FactoryGirl.build :speech_balloon, :panel_id => @panel.id, :speech_balloon_template_id => @speech_balloon_template.id\r
         @speech3 = @sb3.build_speech(\r
-          FactoryGirl.attributes_for(:speech, :writing_format_id => @writing_format.id)\r
+          FactoryGirl.attributes_for(:speech, :writing_format_id => @writing_format.id, :updated_at => Time.now + 200)\r
         )\r
         @balloon3 = @sb3.build_balloon(\r
-          FactoryGirl.attributes_for(:balloon, :system_picture_id => @sp.id, :updated_at => Time.now + 200)\r
+          FactoryGirl.attributes_for(:balloon, :system_picture_id => @sp.id)\r
         )\r
         @sb3.boost\r
         @sb3.save!\r
         @sb4 = FactoryGirl.build :speech_balloon, :panel_id => @panel.id, :speech_balloon_template_id => @speech_balloon_template.id\r
         @speech4 = @sb4.build_speech(\r
-          FactoryGirl.attributes_for(:speech, :writing_format_id => @writing_format.id)\r
+          FactoryGirl.attributes_for(:speech, :writing_format_id => @writing_format.id, :updated_at => Time.now + 300)\r
         )\r
         @balloon4 = @sb4.build_balloon(\r
-          FactoryGirl.attributes_for(:balloon, :system_picture_id => @sp.id, :updated_at => Time.now + 300)\r
+          FactoryGirl.attributes_for(:balloon, :system_picture_id => @sp.id)\r
         )\r
         @sb4.boost\r
         @sb4.save!\r
         @sb5 = FactoryGirl.build :speech_balloon, :panel_id => @panel.id, :speech_balloon_template_id => @speech_balloon_template.id\r
         @speech5 = @sb5.build_speech(\r
-          FactoryGirl.attributes_for(:speech, :writing_format_id => @writing_format.id)\r
+          FactoryGirl.attributes_for(:speech, :writing_format_id => @writing_format.id, :updated_at => Time.now + 400)\r
         )\r
         @balloon5 = @sb5.build_balloon(\r
-          FactoryGirl.attributes_for(:balloon, :system_picture_id => @sp.id, :updated_at => Time.now + 400)\r
+          FactoryGirl.attributes_for(:balloon, :system_picture_id => @sp.id)\r
         )\r
         @sb5.boost\r
         @sb5.save!\r
@@ -487,37 +489,37 @@ describe Speech do
       before do\r
         @sb2 = FactoryGirl.build :speech_balloon, :panel_id => @panel.id, :speech_balloon_template_id => @speech_balloon_template.id\r
         @speech2 = @sb2.build_speech(\r
-          FactoryGirl.attributes_for(:speech, :writing_format_id => @writing_format.id)\r
+          FactoryGirl.attributes_for(:speech, :writing_format_id => @writing_format.id, :updated_at => Time.now + 100)\r
         )\r
         @balloon2 = @sb2.build_balloon(\r
-          FactoryGirl.attributes_for(:balloon, :system_picture_id => @sp.id, :updated_at => Time.now + 100)\r
+          FactoryGirl.attributes_for(:balloon, :system_picture_id => @sp.id)\r
         )\r
         @sb2.boost\r
         @sb2.save!\r
         @sb3 = FactoryGirl.build :speech_balloon, :panel_id => @panel.id, :speech_balloon_template_id => @speech_balloon_template.id\r
         @speech3 = @sb3.build_speech(\r
-          FactoryGirl.attributes_for(:speech, :writing_format_id => @writing_format.id)\r
+          FactoryGirl.attributes_for(:speech, :writing_format_id => @writing_format.id, :updated_at => Time.now + 200)\r
         )\r
         @balloon3 = @sb3.build_balloon(\r
-          FactoryGirl.attributes_for(:balloon, :system_picture_id => @sp.id, :updated_at => Time.now + 200)\r
+          FactoryGirl.attributes_for(:balloon, :system_picture_id => @sp.id)\r
         )\r
         @sb3.boost\r
         @sb3.save!\r
         @sb4 = FactoryGirl.build :speech_balloon, :panel_id => @panel.id, :speech_balloon_template_id => @speech_balloon_template.id\r
         @speech4 = @sb4.build_speech(\r
-          FactoryGirl.attributes_for(:speech, :writing_format_id => @writing_format.id)\r
+          FactoryGirl.attributes_for(:speech, :writing_format_id => @writing_format.id, :updated_at => Time.now + 300)\r
         )\r
         @balloon4 = @sb4.build_balloon(\r
-          FactoryGirl.attributes_for(:balloon, :system_picture_id => @sp.id, :updated_at => Time.now + 300)\r
+          FactoryGirl.attributes_for(:balloon, :system_picture_id => @sp.id)\r
         )\r
         @sb4.boost\r
         @sb4.save!\r
         @sb5 = FactoryGirl.build :speech_balloon, :panel_id => @panel.id, :speech_balloon_template_id => @speech_balloon_template.id\r
         @speech5 = @sb5.build_speech(\r
-          FactoryGirl.attributes_for(:speech, :writing_format_id => @writing_format.id)\r
+          FactoryGirl.attributes_for(:speech, :writing_format_id => @writing_format.id, :updated_at => Time.now + 400)\r
         )\r
         @balloon5 = @sb5.build_balloon(\r
-          FactoryGirl.attributes_for(:balloon, :system_picture_id => @sp.id, :updated_at => Time.now + 400)\r
+          FactoryGirl.attributes_for(:balloon, :system_picture_id => @sp.id)\r
         )\r
         @sb5.boost\r
         @sb5.save!\r
diff --git a/spec/models/story_sheet_spec.rb b/spec/models/story_sheet_spec.rb
new file mode 100644 (file)
index 0000000..b00d8a9
--- /dev/null
@@ -0,0 +1,1856 @@
+# -*- encoding: utf-8 -*-
+require 'spec_helper'
+#スト紙
+describe StorySheet do
+  before do
+    @admin = FactoryGirl.create :admin
+    @sp = FactoryGirl.create :system_picture
+    @lg = FactoryGirl.create :license_group
+    @license = FactoryGirl.create :license, :license_group_id => @lg.id, :system_picture_id => @sp.id
+    SpeechBalloonTemplate.delete_all
+    @speech_balloon_template = FactoryGirl.create :speech_balloon_template, "name" => "circle@pettan.com", "classname" => "CircleSpeechBalloon", "caption" => "cc",  "system_picture_id" => @sp.id, "settings" => '{}'
+    @writing_format = FactoryGirl.create :writing_format
+    @user = FactoryGirl.create( :user_yas)
+    @author = FactoryGirl.create :author, :user_id => @user.id
+    @artist = FactoryGirl.create :artist_yas, :author_id => @author.id
+    @other_user = FactoryGirl.create( :user_yas)
+    @other_author = FactoryGirl.create :author, :user_id => @other_user.id
+    @op = FactoryGirl.create :original_picture, :artist_id => @artist.id
+    @p = FactoryGirl.create :picture, :original_picture_id => @op.id, :license_id => @license.id, :artist_id => @artist.id
+    @rp = FactoryGirl.create :resource_picture, :artist_id => @artist.id, :license_id => @license.id, :original_picture_id => @op.id, :picture_id => @p.id
+  end
+  
+  describe '検証に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.build :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+    end
+    
+    context 'オーソドックスなデータのとき' do
+      it '下限データが通る' do
+        @story_sheet.t = 0
+        @story_sheet.should be_valid
+      end
+      it '上限データが通る' do
+        @story_sheet.t = 99999
+        @story_sheet.should be_valid
+      end
+    end
+    
+    context 'story_idを検証するとき' do
+      it 'nullなら失敗する' do
+        @story_sheet.story_id = nil
+        @story_sheet.should_not be_valid
+      end
+      it '数値でなければ失敗する' do
+        @story_sheet.story_id = 'a'
+        @story_sheet.should_not be_valid
+      end
+      it '存在するストーリーでなければ失敗する' do
+        @story_sheet.story_id = 0
+        @story_sheet.should_not be_valid
+      end
+    end
+    
+    context 'sheet_idを検証するとき' do
+      it 'nullなら失敗する' do
+        @story_sheet.sheet_id = nil
+        @story_sheet.should_not be_valid
+      end
+      it '数値でなければ失敗する' do
+        @story_sheet.sheet_id = 'a'
+        @story_sheet.should_not be_valid
+      end
+      it '存在する用紙でなければ失敗する' do
+        @story_sheet.sheet_id = 0
+        @story_sheet.should_not be_valid
+      end
+    end
+    
+    context 'tを検証するとき' do
+      it 'nullなら失敗する' do
+        @story_sheet.t = nil
+        @story_sheet.should_not be_valid
+      end
+      it '数値でなければ失敗する' do
+        @story_sheet.t = 'a'
+        @story_sheet.should_not be_valid
+      end
+      it '負なら失敗する' do
+        @story_sheet.t = -1
+        @story_sheet.should_not be_valid
+      end
+    end
+    
+    context 'author_idを検証するとき' do
+      it 'nullなら失敗する' do
+        @story_sheet.author_id = nil
+        @story_sheet.should_not be_valid
+      end
+      it '数値でなければ失敗する' do
+        @story_sheet.author_id = 'a'
+        @story_sheet.should_not be_valid
+      end
+      it '存在する作家でなければ失敗する' do
+        @story_sheet.author_id = 0
+        @story_sheet.should_not be_valid
+      end
+    end
+    context '全体を検証するとき' do
+    end
+  end
+  
+  describe 'デフォルト値補充に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+    end
+    
+    #dbのデフォルト値が0だから明示的にnilにしないと追加ができない
+    it 'tをnilにする' do
+      @story_sheet = FactoryGirl.build :story_sheet, :story_id => @story.id, :sheet_id => @sheet.id
+      @story_sheet.supply_default
+      @story_sheet.t.should be_nil
+    end
+    
+  end
+  
+  describe '上書き補充に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+    end
+    
+    context 'author_idを補充' do
+      it '問答無用でauthor_idを補充する' do
+        @story_sheet = FactoryGirl.build :story_sheet, :story_id => @story.id, :sheet_id => @sheet.id
+        @story_sheet.author_id = nil
+        @story_sheet.overwrite @author
+        @story_sheet.author_id.should eq @author.id
+      end
+    end
+    
+  end
+  
+  describe '所持判定に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+      @comico = FactoryGirl.create :comic, :author_id => @other_author.id
+      @storyo = FactoryGirl.create :story, :comic_id => @comico.id, :author_id => @other_author.id
+      @sheeto = FactoryGirl.create :sheet, :author_id => @other_author.id
+      @story_sheeto = FactoryGirl.create :story_sheet, :author_id => @other_author.id, :story_id => @storyo.id, :sheet_id => @sheeto.id
+    end
+    context '事前チェックする' do
+      it '自身にロールリストからの作家取得を依頼している' do
+        StorySheet.should_receive(:get_author_from_roles).with(any_args).exactly(1)
+        r = @story_sheet.own?([@author])
+      end
+    end
+    context 'ロール内作家が取得できるとき' do
+      before do
+      end
+      it 'ロール内作家のidが自身の作家idと一致するなら許可する' do
+        StorySheet.stub(:get_author_from_roles).with(any_args).and_return(@author)
+        r = @story_sheet.own?([@author])
+        r.should be_true
+      end
+      it 'ロール内作家のidが自身の作家idと一致しないならno' do
+        StorySheet.stub(:get_author_from_roles).with(any_args).and_return(@other_author)
+        @story_sheet.own?(@other_author).should be_false
+      end
+    end
+    context 'ロール内作家が取得できないとき' do
+      before do
+        StorySheet.stub(:get_author_from_roles).with(any_args).and_return(nil)
+      end
+      it 'Falseを返す' do
+        r = @story_sheet.own?([@author])
+        r.should be_false
+      end
+    end
+  end
+  
+  describe '閲覧許可に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+    end
+    context 'オープンモードのとき' do
+      before do
+        MagicNumber['run_mode'] = 0
+      end
+      it '自身にゲスト用ロールチェックを問い合わせしている' do
+        StorySheet.any_instance.stub(:guest_role_check).and_return(true)
+        StorySheet.any_instance.should_receive(:guest_role_check).with(any_args).exactly(1)
+        r = @story_sheet.visible?([@author])
+      end
+      it 'ゲスト用ロールチェックが失敗したとき、falseを返す' do
+        StorySheet.any_instance.stub(:guest_role_check).and_return(false)
+        r = @story_sheet.visible?([@author])
+        r.should be_false
+      end
+    end
+    context 'クローズドモードのとき' do
+      before do
+        MagicNumber['run_mode'] = 1
+      end
+      it '自身に読者用ロールチェックを問い合わせしている' do
+        StorySheet.any_instance.stub(:reader_role_check).and_return(true)
+        StorySheet.any_instance.should_receive(:reader_role_check).with(any_args).exactly(1)
+        r = @story_sheet.visible?([@author])
+      end
+      it '読者用ロールチェックが失敗したとき、falseを返す' do
+        StorySheet.any_instance.stub(:reader_role_check).and_return(false)
+        r = @story_sheet.visible?([@author])
+        r.should be_false
+      end
+    end
+    context '事前チェックする' do
+      before do
+        MagicNumber['run_mode'] = 1
+        StorySheet.any_instance.stub(:reader_role_check).and_return(true)
+      end
+      it '自身のストーリーに所持判定を問い合わせしている' do
+        Story.any_instance.stub(:own?).and_return(true)
+        Story.any_instance.should_receive(:own?).with(any_args).exactly(1)
+        r = @story_sheet.visible?([@author])
+      end
+      it '自身のストーリーに閲覧許可を問い合わせしている' do
+        Story.any_instance.stub(:own?).and_return(false)
+        Story.any_instance.stub(:visible?).and_return(true)
+        Story.any_instance.should_receive(:visible?).with(any_args).exactly(1)
+        r = @story_sheet.visible?([@author])
+      end
+    end
+    context 'つつがなく終わるとき' do
+      before do
+        MagicNumber['run_mode'] = 1
+        StorySheet.any_instance.stub(:reader_role_check).and_return(true)
+      end
+      it '自分のストーリーのスト紙なら許可する' do
+        Story.any_instance.stub(:own?).and_return(true)
+        Story.any_instance.stub(:visible).and_return(0)
+        r = @story_sheet.visible?([@author])
+        r.should be_true
+      end
+      it '他人の非公開ストーリーのスト紙なら許可しない' do
+        Story.any_instance.stub(:own?).and_return(false)
+        Story.any_instance.stub(:visible).and_return(0)
+        r = @story_sheet.visible?([@author])
+        r.should be_false
+      end
+      it '他人のストーリーのスト紙でも公開なら許可する' do
+        Story.any_instance.stub(:own?).and_return(false)
+        Story.any_instance.stub(:visible).and_return(1)
+        r = @story_sheet.visible?([@author])
+        r.should be_true
+      end
+    end
+  end
+  
+  describe 'プレイリスト取得に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @sheet2 = FactoryGirl.create :sheet, :author_id => @author.id, :visible => 0
+      @story_sheet = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+      @other_comic = FactoryGirl.create :comic, :author_id => @other_author.id
+      @other_story = FactoryGirl.create :story, :comic_id => @other_comic.id, :author_id => @other_author.id, :visible => 1
+      @other_sheet = FactoryGirl.create :sheet, :author_id => @other_author.id, :visible => 1
+    end
+    context 'offset補正について' do
+      it '文字列から数値に変換される' do
+        StorySheet.offset(100, '8').should eq 8
+      end
+      it 'nilの場合は0になる' do
+        StorySheet.offset(100).should eq 0
+      end
+      #投稿された用紙数以上の値が指定されたときは、最後の用紙だけになる
+      #最後の用紙とは、用紙数‐1.
+      it '1件のときオフセット1なら0になる' do
+        StorySheet.offset(1, '1').should eq 0
+      end
+      it '5件のときオフセット5なら4になる' do
+        StorySheet.offset(5, '5').should eq 4
+      end
+      # 負の値が指定されたときは、最後の用紙から数えて用紙を飛ばして表示する。
+      #-4のときは、最後から4つの用紙を表示する。 
+      it '2件のときオフセット-1なら1になる' do
+        StorySheet.offset(2, '-1').should eq 1
+      end
+      it '5件のときオフセット-2なら3になる' do
+        StorySheet.offset(5, '-2').should eq 3
+      end
+      # 最終的なが負になるなど、不正な値が入ったときは0となる。 
+      it '2件のときオフセット-5なら0になる' do
+        StorySheet.offset(2, '-5').should eq 0
+      end
+    end
+    context 'sheet_count補正について' do
+      it '文字列から数値に変換される' do
+        StorySheet.sheet_count(100, '7').should eq 7
+      end
+      it 'nilの場合はStorySheet.default_sheet_sizeになる' do
+        StorySheet.sheet_count(100).should eq StorySheet.default_sheet_size
+      end
+      it '0以下の場合はStorySheet.default_sheet_sizeになる' do
+        StorySheet.sheet_count(100, '0').should eq StorySheet.default_sheet_size
+      end
+      it 'StorySheet.max_sheet_sizeを超えた場合はStorySheet.max_sheet_sizeになる' do
+        StorySheet.sheet_count(100, '1000').should eq StorySheet.max_sheet_size
+      end
+    end
+    it 'リストを返す' do
+      c = StorySheet.play_list @story, @author
+      c.should eq [@story_sheet]
+    end
+    it 't順で並んでいる' do
+      #公開ストーリーの公開用紙は(他人のストーリーであっても)含んでいる
+      v = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id, :t => 1
+      c = StorySheet.play_list @story, @author
+      c.should eq [ @story_sheet, v]
+    end
+    it '非公開の用紙は含んでいる' do
+      h = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet2.id, :t => 1
+      c = StorySheet.play_list @story, @author
+      c.should eq [ @story_sheet, h]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @story_sheet2 = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id, :t => 1
+        @story_sheet3 = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id, :t => 2
+        @story_sheet4 = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id, :t => 3
+        @story_sheet5 = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id, :t => 4
+      end
+      it 'offset=0なら末尾2件を返す' do
+        #時系列で並んでいる
+        c = StorySheet.play_list( @story, @author, 0, 2)
+        c.should eq [@story_sheet, @story_sheet2]
+      end
+      it 'offset=2なら中間2件を返す' do
+        c = StorySheet.play_list(@story, @author, 2, 2)
+        c.should eq [@story_sheet3, @story_sheet4]
+      end
+      it 'offset=4なら先頭1件を返す' do
+        c = StorySheet.play_list(@story, @author, 4, 2)
+        c.should eq [@story_sheet5]
+      end
+    end
+  end
+  
+  describe '一覧取得に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+      @other_comic = FactoryGirl.create :comic, :author_id => @other_author.id
+      @other_story = FactoryGirl.create :story, :comic_id => @other_comic.id, :author_id => @other_author.id, :visible => 1
+      @other_sheet = FactoryGirl.create :sheet, :author_id => @other_author.id
+      @hidden_comic = FactoryGirl.create :comic, :author_id => @author.id
+      @hidden_story = FactoryGirl.create :story, :author_id => @author.id, :visible => 0
+    end
+    context 'page補正について' do
+      it '文字列から数値に変換される' do
+        StorySheet.page('8').should eq 8
+      end
+      it 'nilの場合は1になる' do
+        StorySheet.page().should eq 1
+      end
+      it '0以下の場合は1になる' do
+        StorySheet.page('0').should eq 1
+      end
+    end
+    context 'page_size補正について' do
+      it '文字列から数値に変換される' do
+        StorySheet.page_size('7').should eq 7
+      end
+      it 'nilの場合はStorySheet.default_page_sizeになる' do
+        StorySheet.page_size().should eq StorySheet.default_page_size
+      end
+      it '0以下の場合はStorySheet.default_page_sizeになる' do
+        StorySheet.page_size('0').should eq StorySheet.default_page_size
+      end
+      it 'StorySheet.max_page_sizeを超えた場合はStorySheet.max_page_sizeになる' do
+        StorySheet.page_size('1000').should eq StorySheet.max_page_size
+      end
+    end
+    it 'リストを返す' do
+      c = StorySheet.list
+      c.should eq [@story_sheet]
+    end
+    it '時系列で並んでいる' do
+      #公開ストーリーのStorySheetは(他人のStorySheetであっても)含んでいる
+      v = FactoryGirl.create :story_sheet, :author_id => @other_author.id, :story_id => @other_story.id, :sheet_id => @other_sheet.id, :t => 0, :updated_at => Time.now + 100
+      c = StorySheet.list 
+      c.should eq [ v, @story_sheet]
+    end
+    it '非公開のスト紙は(自分のスト紙であっても)含まない' do
+      h = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @hidden_story.id, :sheet_id => @sheet.id, :t => 0
+      c = StorySheet.list 
+      c.should eq [ @story_sheet]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @story_sheet2 = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id, :t => 1, :updated_at => Time.now + 100
+        @story_sheet3 = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id, :t => 2, :updated_at => Time.now + 200
+        @story_sheet4 = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id, :t => 3, :updated_at => Time.now + 300
+        @story_sheet5 = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id, :t => 4, :updated_at => Time.now + 400
+      end
+      it '通常は2件を返す' do
+        l = StorySheet.list 1, 2
+        l.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        l = StorySheet.list 1, 2
+        l.should eq [@story_sheet5, @story_sheet4]
+      end
+      it 'page=2なら中間2件を返す' do
+        l = StorySheet.list 2, 2
+        l.should eq [@story_sheet3, @story_sheet2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        l = StorySheet.list 3, 2
+        l.should eq [@story_sheet]
+      end
+    end
+  end
+  
+  describe '自分のスト紙一覧取得に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+      @other_comic = FactoryGirl.create :comic, :author_id => @other_author.id
+      @other_story = FactoryGirl.create :story, :comic_id => @other_comic.id, :author_id => @other_author.id, :visible => 1
+      @other_sheet = FactoryGirl.create :sheet, :author_id => @other_author.id, :visible => 1
+      @hcomic = FactoryGirl.create :comic, :author_id => @author.id
+      @hstory = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 0
+      @hsheet = FactoryGirl.create :sheet, :author_id => @author.id, :visible => 0
+    end
+    context 'つつがなく終わるとき' do
+      it '一覧取得オプションを利用している' do
+        StorySheet.stub(:list_opt).with(any_args).and_return({})
+        StorySheet.should_receive(:list_opt).with(any_args).exactly(1)
+        r = StorySheet.mylist @author
+      end
+    end
+    it 'リストを返す' do
+      s = StorySheet.mylist @author
+      s.should eq [@story_sheet]
+    end
+    it '時系列で並んでいる' do
+      ns = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id, :t => 1, :updated_at => Time.now + 100
+      sl = StorySheet.mylist @author
+      sl.should eq [ns, @story_sheet]
+    end
+    it '他人のスト紙は用紙ストーリーともに公開でも含まない' do
+      so = FactoryGirl.create :story_sheet, :author_id => @other_author.id, :story_id => @other_story.id, :sheet_id => @other_sheet.id
+      sl = StorySheet.mylist @author
+      sl.should eq [@story_sheet]
+    end
+    it '自分のスト紙は用紙ストーリーともに非公開でも含んでいる' do
+      hs = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @hstory.id, :sheet_id => @hsheet.id, :updated_at => Time.now + 100
+      sl = StorySheet.mylist @author
+      sl.should eq [hs, @story_sheet]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @story_sheet2 = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id, :t => 1, :updated_at => Time.now + 100
+        @story_sheet3 = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id, :t => 2, :updated_at => Time.now + 200
+        @story_sheet4 = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id, :t => 3, :updated_at => Time.now + 300
+        @story_sheet5 = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id, :t => 4, :updated_at => Time.now + 400
+        StorySheet.stub(:default_page_size).and_return(2)
+      end
+      it '通常は2件を返す' do
+        l = StorySheet.mylist @author, 1, 2
+        l.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        l = StorySheet.mylist @author, 1, 2
+        l.should eq [@story_sheet5, @story_sheet4]
+      end
+      it 'page=2なら中間2件を返す' do
+        l = StorySheet.mylist @author, 2, 2
+        l.should eq [@story_sheet3, @story_sheet2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        l = StorySheet.mylist @author, 3, 2
+        l.should eq [@story_sheet]
+      end
+    end
+  end
+  
+  describe '他作家のスト紙一覧取得に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+      @other_comic = FactoryGirl.create :comic, :author_id => @other_author.id
+      @other_story = FactoryGirl.create :story, :comic_id => @other_comic.id, :author_id => @other_author.id, :visible => 1
+      @other_sheet = FactoryGirl.create :sheet, :author_id => @other_author.id, :visible => 1
+      @other_story_sheet = FactoryGirl.create :story_sheet, :author_id => @other_author.id, :story_id => @other_story.id, :sheet_id => @other_sheet.id
+    end
+    it 'リストを返す' do
+      r = StorySheet.himlist @other_author
+      r.should eq [@other_story_sheet]
+    end
+    it '時系列で並んでいる' do
+      ns = FactoryGirl.create :story_sheet, :author_id => @other_author.id, :story_id => @other_story.id, :sheet_id => @other_sheet.id, :t => 1, :updated_at => Time.now + 100
+      r = StorySheet.himlist @other_author
+      r.should eq [ns, @other_story_sheet]
+    end
+    it '公開ストーリーのスト紙' do
+      @hstory = FactoryGirl.create :story, :author_id => @other_author.id, :visible => 0
+      ns = FactoryGirl.create :story_sheet, :author_id => @other_author.id, :story_id => @hstory.id, :sheet_id => @other_sheet.id, :t => 1, :updated_at => Time.now + 100
+      r = StorySheet.himlist @other_author
+      r.should eq [@other_story_sheet]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @other_story_sheet2 = FactoryGirl.create :story_sheet, :author_id => @other_author.id, :story_id => @other_story.id, :sheet_id => @other_sheet.id, :t => 1, :updated_at => Time.now + 100
+        @other_story_sheet3 = FactoryGirl.create :story_sheet, :author_id => @other_author.id, :story_id => @other_story.id, :sheet_id => @other_sheet.id, :t => 2, :updated_at => Time.now + 200
+        @other_story_sheet4 = FactoryGirl.create :story_sheet, :author_id => @other_author.id, :story_id => @other_story.id, :sheet_id => @other_sheet.id, :t => 3, :updated_at => Time.now + 300
+        @other_story_sheet5 = FactoryGirl.create :story_sheet, :author_id => @other_author.id, :story_id => @other_story.id, :sheet_id => @other_sheet.id, :t => 4, :updated_at => Time.now + 400
+        StorySheet.stub(:default_page_size).and_return(2)
+      end
+      it '通常は2件を返す' do
+        l = StorySheet.himlist @other_author, 1, 2
+        l.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        l = StorySheet.himlist @other_author, 1, 2
+        l.should eq [@other_story_sheet5, @other_story_sheet4]
+      end
+      it 'page=2なら中間2件を返す' do
+        l = StorySheet.himlist @other_author, 2, 2
+        l.should eq [@other_story_sheet3, @other_story_sheet2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        l = StorySheet.himlist @other_author, 3, 2
+        l.should eq [@other_story_sheet]
+      end
+    end
+  end
+  
+  describe 'スト紙一覧ページ制御に於いて' do
+    before do
+      StorySheet.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = StorySheet.list_paginate 
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it 'スト紙一覧の取得条件を利用している' do
+      StorySheet.stub(:list_where).with(any_args).and_return('')
+      StorySheet.should_receive(:list_where).with(any_args).exactly(1)
+      r = StorySheet.list_paginate 
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = StorySheet.list_paginate 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe '自分のスト紙一覧ページ制御に於いて' do
+    before do
+      StorySheet.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = StorySheet.mylist_paginate @author
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it '自分のスト紙一覧の取得条件を利用している' do
+      StorySheet.stub(:mylist_where).with(any_args).and_return('')
+      StorySheet.should_receive(:mylist_where).with(any_args).exactly(1)
+      r = StorySheet.mylist_paginate @author
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = StorySheet.mylist_paginate @author, 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe '他作家のスト紙一覧ページ制御に於いて' do
+    before do
+      StorySheet.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = StorySheet.himlist_paginate @other_author
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it '他作家のスト紙一覧の取得条件を利用している' do
+      StorySheet.stub(:himlist_where).with(any_args).and_return('')
+      StorySheet.should_receive(:himlist_where).with(any_args).exactly(1)
+      r = StorySheet.himlist_paginate @other_author
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = StorySheet.himlist_paginate @other_author, 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe 'list関連テーブルプションに於いて' do
+    it '3つの項目を含んでいる' do
+      r = StorySheet.list_opt
+      r.should have(3).items
+    end
+    it 'ストーリーを含んでいる' do
+      r = StorySheet.list_opt
+      r.has_key?(:story).should be_true
+    end
+      it 'ストーリーは作家を含んでいる' do
+        r = StorySheet.list_opt
+        r[:story].has_key?(:author).should be_true
+      end
+    it '作家を含んでいる' do
+      r = StorySheet.list_opt
+      r.has_key?(:author).should be_true
+    end
+    it '用紙を含んでいる' do
+      r = StorySheet.list_opt
+      r.has_key?(:sheet).should be_true
+    end
+      it '用紙は作家を含んでいる' do
+        r = StorySheet.list_opt
+        r[:sheet].has_key?(:author).should be_true
+      end
+  end
+  describe 'json一覧出力オプションに於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+    end
+    it 'ストーリーを含んでいる' do
+      r = StorySheet.list.to_json StorySheet.list_json_opt
+      j = JSON.parse r
+      i = j.first
+      i.has_key?('story').should be_true
+    end
+      it 'ストーリーは作家を含んでいる' do
+        r = StorySheet.list.to_json StorySheet.list_json_opt
+        j = JSON.parse r
+        i = j.first
+        s = i['story']
+        s.has_key?('author').should be_true
+      end
+    it '用紙を含んでいる' do
+      r = StorySheet.list.to_json StorySheet.list_json_opt
+      j = JSON.parse r
+      i = j.first
+      i.has_key?('sheet').should be_true
+    end
+      it '用紙は作家を含んでいる' do
+        r = StorySheet.list.to_json StorySheet.list_json_opt
+        j = JSON.parse r
+        i = j.first
+        s = i['sheet']
+        s.has_key?('author').should be_true
+      end
+    it '作家を含んでいる' do
+      r = StorySheet.list.to_json StorySheet.list_json_opt
+      j = JSON.parse r
+      i = j.first
+      i.has_key?('author').should be_true
+    end
+  end
+  
+  describe '単体取得に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+    end
+    context 'つつがなく終わるとき' do
+      it '単体取得オプションを利用している' do
+        StorySheet.stub(:show_opt).with(any_args).and_return({})
+        StorySheet.should_receive(:show_opt).with(any_args).exactly(1)
+        r = StorySheet.show @story_sheet.id, @author
+      end
+      it '閲覧許可を問い合わせている' do
+        StorySheet.any_instance.stub(:visible?).with(@author).and_return(true)
+        StorySheet.any_instance.should_receive(:visible?).with(@author).exactly(1)
+        r = StorySheet.show @story_sheet.id, @author
+      end
+    end
+    it '指定のスト紙を返す' do
+      l = StorySheet.show @story_sheet.id, @author
+      l.should eq @story_sheet
+    end
+    context '他人のスト紙を開こうとしたとき' do
+      it '403Forbidden例外を返す' do
+        StorySheet.any_instance.stub(:visible?).with(@other_author).and_return(false)
+        lambda{
+          StorySheet.show @story_sheet.id, @other_author
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+    context '存在しないスト紙を開こうとしたとき' do
+      it '404RecordNotFound例外を返す' do
+        lambda{
+          StorySheet.show 110, @author
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+    end
+  end
+  
+  describe '編集取得に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+    end
+    context 'つつがなく終わるとき' do
+      it '単体取得オプションを利用している' do
+        StorySheet.stub(:show_opt).with(any_args).and_return({})
+        StorySheet.should_receive(:show_opt).with(any_args).exactly(1)
+        r = StorySheet.edit @story_sheet.id, @author
+      end
+      it '所持判定を問い合わせている' do
+        StorySheet.any_instance.stub(:own?).with(any_args).and_return(true)
+        StorySheet.any_instance.should_receive(:own?).with(any_args).exactly(1)
+        r = StorySheet.edit @story_sheet.id, @author
+      end
+    end
+    it '指定のスト紙を返す' do
+      l = StorySheet.edit @story_sheet.id, @author
+      l.should eq @story_sheet
+    end
+    context '他人のスト紙を開こうとしたとき' do
+      it '403Forbidden例外を返す' do
+        StorySheet.any_instance.stub(:own?).and_return(false)
+        lambda{
+          StorySheet.edit @story_sheet.id, @author
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+    context '存在しないスト紙を開こうとしたとき' do
+      it '404RecordNotFound例外を返す' do
+        lambda{
+          StorySheet.edit 110, @author
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+    end
+  end
+  
+  describe '単体取得オプションに於いて' do
+    it 'includeキーを含んでいる' do
+      r = StorySheet.show_opt
+      r.has_key?(:include).should be_true
+    end
+    it '3つの項目を含んでいる' do
+      r = StorySheet.show_opt[:include]
+      r.should have(3).items
+    end
+    it 'ストーリーを含んでいる' do
+      r = StorySheet.show_opt[:include]
+      r.has_key?(:story).should be_true
+    end
+      it 'ストーリーは作家を含んでいる' do
+        r = StorySheet.show_opt[:include]
+        r[:story].has_key?(:author).should be_true
+      end
+    it '作家を含んでいる' do
+      r = StorySheet.show_opt[:include]
+      r.has_key?(:author).should be_true
+    end
+    it '用紙を含んでいる' do
+      r = StorySheet.show_opt[:include]
+      r.has_key?(:sheet).should be_true
+    end
+      it '用紙は作家を含んでいる' do
+        r = StorySheet.show_opt[:include]
+        r[:sheet].has_key?(:author).should be_true
+      end
+  end
+  describe 'json単体取得オプションに於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+    end
+    it 'ストーリーを含んでいる' do
+      r = StorySheet.show(@story_sheet.id, @author).to_json StorySheet.show_json_opt
+      j = JSON.parse r
+      i = j
+      i.has_key?('story').should be_true
+    end
+      it 'ストーリーは作家を含んでいる' do
+        r = StorySheet.show(@story_sheet.id, @author).to_json StorySheet.show_json_opt
+        j = JSON.parse r
+        i = j
+        s = i['story']
+        s.has_key?('author').should be_true
+      end
+    it '用紙を含んでいる' do
+      r = StorySheet.show(@story_sheet.id, @author).to_json StorySheet.show_json_opt
+      j = JSON.parse r
+      i = j
+      i.has_key?('sheet').should be_true
+    end
+      it '用紙は作家を含んでいる' do
+        r = StorySheet.show(@story_sheet.id, @author).to_json StorySheet.show_json_opt
+        j = JSON.parse r
+        i = j
+        s = i['sheet']
+        s.has_key?('author').should be_true
+      end
+    it '作家を含んでいる' do
+      r = StorySheet.show(@story_sheet.id, @author).to_json StorySheet.show_json_opt
+      j = JSON.parse r
+      i = j
+      i.has_key?('author').should be_true
+    end
+  end
+  
+  describe 't補充値に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+    end
+    
+    context 'ストーリー初の用紙なら' do
+      it '0を補充値とする' do
+        @story_sheet = FactoryGirl.build :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+        @story_sheet.t = nil
+        r = StorySheet.new_t @story_sheet.story_id
+        r.should eq 0
+      end
+    end
+    context 'ストーリーに一個用紙があるとき' do
+      it '1を補充値とする' do
+        FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id, :t => 0
+        @story_sheet = FactoryGirl.build :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+        @story_sheet.t = nil
+        r = StorySheet.new_t @story_sheet.story_id
+        r.should eq 1
+      end
+    end
+    context 'ストーリーに2個用紙があるとき' do
+      it '2を補充値とする' do
+        FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id, :t => 0
+        FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id, :t => 1
+        @story_sheet = FactoryGirl.build :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+        @story_sheet.t = nil
+        r = StorySheet.new_t @story_sheet.story_id
+        r.should eq 2
+      end
+    end
+  end
+  describe 'シリアライズチェックに於いて' do
+    context 'つつがなく終わるとき' do
+      it '0からシリアライズされているならTrueを返す' do
+        r = StorySheet.serial? [0, 1, 2]
+        r.should be_true
+      end
+      it '見た目はシリアライズされてなくてもソート結果が無事ならtrueを返す' do
+        r = StorySheet.serial? [0, 2, 1]
+        r.should be_true
+      end
+      it '見た目はシリアライズされてなくてもソート結果が無事ならtrueを返す' do
+        r = StorySheet.serial? [ 2, 1, 4, 3, 0]
+        r.should be_true
+      end
+    end
+    context '異常なとき' do
+      it '0から始まらないならFalseを返す' do
+        r = StorySheet.serial? [1, 2, 3]
+        r.should be_false
+      end
+      it '連続していないならFalseを返す' do
+        r = StorySheet.serial? [0, 1, 2, 4]
+        r.should be_false
+      end
+      it '連続していないならFalseを返す' do
+        r = StorySheet.serial? [0, 1, 2, 4, 5]
+        r.should be_false
+      end
+    end
+  end
+  describe 't収集に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+      @story2 = FactoryGirl.create :story, :author_id => @author.id
+      @c2story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story2.id, :sheet_id => @sheet.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it 'スト紙から同一ストーリーのtだけを収集している' do
+        r = StorySheet.collect_t @story_sheet
+        r.should eq [0]
+      end
+    end
+    context '複数用紙のとき' do
+      it 'スト紙から同一ストーリーのtだけを収集している' do
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        r = StorySheet.collect_t @story_sheet
+        r.sort.should eq [0, 1]
+      end
+    end
+    context '複数用紙でヨソのストーリーも混じっているとき' do
+      it 'スト紙から同一ストーリーのtだけを収集している' do
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        r = StorySheet.collect_t @story_sheet
+        r.sort.should eq [0, 1]
+      end
+    end
+  end
+  describe 'tチェックに於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.build :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it 't収集を依頼している' do
+        StorySheet.should_receive(:collect_t).with(any_args).exactly(1)
+        StorySheet.stub(:collect_t).with(any_args).and_return([])
+        StorySheet.stub(:serial?).with(any_args).and_return(true)
+        r = StorySheet.validate_t @story_sheet
+      end
+      it '収集したtをシリアライズチェック依頼している' do
+        StorySheet.stub(:collect_t).with(any_args).and_return([])
+        StorySheet.should_receive(:serial?).with(any_args).exactly(1)
+        StorySheet.stub(:serial?).with(any_args).and_return(true)
+        r = StorySheet.validate_t @story_sheet
+      end
+    end
+    #実データでチェック
+    #依頼チェックだけでは不安なので最低限のチェックを
+    context '新規のとき' do
+      it '一件だけで正常通過している' do
+        @story_sheet = FactoryGirl.build :story_sheet, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id, :t => 0
+        r = StorySheet.validate_t @story_sheet
+        r.should be_true 
+      end
+    end
+    context '既存のとき' do
+      it '2件目を作っても正常通過している' do
+        @story_sheet = FactoryGirl.create :story_sheet, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id, :t => 0
+        @story_sheet2 = FactoryGirl.build :story_sheet, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id, :t => 1
+        r = StorySheet.validate_t @story_sheet2
+        r.should be_true 
+      end
+    end
+  end
+  describe '挿入シフトに於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+    end
+    context '依頼チェック' do
+      #テーブルが空で0に挿入
+      it 'Updateを依頼している' do
+        StorySheet.stub(:update_all).with(any_args)
+        StorySheet.should_receive(:update_all).with(any_args).exactly(1)
+        @story_sheet = FactoryGirl.build :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet.insert_shift
+      end
+    end
+    context 'テーブルに1件(t:0)で0に挿入したとき' do
+      before do
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.build :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+      end
+      it '既存の行を1にシフトしている' do
+        @story_sheet2.insert_shift
+        l = StorySheet.find :all
+        l.first.t.should eq 1
+      end
+      it 'これから挿入するt(0)が欠番になっている' do
+        @story_sheet2.insert_shift
+        l = StorySheet.find(:all).map {|s| s.t }
+        l.include?(0).should_not be_true
+      end
+    end
+    context 'テーブルに2件(t:0,1)で1に挿入したとき' do
+      before do
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet3 = FactoryGirl.build :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+      end
+      it '既存のt1を2にシフトしてこれから挿入するt(1)が欠番になっている' do
+        @story_sheet3.insert_shift
+        l = StorySheet.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 2]
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)で2に挿入したとき' do
+      before do
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet3 = FactoryGirl.create :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet4 = FactoryGirl.create :story_sheet, :t => 3, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet5 = FactoryGirl.create :story_sheet, :t => 4, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet6 = FactoryGirl.build :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+      end
+      it '既存のt1を2にシフトしてこれから挿入するt(1)が欠番になっている' do
+        @story_sheet6.insert_shift
+        l = StorySheet.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 1, 3, 4, 5]
+      end
+    end
+    context '先ほどのケース+他のストーリー1件で挿入したとき' do
+      before do
+        @story2 = FactoryGirl.create :story, :author_id => @author.id
+        @story_sheetc2 = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story2.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet3 = FactoryGirl.create :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet4 = FactoryGirl.create :story_sheet, :t => 3, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet5 = FactoryGirl.create :story_sheet, :t => 4, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet6 = FactoryGirl.build :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+      end
+      it '既存のt1を2にシフトしてこれから挿入するt(1)が欠番になっている' do
+        @story_sheet6.insert_shift
+        l = StorySheet.find(:all, :conditions => ['story_id = ?', @story.id]).map {|s| s.t }
+        l.sort.should eq [0, 1, 3, 4, 5]
+      end
+      it '他のストーリーに影響がない' do
+        ot = @story_sheetc2.t
+        @story_sheet6.insert_shift
+        @story_sheetc2.reload
+        @story_sheetc2.t.should eq ot
+      end
+    end
+  end
+  describe '少ない方に移動に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+    end
+    context '依頼チェック' do
+      it 'Updateを依頼している' do
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        StorySheet.stub(:update_all).with(any_args)
+        StorySheet.should_receive(:update_all).with(any_args).exactly(1)
+        ot = @story_sheet2.t
+        @story_sheet2.t = 0
+        @story_sheet2.lesser_shift ot
+      end
+    end
+    context 'テーブルに2件(t:0,1)で1を0に移動したとき' do
+      before do
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @ot = @story_sheet2.t
+        @story_sheet2.t = 0
+      end
+      it '既存のt0を1にシフトしてこれから挿入するt(0)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story_sheet2.lesser_shift @ot
+        l = StorySheet.find(:all).map {|s| s.t }
+        l.sort.should eq [1, 1]
+      end
+      it '既存のt0を1にシフトしている' do
+        @story_sheet2.lesser_shift @ot
+        @story_sheet.reload
+        @story_sheet.t.should eq 1
+      end
+    end
+    context 'テーブルに3件(t:0,1,2)で2を1に移動したとき' do
+      before do
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet3 = FactoryGirl.create :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @ot = @story_sheet3.t
+        @story_sheet3.t = 1
+      end
+      it '既存のt1を2にシフトしてこれから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story_sheet3.lesser_shift @ot
+        l = StorySheet.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 2, 2]
+      end
+      it '既存のt1を2にシフトしている' do
+        @story_sheet3.lesser_shift @ot
+        @story_sheet2.reload
+        @story_sheet2.t.should eq 2
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)で3を1に移動したとき' do
+      before do
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet3 = FactoryGirl.create :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet4 = FactoryGirl.create :story_sheet, :t => 3, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet5 = FactoryGirl.create :story_sheet, :t => 4, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @ot = @story_sheet4.t
+        @story_sheet4.t = 1
+      end
+      it 'これから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story_sheet4.lesser_shift @ot
+        l = StorySheet.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 2, 3, 3, 4]
+      end
+      it '既存のt0には変化がない' do
+        @story_sheet4.lesser_shift @ot
+        @story_sheet.reload
+        @story_sheet.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @story_sheet4.lesser_shift @ot
+        @story_sheet5.reload
+        @story_sheet5.t.should eq 4
+      end
+      it '既存のt1を2にシフトしている' do
+        @story_sheet4.lesser_shift @ot
+        @story_sheet2.reload
+        @story_sheet2.t.should eq 2
+      end
+      it '既存のt2を3にシフトしている' do
+        @story_sheet4.lesser_shift @ot
+        @story_sheet3.reload
+        @story_sheet3.t.should eq 3
+      end
+    end
+    context '先ほどのケース+他のストーリー1件で挿入したとき' do
+      before do
+        @story2 = FactoryGirl.create :story, :author_id => @author.id
+        @story_sheetc2 = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story2.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet3 = FactoryGirl.create :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet4 = FactoryGirl.create :story_sheet, :t => 3, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet5 = FactoryGirl.create :story_sheet, :t => 4, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @ot = @story_sheet4.t
+        @story_sheet4.t = 1
+      end
+      it 'これから挿入するt(1)が欠番になっている' do
+        @story_sheet4.lesser_shift @ot
+        l = StorySheet.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0, 2, 3, 3, 4]
+      end
+      it '既存のt0には変化がない' do
+        @story_sheet4.lesser_shift @ot
+        @story_sheet.reload
+        @story_sheet.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @story_sheet4.lesser_shift @ot
+        @story_sheet5.reload
+        @story_sheet5.t.should eq 4
+      end
+      it '既存のt1を2にシフトしている' do
+        @story_sheet4.lesser_shift @ot
+        @story_sheet2.reload
+        @story_sheet2.t.should eq 2
+      end
+      it '既存のt2を3にシフトしている' do
+        @story_sheet4.lesser_shift @ot
+        @story_sheet3.reload
+        @story_sheet3.t.should eq 3
+      end
+      it '他のストーリーに影響がない' do
+        @story_sheet4.lesser_shift @ot
+        @story_sheetc2.reload
+        @story_sheetc2.t.should eq 0
+      end
+    end
+    #例外ケース。
+    #負のときは0として正常扱い
+    context 'テーブルに2件(t:0,1)で1を-1に移動したとき' do
+      before do
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @ot = @story_sheet2.t
+        @story_sheet2.t = -1
+      end
+      it '既存のt0を1にシフトしてこれから挿入するt(0)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story_sheet2.lesser_shift @ot
+        l = StorySheet.find(:all).map {|s| s.t }
+        l.sort.should eq [1, 1]
+      end
+      it '既存のt0を1にシフトしている' do
+        @story_sheet2.lesser_shift @ot
+        @story_sheet.reload
+        @story_sheet.t.should eq 1
+      end
+      it '既存のt1は0に補正されている' do
+        @story_sheet2.lesser_shift @ot
+        @story_sheet2.t.should eq 0
+      end
+    end
+  end
+  describe '大きい方に移動に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+    end
+    context '依頼チェック' do
+      it 'Updateを依頼している' do
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        StorySheet.stub(:update_all).with(any_args)
+        StorySheet.should_receive(:update_all).with(any_args).exactly(1)
+        ot = @story_sheet.t
+        @story_sheet.t = 1
+        @story_sheet.higher_shift ot
+      end
+    end
+    context 'テーブルに2件(t:0,1)で0を1に移動したとき' do
+      before do
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @ot = @story_sheet.t
+        @story_sheet.t = 1
+      end
+      it '既存のt1を0にシフトしてこれから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story_sheet.higher_shift @ot
+        l = StorySheet.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0]
+      end
+      it '既存のt1を0にシフトしている' do
+        @story_sheet.higher_shift @ot
+        @story_sheet2.reload
+        @story_sheet2.t.should eq 0
+      end
+    end
+    context 'テーブルに3件(t:0,1,2)で0を1に移動したとき' do
+      before do
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet3 = FactoryGirl.create :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @ot = @story_sheet.t
+        @story_sheet.t = 1
+      end
+      it '既存のt1を0にシフトしてこれから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story_sheet.higher_shift @ot
+        l = StorySheet.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0, 2]
+      end
+      it '既存のt1を0にシフトしている' do
+        @story_sheet.higher_shift @ot
+        @story_sheet2.reload
+        @story_sheet2.t.should eq 0
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)で1を3に移動したとき' do
+      before do
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet3 = FactoryGirl.create :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet4 = FactoryGirl.create :story_sheet, :t => 3, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet5 = FactoryGirl.create :story_sheet, :t => 4, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @ot = @story_sheet2.t
+        @story_sheet2.t = 3
+      end
+      it 'これから挿入するt(3)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story_sheet2.higher_shift @ot
+        l = StorySheet.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 1, 1, 2, 4]
+      end
+      it '既存のt0には変化がない' do
+        @story_sheet2.higher_shift @ot
+        @story_sheet.reload
+        @story_sheet.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @story_sheet2.higher_shift @ot
+        @story_sheet5.reload
+        @story_sheet5.t.should eq 4
+      end
+      it '既存のt2を1にシフトしている' do
+        @story_sheet2.higher_shift @ot
+        @story_sheet3.reload
+        @story_sheet3.t.should eq 1
+      end
+      it '既存のt3を2にシフトしている' do
+        @story_sheet2.higher_shift @ot
+        @story_sheet4.reload
+        @story_sheet4.t.should eq 2
+      end
+    end
+    context '先ほどのケース+他のストーリー1件で挿入したとき' do
+      before do
+        @story2 = FactoryGirl.create :story, :author_id => @author.id
+        @story_sheetc2 = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story2.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet3 = FactoryGirl.create :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet4 = FactoryGirl.create :story_sheet, :t => 3, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet5 = FactoryGirl.create :story_sheet, :t => 4, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @ot = @story_sheet2.t
+        @story_sheet2.t = 3
+      end
+      it 'これから挿入するt(3)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story_sheet2.higher_shift @ot
+        l = StorySheet.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0, 1, 1, 2, 4]
+      end
+      it '既存のt0には変化がない' do
+        @story_sheet2.higher_shift @ot
+        @story_sheet.reload
+        @story_sheet.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @story_sheet2.higher_shift @ot
+        @story_sheet5.reload
+        @story_sheet5.t.should eq 4
+      end
+      it '既存のt2を1にシフトしている' do
+        @story_sheet2.higher_shift @ot
+        @story_sheet3.reload
+        @story_sheet3.t.should eq 1
+      end
+      it '既存のt3を2にシフトしている' do
+        @story_sheet2.higher_shift @ot
+        @story_sheet4.reload
+        @story_sheet4.t.should eq 2
+      end
+      it '他のストーリーに影響がない' do
+        @story_sheet2.higher_shift @ot
+        @story_sheetc2.reload
+        @story_sheetc2.t.should eq 0
+      end
+    end
+    #例外ケース。
+    #max超えたときはmaxとして正常扱い
+    context 'テーブルに2件(t:0,1)で0を2に移動したとき' do
+      before do
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @ot = @story_sheet.t
+        @story_sheet.t = 2
+      end
+      it '既存のt1を0にシフトしてこれから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story_sheet.higher_shift @ot
+        l = StorySheet.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0]
+      end
+      it '既存のt1を0にシフトしている' do
+        @story_sheet.higher_shift @ot
+        @story_sheet2.reload
+        @story_sheet2.t.should eq 0
+      end
+      it '既存のt0は1に補正されている' do
+        @story_sheet.higher_shift @ot
+        @story_sheet.t.should eq 1
+      end
+    end
+  end
+  describe '入れ替えに於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+      @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+    end
+    context '新tが旧tより小さいとき' do
+      it '少ない方に移動を依頼している' do
+        StorySheet.any_instance.stub(:lesser_shift).with(any_args)
+        StorySheet.any_instance.should_receive(:lesser_shift).with(any_args).exactly(1)
+        ot = @story_sheet2.t
+        @story_sheet2.t = 0
+        @story_sheet2.update_shift ot
+      end
+    end
+    context '新tが旧tより大きいとき' do
+      it '大きい方に移動を依頼している' do
+        StorySheet.any_instance.stub(:higher_shift).with(any_args)
+        StorySheet.any_instance.should_receive(:higher_shift).with(any_args).exactly(1)
+        ot = @story_sheet.t
+        @story_sheet.t = 1
+        @story_sheet.update_shift ot
+      end
+    end
+  end
+  describe '順序入れ替えに於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+    end
+    context 'オブジェクトが新規でtが空のとき' do
+      it '末尾追加としてtを補充依頼している' do
+        @story_sheet = FactoryGirl.build :story_sheet, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        StorySheet.stub(:new_t).with(any_args).and_return(0)
+        StorySheet.should_receive(:new_t).with(any_args).exactly(1)
+        @story_sheet.t = nil
+        r = @story_sheet.rotate
+      end
+    end
+    context 'オブジェクトが新規でtが設定されているとき' do
+      it '挿入追加として挿入シフトを依頼している' do
+        @story_sheet = FactoryGirl.build :story_sheet, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        StorySheet.any_instance.stub(:insert_shift).with(any_args)
+        StorySheet.any_instance.should_receive(:insert_shift).with(any_args).exactly(1)
+        @story_sheet.t = 0
+        r = @story_sheet.rotate
+      end
+    end
+    context 'オブジェクトが新規でなくtが設定されているとき' do
+      it '用紙移動として入れ替えを依頼している' do
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        StorySheet.any_instance.stub(:update_shift).with(any_args)
+        StorySheet.any_instance.should_receive(:update_shift).with(1).exactly(1)
+        @story_sheet2.t = 0
+        r = @story_sheet.rotate 1
+      end
+    end
+    context 'オブジェクトが新規でなくtが空のとき' do
+      it '入れ替えもシフトもせず、tを空のままにしている' do
+        #結果、tに欠番が生じてシリアライズチェックでひっかかる
+      end
+    end
+  end
+  describe '編集許可に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.build :story_sheet, :t => nil, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it 'trueを返す' do
+        r = @story_sheet.allow?
+        r.should be_true
+      end
+    end
+    context 'ストーリーで引っかかるとき' do
+      it 'falseを返す' do
+        Sheet.any_instance.stub(:usable?).with(any_args).and_return(true)
+        Story.any_instance.stub(:own?).with(any_args).and_return(false)
+        r = @story_sheet.allow?
+        r.should be_false
+      end
+    end
+    context '用紙で引っかかるとき' do
+      it 'falseを返す' do
+        Story.any_instance.stub(:own?).with(any_args).and_return(true)
+        Sheet.any_instance.stub(:usable?).with(any_args).and_return(false)
+        r = @story_sheet.allow?
+        r.should be_false
+      end
+    end
+    context 'ストーリーまたは用紙が指定されていなかったとき' do
+      it 'nilを返す' do
+        Story.any_instance.stub(:own?).with(any_args).and_return(true)
+        @story_sheet.sheet_id = nil
+        r = @story_sheet.allow?
+        r.should eq nil
+      end
+      it 'nilを返す' do
+        Sheet.any_instance.stub(:usable?).with(any_args).and_return(true)
+        @story_sheet.story_id = nil
+        r = @story_sheet.allow?
+        r.should eq nil
+      end
+    end
+  end
+  describe '保存に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.build :story_sheet, :t => nil, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '編集許可チェックを依頼している' do
+        StorySheet.any_instance.stub(:allow?).with(any_args).and_return(true)
+        StorySheet.any_instance.should_receive(:allow?).with(any_args).exactly(1)
+        r = @story_sheet.store
+      end
+      it '順序入れ替えを依頼している' do
+        StorySheet.any_instance.stub(:rotate).with(any_args).and_return(0)
+        StorySheet.any_instance.should_receive(:rotate).with(any_args).exactly(1)
+        StorySheet.any_instance.stub(:save).with(any_args).and_return(true)
+        StorySheet.stub(:validate_t).with(any_args).and_return(true)
+        r = @story_sheet.store 
+      end
+      it '保存を依頼している' do
+        StorySheet.stub(:new_t).with(any_args).and_return(0)
+        StorySheet.any_instance.stub(:save).with(any_args).and_return(true)
+        StorySheet.any_instance.should_receive(:save).with(any_args).exactly(1)
+        StorySheet.stub(:validate_t).with(any_args).and_return(true)
+        r = @story_sheet.store
+      end
+      it 'tのシリアライズチェックを依頼している' do
+        StorySheet.stub(:new_t).with(any_args).and_return(0)
+        StorySheet.any_instance.stub(:save).with(any_args).and_return(true)
+        StorySheet.stub(:validate_t).with(any_args).and_return(true)
+        StorySheet.should_receive(:validate_t).with(any_args).exactly(1)
+        r = @story_sheet.store
+      end
+    end
+    #入れ替えテストと同じテストを実施。こちらはシフトだけでなく本尊も更新されている
+    context 'テーブルに5件(t:0,1,2,3,4)+他のストーリー1件で2に挿入したとき' do
+      before do
+        @story2 = FactoryGirl.create :story, :author_id => @author.id
+        @story_sheetc2 = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story2.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet3 = FactoryGirl.create :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet4 = FactoryGirl.create :story_sheet, :t => 3, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet5 = FactoryGirl.create :story_sheet, :t => 4, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet6 = FactoryGirl.build :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+      end
+      it '既存のt0には変化がない' do
+        @story_sheet6.store
+        @story_sheet.reload
+        @story_sheet.t.should eq 0
+      end
+      it '既存のt1には変化がない' do
+        @story_sheet6.store
+        @story_sheet2.reload
+        @story_sheet2.t.should eq 1
+      end
+      it '既存のt2を3にシフトしている' do
+        @story_sheet6.store
+        @story_sheet3.reload
+        @story_sheet3.t.should eq 3
+      end
+      it '既存のt3を4にシフトしている' do
+        @story_sheet6.store
+        @story_sheet4.reload
+        @story_sheet4.t.should eq 4
+      end
+      it '既存のt5を5にシフトしている' do
+        @story_sheet6.store
+        @story_sheet5.reload
+        @story_sheet5.t.should eq 5
+      end
+      it '新規のt2が作成されている' do
+        @story_sheet6.store
+        @story_sheet6.reload
+        @story_sheet6.t.should eq 2
+      end
+      it '他のストーリーに影響がない' do
+        @ot = @story_sheetc2.t
+        @story_sheet6.store
+        @story_sheetc2.reload
+        @story_sheetc2.t.should eq @ot
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)+他のストーリー1件で3を1に移動したとき' do
+      before do
+        @story2 = FactoryGirl.create :story, :author_id => @author.id
+        @story_sheetc2 = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story2.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet3 = FactoryGirl.create :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet4 = FactoryGirl.create :story_sheet, :t => 3, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet5 = FactoryGirl.create :story_sheet, :t => 4, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @ot = @story_sheet4.t
+        @story_sheet4.t = 1
+      end
+      it '既存のt0には変化がない' do
+        @story_sheet4.store @ot
+        @story_sheet.reload
+        @story_sheet.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @story_sheet4.store @ot
+        @story_sheet5.reload
+        @story_sheet5.t.should eq 4
+      end
+      it '既存のt1を2にシフトしている' do
+        @story_sheet4.store @ot
+        @story_sheet2.reload
+        @story_sheet2.t.should eq 2
+      end
+      it '既存のt2を3にシフトしている' do
+        @story_sheet4.store @ot
+        @story_sheet3.reload
+        @story_sheet3.t.should eq 3
+      end
+      it '既存のt3を1にシフトしている' do
+        @story_sheet4.store @ot
+        @story_sheet4.reload
+        @story_sheet4.t.should eq 1
+      end
+      it '他のストーリーに影響がない' do
+        @story_sheet4.store @ot
+        @story_sheetc2.reload
+        @story_sheetc2.t.should eq 0
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)+他のストーリー1件で1を3に移動したとき' do
+      before do
+        @story2 = FactoryGirl.create :story, :author_id => @author.id
+        @story_sheetc2 = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story2.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet3 = FactoryGirl.create :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet4 = FactoryGirl.create :story_sheet, :t => 3, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet5 = FactoryGirl.create :story_sheet, :t => 4, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @ot = @story_sheet2.t
+        @story_sheet2.t = 3
+      end
+      it '既存のt0には変化がない' do
+        @story_sheet2.store @ot
+        @story_sheet.reload
+        @story_sheet.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @story_sheet2.store @ot
+        @story_sheet5.reload
+        @story_sheet5.t.should eq 4
+      end
+      it '既存のt1を3にシフトしている' do
+        @story_sheet2.store @ot
+        @story_sheet2.reload
+        @story_sheet2.t.should eq 3
+      end
+      it '既存のt2を1にシフトしている' do
+        @story_sheet2.store @ot
+        @story_sheet3.reload
+        @story_sheet3.t.should eq 1
+      end
+      it '既存のt3を2にシフトしている' do
+        @story_sheet2.store @ot
+        @story_sheet4.reload
+        @story_sheet4.t.should eq 2
+      end
+      it '他のストーリーに影響がない' do
+        @story_sheet2.store @ot
+        @story_sheetc2.reload
+        @story_sheetc2.t.should eq 0
+      end
+    end
+    #ロールバックテスト。入れ替えが直接DBをいじるので、すべてのケースで確実にロールバックを確認する
+    context 'テーブルに5件(t:0,1,2,3,4)+他のストーリー1件で2に挿入したが保存に失敗したとき' do
+      before do
+        StorySheet.any_instance.stub(:save).with(any_args).and_return(false)
+        @story2 = FactoryGirl.create :story, :author_id => @author.id
+        @story_sheetc2 = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story2.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet3 = FactoryGirl.create :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet4 = FactoryGirl.create :story_sheet, :t => 3, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet5 = FactoryGirl.create :story_sheet, :t => 4, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet6 = FactoryGirl.build :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+      end
+      it '既存のtに変化がない' do
+        @story_sheet6.store
+        @story_sheet.reload
+        @story_sheet.t.should eq 0
+        @story_sheet2.reload
+        @story_sheet2.t.should eq 1
+        @story_sheet3.reload
+        @story_sheet3.t.should eq 2
+        @story_sheet4.reload
+        @story_sheet4.t.should eq 3
+        @story_sheet5.reload
+        @story_sheet5.t.should eq 4
+        @story_sheetc2.reload
+        @story_sheetc2.t.should eq 0
+      end
+      it 'falseを返す' do
+        r = @story_sheet6.store
+        r.should be_false
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)+他のストーリー1件で3を1に移動したがシリアルチェックに失敗したとき' do
+      before do
+        StorySheet.stub(:validate_t).with(any_args).and_return(false)
+        @story2 = FactoryGirl.create :story, :author_id => @author.id
+        @story_sheetc2 = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story2.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet3 = FactoryGirl.create :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet4 = FactoryGirl.create :story_sheet, :t => 3, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet5 = FactoryGirl.create :story_sheet, :t => 4, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @ot = @story_sheet4.t
+        @story_sheet4.t = 1
+      end
+      it '既存のtに変化がない' do
+        @story_sheet4.store @ot
+        @story_sheet.reload
+        @story_sheet.t.should eq 0
+        @story_sheet2.reload
+        @story_sheet2.t.should eq 1
+        @story_sheet3.reload
+        @story_sheet3.t.should eq 2
+        @story_sheet4.reload
+        @story_sheet4.t.should eq 3
+        @story_sheet5.reload
+        @story_sheet5.t.should eq 4
+        @story_sheetc2.reload
+        @story_sheetc2.t.should eq 0
+      end
+      it 'falseを返す' do
+        r = @story_sheet4.store @ot
+        r.should be_false
+      end
+      it 'tにエラーメッセージが入っている' do
+        @story_sheet4.store @ot
+        @story_sheet4.errors[:t].should_not be_empty
+        @story_sheet4.valid?.should be_true
+      end
+    end
+    context '編集不可だったとき' do
+      before do
+        @story_sheet = FactoryGirl.build :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        StorySheet.any_instance.stub(:allow?).and_return(false)
+      end
+      it '403Forbidden例外を返す' do
+        lambda{
+          @story_sheet.store
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+  end
+  describe '切り詰め処理つき削除に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.create :story_sheet, :t => 0, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '削除される' do
+        lambda{
+          @story_sheet.destroy_and_shorten
+        }.should change(StorySheet, :count ).by(-1)
+      end
+      it 'Trueを返す' do
+        r = @story_sheet.destroy_and_shorten
+        r.should be_true 
+      end
+    end
+    context '削除に失敗したとき' do
+      before do
+        StorySheet.any_instance.stub(:destroy).and_return(false)
+      end
+      it 'ロールバックされる' do
+        lambda{
+          @story_sheet.destroy_and_shorten
+        }.should_not change(StorySheet, :count )
+      end
+      it 'Falseを返す' do
+        r = @story_sheet.destroy_and_shorten
+        r.should be_false
+      end
+    end
+    #連携テスト。切り詰めが直接DBをいじる
+    context '2件で先頭を削除したとき' do
+      before do
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+      end
+      it '行が削除される' do
+        lambda{
+          @story_sheet.destroy_and_shorten
+        }.should change(StorySheet, :count ).by(-1)
+      end
+      it '先頭は削除される' do
+        @story_sheet.destroy_and_shorten
+        lambda{
+          StorySheet.find @story_sheet.id
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+      it '2件目は前に詰められる' do
+        @story_sheet.destroy_and_shorten
+        @story_sheet2.reload
+        @story_sheet2.t.should eq 0
+      end
+    end
+    context '3件で先頭を削除したとき' do
+      before do
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet3 = FactoryGirl.create :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+      end
+      it '行が削除される' do
+        lambda{
+          @story_sheet.destroy_and_shorten
+        }.should change(StorySheet, :count ).by(-1)
+      end
+      it '先頭は削除される' do
+        @story_sheet.destroy_and_shorten
+        lambda{
+          StorySheet.find @story_sheet.id
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+      it '2件目は前に詰められる' do
+        @story_sheet.destroy_and_shorten
+        @story_sheet2.reload
+        @story_sheet2.t.should eq 0
+      end
+      it '3件目は前に詰められる' do
+        @story_sheet.destroy_and_shorten
+        @story_sheet3.reload
+        @story_sheet3.t.should eq 1
+      end
+    end
+    context '5件で3件目を削除したとき' do
+      before do
+        @story_sheet2 = FactoryGirl.create :story_sheet, :t => 1, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet3 = FactoryGirl.create :story_sheet, :t => 2, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet4 = FactoryGirl.create :story_sheet, :t => 3, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+        @story_sheet5 = FactoryGirl.create :story_sheet, :t => 4, :story_id => @story.id, :sheet_id => @sheet.id, :author_id => @author.id
+      end
+      it '行が削除される' do
+        lambda{
+          @story_sheet3.destroy_and_shorten
+        }.should change(StorySheet, :count ).by(-1)
+      end
+      it '1件目は変化がない' do
+        @story_sheet3.destroy_and_shorten
+        @story_sheet.reload
+        @story_sheet.t.should eq 0
+      end
+      it '2件目は変化がない' do
+        @story_sheet3.destroy_and_shorten
+        @story_sheet2.reload
+        @story_sheet2.t.should eq 1
+      end
+      it '3件目は削除される' do
+        @story_sheet3.destroy_and_shorten
+        lambda{
+          StorySheet.find @story_sheet3.id
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+      it '4件目は前に詰められる' do
+        @story_sheet3.destroy_and_shorten
+        @story_sheet4.reload
+        @story_sheet4.t.should eq 2
+      end
+      it '5件目は前に詰められる' do
+        @story_sheet3.destroy_and_shorten
+        @story_sheet5.reload
+        @story_sheet5.t.should eq 3
+      end
+    end
+    #ロールバックテスト。切り詰めが直接DBをいじるので、すべてのケースで確実にロールバックを確認する
+  end
+end
diff --git a/spec/models/story_spec.rb b/spec/models/story_spec.rb
new file mode 100644 (file)
index 0000000..de0452b
--- /dev/null
@@ -0,0 +1,1719 @@
+# -*- encoding: utf-8 -*-
+require 'spec_helper'
+#ストーリー
+
+describe Story do
+  before do
+    @admin = FactoryGirl.create :admin
+    @demand_user = FactoryGirl.create :demand_user
+    @sp = FactoryGirl.create :system_picture
+    @lg = FactoryGirl.create :license_group
+    @license = FactoryGirl.create :license, :license_group_id => @lg.id, :system_picture_id => @sp.id
+    @user = FactoryGirl.create( :user_yas)
+    @author = FactoryGirl.create :author, :user_id => @user.id
+    @artist = FactoryGirl.create :artist_yas, :author_id => @author.id
+    @other_user = FactoryGirl.create( :user_yas)
+    @other_author = FactoryGirl.create :author, :user_id => @other_user.id
+    @other_artist = FactoryGirl.create :artist_yas, :author_id => @other_author.id
+  end
+  
+  describe '検証に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.build :story, :comic_id => @comic.id, :author_id => @author.id
+    end
+    
+    context 'オーソドックスなデータのとき' do
+      it '下限データが通る' do
+        @story.title = 'a'
+        @story.visible = 0
+        @story.t = 0
+        @story.should be_valid
+      end
+      it '上限データが通る' do
+        @story.title = 'a'*100
+        @story.visible = 1
+        @story.t = 99999
+        @story.should be_valid
+      end
+    end
+    
+    context 'titleを検証するとき' do
+      it 'nullなら失敗する' do
+        @story.title = nil
+        @story.should_not be_valid
+      end
+      it '100文字以上なら失敗する' do
+        @story.title = 'a'*101
+        @story.should_not be_valid
+      end
+    end
+    context 'visibleを検証するとき' do
+      it 'nullなら失敗する' do
+        @story.visible = nil
+        @story.should_not be_valid
+      end
+      it '負なら失敗する' do
+        @story.visible = -1
+        @story.should_not be_valid
+      end
+      it '2以上なら失敗する' do
+        @story.visible = 2
+        @story.should_not be_valid
+      end
+    end
+    context 'tを検証するとき' do
+      it 'nullなら失敗する' do
+        @story.t = nil
+        @story.should_not be_valid
+      end
+      it '数値でなければ失敗する' do
+        @story.t = 'a'
+        @story.should_not be_valid
+      end
+      it '負なら失敗する' do
+        @story.t = -1
+        @story.should_not be_valid
+      end
+    end
+  end
+  
+  describe '文字コード検証に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.build :story, :comic_id => @comic.id, :author_id => @author.id
+    end
+    
+    context 'titleを検証するとき' do
+      it 'Shift JISなら失敗する' do
+        @story.title = "\x83G\x83r\x83]\x83D"
+        lambda{
+          @story.valid_encode
+        }.should raise_error(Pettanr::BadRequest)
+      end
+    end
+    
+    context 'descriptionを検証するとき' do
+      it 'Shift JISなら失敗する' do
+        @story.description = "\x83G\x83r\x83]\x83D"
+        lambda{
+          @story.valid_encode
+        }.should raise_error(Pettanr::BadRequest)
+      end
+    end
+  end
+  
+  describe 'デフォルト値補充に於いて' do
+    it 'visibleが0になっている' do
+      @story = FactoryGirl.build :story, :visible => nil
+      @story.supply_default
+      @story.visible.should eq 0
+    end
+  end
+  
+  describe '上書き補充に於いて' do
+    it '作家idが設定されている' do
+      @story = FactoryGirl.build :story, :author_id => nil
+      @story.overwrite @author
+      @story.author_id.should eq @author.id
+    end
+  end
+  
+  describe '所持判定に於いて' do
+    before do
+      @story = FactoryGirl.build :story, :author_id => @author.id
+    end
+    context '事前チェックする' do
+      it '自身にロールリストからの作家取得を依頼している' do
+        Story.should_receive(:get_author_from_roles).with(any_args).exactly(1)
+        r = @story.own?([@author])
+      end
+    end
+    context 'ロール内作家が取得できるとき' do
+      before do
+      end
+      it 'ロール内作家のidが自身の作家idと一致するなら許可する' do
+        Story.stub(:get_author_from_roles).with(any_args).and_return(@author)
+        r = @story.own?([@author])
+        r.should be_true
+      end
+      it 'ロール内作家のidが自身の作家idと一致しないならno' do
+        Story.stub(:get_author_from_roles).with(any_args).and_return(@other_author)
+        @story.own?(@other_author).should be_false
+      end
+    end
+    context 'ロール内作家が取得できないとき' do
+      before do
+        Story.stub(:get_author_from_roles).with(any_args).and_return(nil)
+      end
+      it 'Falseを返す' do
+        r = @story.own?([@author])
+        r.should be_false
+      end
+    end
+  end
+  
+  describe '閲覧許可に於いて' do
+    before do
+      @story = FactoryGirl.build :story, :author_id => @author.id
+    end
+    context 'オープンモードのとき' do
+      before do
+        MagicNumber['run_mode'] = 0
+      end
+      it '自身にゲスト用ロールチェックを問い合わせしている' do
+        Story.any_instance.stub(:guest_role_check).and_return(true)
+        Story.any_instance.should_receive(:guest_role_check).with(any_args).exactly(1)
+        r = @story.visible?([@author])
+      end
+      it 'ゲスト用ロールチェックが失敗したとき、falseを返す' do
+        Story.any_instance.stub(:guest_role_check).and_return(false)
+        r = @story.visible?([@author])
+        r.should be_false
+      end
+    end
+    context 'クローズドモードのとき' do
+      before do
+        MagicNumber['run_mode'] = 1
+      end
+      it '自身に読者用ロールチェックを問い合わせしている' do
+        Story.any_instance.stub(:reader_role_check).and_return(true)
+        Story.any_instance.should_receive(:reader_role_check).with(any_args).exactly(1)
+        r = @story.visible?([@author])
+      end
+      it '読者用ロールチェックが失敗したとき、falseを返す' do
+        Story.any_instance.stub(:reader_role_check).and_return(false)
+        r = @story.visible?([@author])
+        r.should be_false
+      end
+    end
+    context '事前チェックする' do
+      before do
+        MagicNumber['run_mode'] = 1
+        Story.any_instance.stub(:reader_role_check).and_return(true)
+        Story.any_instance.stub(:own?).and_return(true)
+      end
+      it '自身に所持判定を問い合わせしている' do
+        Story.any_instance.should_receive(:own?).with(any_args).exactly(1)
+        r = @story.visible?([@author])
+      end
+    end
+    context 'つつがなく終わるとき' do
+      before do
+        MagicNumber['run_mode'] = 1
+        Story.any_instance.stub(:reader_role_check).and_return(true)
+      end
+      it '自分のストーリーなら許可する' do
+        Story.any_instance.stub(:own?).and_return(true)
+        Story.any_instance.stub(:visible).and_return(0)
+        r = @story.visible?([@author])
+        r.should be_true
+      end
+      it '他人の非公開ストーリーなら許可しない' do
+        Story.any_instance.stub(:own?).and_return(false)
+        Story.any_instance.stub(:visible).and_return(0)
+        r = @story.visible?([@author])
+        r.should be_false
+      end
+      it '他人のストーリーでも公開なら許可する' do
+        Story.any_instance.stub(:own?).and_return(false)
+        Story.any_instance.stub(:visible).and_return(1)
+        r = @story.visible?([@author])
+        r.should be_true
+      end
+    end
+  end
+  
+  describe '一覧取得に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id
+    end
+    context 'page補正について' do
+      it '文字列から数値に変換される' do
+        Story.page('8').should eq 8
+      end
+      it 'nilの場合は1になる' do
+        Story.page().should eq 1
+      end
+      it '0以下の場合は1になる' do
+        Story.page('0').should eq 1
+      end
+    end
+    context 'page_size補正について' do
+      it '文字列から数値に変換される' do
+        Story.page_size('7').should eq 7
+      end
+      it 'nilの場合はStory.default_page_sizeになる' do
+        Story.page_size().should eq Story.default_page_size
+      end
+      it '0以下の場合はStory.default_page_sizeになる' do
+        Story.page_size('0').should eq Story.default_page_size
+      end
+      it 'Story.max_page_sizeを超えた場合はStory.max_page_sizeになる' do
+        Story.page_size('1000').should eq Story.max_page_size
+      end
+    end
+    context 'つつがなく終わるとき' do
+      it '一覧取得オプションを利用している' do
+        Story.stub(:list_opt).with(any_args).and_return({})
+        Story.should_receive(:list_opt).with(any_args).exactly(1)
+        r = Story.list
+      end
+    end
+    it 'リストを返す' do
+      c = Story.list
+      c.should eq [@story]
+    end
+    it '非公開ストーリーは(自分のストーリーであっても)含んでいない' do
+      FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 0
+      c = Story.list
+      c.should eq [@story]
+    end
+    it '時系列で並んでいる' do
+      #公開ストーリーは(他人のストーリーであっても)含んでいる
+      other_comic = FactoryGirl.create :comic, :author_id => @other_author.id
+      v = FactoryGirl.create :story, :comic_id => other_comic.id, :author_id => @other_author.id, :updated_at => Time.now + 100
+      c = Story.list
+      c.should eq [v, @story]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @story2 = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :updated_at => Time.now + 100
+        @story3 = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :updated_at => Time.now + 200
+        @story4 = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :updated_at => Time.now + 300
+        @story5 = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :updated_at => Time.now + 400
+        Story.stub(:default_page_size).and_return(2)
+      end
+      it '通常は2件を返す' do
+        c = Story.list
+        c.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        c = Story.list(1)
+        c.should eq [@story5, @story4]
+      end
+      it 'page=2なら中間2件を返す' do
+        c = Story.list(2)
+        c.should eq [@story3, @story2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        c = Story.list(3)
+        c.should eq [@story]
+      end
+    end
+  end
+  
+  describe '自分のストーリー一覧取得に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '一覧取得オプションを利用している' do
+        Story.stub(:list_opt).with(any_args).and_return({})
+        Story.should_receive(:list_opt).with(any_args).exactly(1)
+        r = Story.mylist @author
+      end
+    end
+    it 'リストを返す' do
+      c = Story.mylist @author
+      c.should eq [@story]
+    end
+    it '時系列で並んでいる' do
+      nc = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :updated_at => Time.now + 100
+      cl = Story.mylist @author
+      cl.should eq [nc, @story]
+    end
+    it '他人のストーリーは公開でも含まない' do
+      other_comic = FactoryGirl.create :comic, :author_id => @other_author.id
+      nc = FactoryGirl.create :story, :comic_id => other_comic.id, :author_id => @other_author.id, :visible => 1
+      cl = Story.mylist @author
+      cl.should eq [@story]
+    end
+    it '自分のストーリーは非公開でも含んでいる' do
+      nc = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 0, :updated_at => Time.now + 100
+      cl = Story.mylist @author
+      cl.should eq [nc, @story]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @story2 = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :updated_at => Time.now + 100
+        @story3 = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :updated_at => Time.now + 200
+        @story4 = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :updated_at => Time.now + 300
+        @story5 = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :updated_at => Time.now + 400
+      end
+      it '通常は2件を返す' do
+        c = Story.mylist @author, 1, 2
+        c.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        c = Story.mylist(@author, 1, 2)
+        c.should eq [@story5, @story4]
+      end
+      it 'page=2なら中間2件を返す' do
+        c = Story.mylist(@author, 2, 2)
+        c.should eq [@story3, @story2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        c = Story.mylist(@author, 3, 2)
+        c.should eq [@story]
+      end
+    end
+  end
+  
+  describe '他作家のストーリー一覧取得に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id
+      @other_comic = FactoryGirl.create :comic, :author_id => @other_author.id
+      @other_story = FactoryGirl.create :story, :author_id => @other_author.id, :visible => 1
+    end
+    context 'つつがなく終わるとき' do
+      it '一覧取得オプションを利用している' do
+        Story.stub(:list_opt).with(any_args).and_return({})
+        Story.should_receive(:list_opt).with(any_args).exactly(1)
+        r = Story.himlist @other_author
+      end
+    end
+    it '指定した作家のリストを返す' do
+      r = Story.himlist @other_author
+      r.should eq [@other_story]
+    end
+    it '時系列で並んでいる' do
+      nc = FactoryGirl.create :story, :comic_id => @other_comic.id, :author_id => @other_author.id, :updated_at => Time.now + 100
+      r = Story.himlist @other_author
+      r.should eq [nc, @other_story]
+    end
+    it '公開ストーリーに限る ' do
+      hidden = FactoryGirl.create :story, :comic_id => @other_comic.id, :author_id => @other_author.id, :visible => 0
+      r = Story.himlist @other_author
+      r.should eq [@other_story]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @other_story2 = FactoryGirl.create :story, :comic_id => @other_comic.id, :author_id => @other_author.id, :updated_at => Time.now + 100
+        @other_story3 = FactoryGirl.create :story, :comic_id => @other_comic.id, :author_id => @other_author.id, :updated_at => Time.now + 200
+        @other_story4 = FactoryGirl.create :story, :comic_id => @other_comic.id, :author_id => @other_author.id, :updated_at => Time.now + 300
+        @other_story5 = FactoryGirl.create :story, :comic_id => @other_comic.id, :author_id => @other_author.id, :updated_at => Time.now + 400
+      end
+      it '通常は2件を返す' do
+        c = Story.himlist @other_author, 1, 2
+        c.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        c = Story.himlist(@other_author, 1, 2)
+        c.should eq [@other_story5, @other_story4]
+      end
+      it 'page=2なら中間2件を返す' do
+        c = Story.himlist(@other_author, 2, 2)
+        c.should eq [@other_story3, @other_story2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        c = Story.himlist(@other_author, 3, 2)
+        c.should eq [@other_story]
+      end
+    end
+  end
+  
+  describe 'ストーリー一覧ページ制御に於いて' do
+    before do
+      Story.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = Story.list_paginate 
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it 'ストーリー一覧の取得条件を利用している' do
+      Story.stub(:list_where).with(any_args).and_return('')
+      Story.should_receive(:list_where).with(any_args).exactly(1)
+      r = Story.list_paginate 
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = Story.list_paginate 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe '自分のストーリー一覧ページ制御に於いて' do
+    before do
+      Story.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = Story.mylist_paginate @author
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it '自分のストーリー一覧の取得条件を利用している' do
+      Story.stub(:mylist_where).with(any_args).and_return('')
+      Story.should_receive(:mylist_where).with(any_args).exactly(1)
+      r = Story.mylist_paginate @author
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = Story.mylist_paginate @author, 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe '他作家のストーリー一覧ページ制御に於いて' do
+    before do
+      Story.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = Story.himlist_paginate @other_author
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it '他作家のストーリー一覧の取得条件を利用している' do
+      Story.stub(:himlist_where).with(any_args).and_return('')
+      Story.should_receive(:himlist_where).with(any_args).exactly(1)
+      r = Story.himlist_paginate @other_author
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = Story.himlist_paginate @other_author, 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe '一覧取得オプションに於いて' do
+    it '2つの項目を含んでいる' do
+      r = Story.list_opt
+      r.should have(2).items
+    end
+    it 'スト紙を含んでいる' do
+      r = Story.list_opt
+      r.has_key?(:story_sheets).should be_true
+    end
+      it 'スト紙は用紙を含んでいる' do
+        r = Story.list_opt
+        r[:story_sheets].has_key?(:sheet).should be_true
+      end
+      it 'スト紙は作家を含んでいる' do
+        r = Story.list_opt
+        r[:story_sheets].has_key?(:author).should be_true
+      end
+    it '作家を含んでいる' do
+      r = Story.list_opt
+      r.has_key?(:author).should be_true
+    end
+  end
+  describe 'json一覧出力オプションに於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+    end
+    it 'スト紙を含んでいる' do
+      r = Story.list.to_json Story.list_json_opt
+      j = JSON.parse r
+      i = j.first
+      i.has_key?('story_sheets').should be_true
+    end
+      it 'スト紙は用紙を含んでいる' do
+        r = Story.list.to_json Story.list_json_opt
+        j = JSON.parse r
+        i = j.first
+        s = i['story_sheets'].first
+        s.has_key?('sheet').should be_true
+      end
+      it 'スト紙は作家を含んでいる' do
+        r = Story.list.to_json Story.list_json_opt
+        j = JSON.parse r
+        i = j.first
+        s = i['story_sheets'].first
+        s.has_key?('author').should be_true
+      end
+    it '作家を含んでいる' do
+      r = Story.list.to_json Story.list_json_opt
+      j = JSON.parse r
+      i = j.first
+      i.has_key?('author').should be_true
+    end
+  end
+  
+  describe '単体取得に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '単体取得オプションを利用している' do
+        Story.stub(:show_opt).with(any_args).and_return({})
+        Story.should_receive(:show_opt).with(any_args).exactly(1)
+        r = Story.show @story.id, @author
+      end
+      it '閲覧許可を問い合わせている' do
+        Story.any_instance.stub(:visible?).with(any_args).and_return(true)
+        Story.any_instance.should_receive(:visible?).with(any_args).exactly(1)
+        r = Story.show @story.id, @author
+      end
+    end
+    it '指定のストーリーを返す' do
+      c = Story.show @story.id, @author
+      c.should eq @story
+    end
+    context '閲覧許可が出なかったとき' do
+      it '403Forbidden例外を返す' do
+        Story.any_instance.stub(:visible?).and_return(false)
+        lambda{
+          Story.show @story.id, @author
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+    context '存在しないストーリーを開こうとしたとき' do
+      it '404RecordNotFound例外を返す' do
+        lambda{
+          Story.show 110, @author
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+    end
+  end
+  
+  describe '編集取得に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '単体取得オプションを利用している' do
+        Story.stub(:show_opt).with(any_args).and_return({})
+        Story.should_receive(:show_opt).with(any_args).exactly(1)
+        r = Story.edit @story.id, @author
+      end
+      it '所持判定を問い合わせている' do
+        Story.any_instance.stub(:own?).with(any_args).and_return(true)
+        Story.any_instance.should_receive(:own?).with(any_args).exactly(1)
+        r = Story.edit @story.id, @author
+      end
+    end
+    it '指定のストーリーを返す' do
+      Story.any_instance.stub(:own?).and_return(true)
+      c = Story.edit @story.id, @author.id
+      c.should eq @story
+    end
+    context '他人のストーリーを開こうとしたとき' do
+      it '403Forbidden例外を返す' do
+        Story.any_instance.stub(:own?).and_return(false)
+        lambda{
+          Story.edit @story.id, @author
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+    context '存在しないストーリーを開こうとしたとき' do
+      it '404RecordNotFound例外を返す' do
+        lambda{
+          Story.edit 110, @author
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+    end
+  end
+  describe '単体取得オプションに於いて' do
+    it 'includeキーを含んでいる' do
+      r = Story.show_opt
+      r.has_key?(:include).should be_true
+    end
+    it '2つの項目を含んでいる' do
+      r = Story.show_opt[:include]
+      r.should have(2).items
+    end
+    it '作家を含んでいる' do
+      r = Story.show_opt[:include]
+      r.has_key?(:author).should be_true
+    end
+    it 'スト紙を含んでいる' do
+      r = Story.show_opt[:include]
+      r.has_key?(:story_sheets).should be_true
+    end
+      it 'スト紙は用紙を含んでいる' do
+        r = Story.show_opt[:include]
+        r[:story_sheets].has_key?(:sheet).should be_true
+      end
+      it 'スト紙は作家を含んでいる' do
+        r = Story.show_opt[:include]
+        r[:story_sheets].has_key?(:author).should be_true
+      end
+  end
+  describe 'json単体出力オプションに於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :visible => 1
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+    end
+    it 'スト紙を含んでいる' do
+      r = Story.show(@story.id, @author).to_json Story.show_json_opt
+      j = JSON.parse r
+      i = j
+      i.has_key?('story_sheets').should be_true
+    end
+      it 'スト紙は用紙を含んでいる' do
+        r = Story.show(@story.id, @author).to_json Story.show_json_opt
+        j = JSON.parse r
+        i = j
+        s = i['story_sheets'].first
+        s.has_key?('sheet').should be_true
+      end
+      it 'スト紙は作家を含んでいる' do
+        r = Story.show(@story.id, @author).to_json Story.show_json_opt
+        j = JSON.parse r
+        i = j
+        s = i['story_sheets'].first
+        s.has_key?('author').should be_true
+      end
+    it '作家を含んでいる' do
+      r = Story.show(@story.id, @author).to_json Story.show_json_opt
+      j = JSON.parse r
+      i = j
+      i.has_key?('author').should be_true
+    end
+  end
+  
+  describe 't補充値に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+    end
+    
+    context 'コミック初のコマなら' do
+      it '0を補充値とする' do
+        @story = FactoryGirl.build :story, :author_id => @author.id, :comic_id => @comic.id
+        @story.t = nil
+        r = Story.new_t @story.comic_id
+        r.should eq 0
+      end
+    end
+    context 'コミックに一個コマがあるとき' do
+      it '1を補充値とする' do
+        FactoryGirl.create :story, :author_id => @author.id, :comic_id => @comic.id, :t => 0
+        @story = FactoryGirl.build :story, :author_id => @author.id, :comic_id => @comic.id
+        @story.t = nil
+        r = Story.new_t @story.comic_id
+        r.should eq 1
+      end
+    end
+    context 'コミックに2個コマがあるとき' do
+      it '2を補充値とする' do
+        FactoryGirl.create :story, :author_id => @author.id, :comic_id => @comic.id, :t => 0
+        FactoryGirl.create :story, :author_id => @author.id, :comic_id => @comic.id, :t => 1
+        @story = FactoryGirl.build :story, :author_id => @author.id, :comic_id => @comic.id
+        @story.t = nil
+        r = Story.new_t @story.comic_id
+        r.should eq 2
+      end
+    end
+  end
+  describe 'シリアライズチェックに於いて' do
+    context 'つつがなく終わるとき' do
+      it '0からシリアライズされているならTrueを返す' do
+        r = Story.serial? [0, 1, 2]
+        r.should be_true
+      end
+      it '見た目はシリアライズされてなくてもソート結果が無事ならtrueを返す' do
+        r = Story.serial? [0, 2, 1]
+        r.should be_true
+      end
+      it '見た目はシリアライズされてなくてもソート結果が無事ならtrueを返す' do
+        r = Story.serial? [ 2, 1, 4, 3, 0]
+        r.should be_true
+      end
+    end
+    context '異常なとき' do
+      it '0から始まらないならFalseを返す' do
+        r = Story.serial? [1, 2, 3]
+        r.should be_false
+      end
+      it '連続していないならFalseを返す' do
+        r = Story.serial? [0, 1, 2, 4]
+        r.should be_false
+      end
+      it '連続していないならFalseを返す' do
+        r = Story.serial? [0, 1, 2, 4, 5]
+        r.should be_false
+      end
+    end
+  end
+  describe 't収集に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+      @comic2 = FactoryGirl.create :comic, :author_id => @author.id
+      @c2story = FactoryGirl.create :story, :t => 0, :comic_id => @comic2.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it 'ストーリーから同一コミックのtだけを収集している' do
+        r = Story.collect_t @story
+        r.should eq [0]
+      end
+    end
+    context '複数コマのとき' do
+      it 'ストーリーから同一コミックのtだけを収集している' do
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        r = Story.collect_t @story
+        r.sort.should eq [0, 1]
+      end
+    end
+    context '複数ストーリーでヨソのコミックも混じっているとき' do
+      it 'ストーリーから同一コミックのtだけを収集している' do
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        r = Story.collect_t @story
+        r.sort.should eq [0, 1]
+      end
+    end
+  end
+  describe 'tチェックに於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.build :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it 't収集を依頼している' do
+        Story.should_receive(:collect_t).with(any_args).exactly(1)
+        Story.stub(:collect_t).with(any_args).and_return([])
+        Story.stub(:serial?).with(any_args).and_return(true)
+        r = Story.validate_t @story
+      end
+      it '収集したtをシリアライズチェック依頼している' do
+        Story.stub(:collect_t).with(any_args).and_return([])
+        Story.should_receive(:serial?).with(any_args).exactly(1)
+        Story.stub(:serial?).with(any_args).and_return(true)
+        r = Story.validate_t @story
+      end
+    end
+    #実データでチェック
+    #依頼チェックだけでは不安なので最低限のチェックを
+    context '新規のとき' do
+      it '一件だけで正常通過している' do
+        @story = FactoryGirl.build :story, :comic_id => @comic.id, :author_id => @author.id, :t => 0
+        r = Story.validate_t @story
+        r.should be_true 
+      end
+    end
+    context '既存のとき' do
+      it '2件目を作っても正常通過している' do
+        @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id, :t => 0
+        @story2 = FactoryGirl.build :story, :comic_id => @comic.id, :author_id => @author.id, :t => 1
+        r = Story.validate_t @story2
+        r.should be_true 
+      end
+    end
+  end
+  describe '挿入シフトに於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+    end
+    context '依頼チェック' do
+      #テーブルが空で0に挿入
+      it 'Updateを依頼している' do
+        Story.stub(:update_all).with(any_args)
+        Story.should_receive(:update_all).with(any_args).exactly(1)
+        @story = FactoryGirl.build :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story.insert_shift
+      end
+    end
+    context 'テーブルに1件(t:0)で0に挿入したとき' do
+      before do
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.build :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+      end
+      it '既存の行を1にシフトしている' do
+        @story2.insert_shift
+        l = Story.find :all
+        l.first.t.should eq 1
+      end
+      it 'これから挿入するt(0)が欠番になっている' do
+        @story2.insert_shift
+        l = Story.find(:all).map {|s| s.t }
+        l.include?(0).should_not be_true
+      end
+    end
+    context 'テーブルに2件(t:0,1)で1に挿入したとき' do
+      before do
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @story3 = FactoryGirl.build :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+      end
+      it '既存のt1を2にシフトしてこれから挿入するt(1)が欠番になっている' do
+        @story3.insert_shift
+        l = Story.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 2]
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)で2に挿入したとき' do
+      before do
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @story3 = FactoryGirl.create :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+        @story4 = FactoryGirl.create :story, :t => 3, :comic_id => @comic.id, :author_id => @author.id
+        @story5 = FactoryGirl.create :story, :t => 4, :comic_id => @comic.id, :author_id => @author.id
+        @story6 = FactoryGirl.build :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+      end
+      it '既存のt1を2にシフトしてこれから挿入するt(1)が欠番になっている' do
+        @story6.insert_shift
+        l = Story.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 1, 3, 4, 5]
+      end
+    end
+    context '先ほどのケース+他のコミック1件で挿入したとき' do
+      before do
+        @comic2 = FactoryGirl.create :comic, :author_id => @author.id
+        @storyc2 = FactoryGirl.create :story, :t => 0, :comic_id => @comic2.id, :author_id => @author.id
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @story3 = FactoryGirl.create :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+        @story4 = FactoryGirl.create :story, :t => 3, :comic_id => @comic.id, :author_id => @author.id
+        @story5 = FactoryGirl.create :story, :t => 4, :comic_id => @comic.id, :author_id => @author.id
+        @story6 = FactoryGirl.build :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+      end
+      it '既存のt1を2にシフトしてこれから挿入するt(1)が欠番になっている' do
+        @story6.insert_shift
+        l = Story.find(:all, :conditions => ['comic_id = ?', @comic.id]).map {|s| s.t }
+        l.sort.should eq [0, 1, 3, 4, 5]
+      end
+      it '他のコミックに影響がない' do
+        ot = @storyc2.t
+        @story6.insert_shift
+        @storyc2.reload
+        @storyc2.t.should eq ot
+      end
+    end
+  end
+  describe '少ない方に移動に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+    end
+    context '依頼チェック' do
+      it 'Updateを依頼している' do
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        Story.stub(:update_all).with(any_args)
+        Story.should_receive(:update_all).with(any_args).exactly(1)
+        ot = @story2.t
+        @story2.t = 0
+        @story2.lesser_shift ot
+      end
+    end
+    context 'テーブルに2件(t:0,1)で1を0に移動したとき' do
+      before do
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @ot = @story2.t
+        @story2.t = 0
+      end
+      it '既存のt0を1にシフトしてこれから挿入するt(0)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story2.lesser_shift @ot
+        l = Story.find(:all).map {|s| s.t }
+        l.sort.should eq [1, 1]
+      end
+      it '既存のt0を1にシフトしている' do
+        @story2.lesser_shift @ot
+        @story.reload
+        @story.t.should eq 1
+      end
+    end
+    context 'テーブルに3件(t:0,1,2)で2を1に移動したとき' do
+      before do
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @story3 = FactoryGirl.create :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+        @ot = @story3.t
+        @story3.t = 1
+      end
+      it '既存のt1を2にシフトしてこれから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story3.lesser_shift @ot
+        l = Story.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 2, 2]
+      end
+      it '既存のt1を2にシフトしている' do
+        @story3.lesser_shift @ot
+        @story2.reload
+        @story2.t.should eq 2
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)で3を1に移動したとき' do
+      before do
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @story3 = FactoryGirl.create :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+        @story4 = FactoryGirl.create :story, :t => 3, :comic_id => @comic.id, :author_id => @author.id
+        @story5 = FactoryGirl.create :story, :t => 4, :comic_id => @comic.id, :author_id => @author.id
+        @ot = @story4.t
+        @story4.t = 1
+      end
+      it 'これから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story4.lesser_shift @ot
+        l = Story.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 2, 3, 3, 4]
+      end
+      it '既存のt0には変化がない' do
+        @story4.lesser_shift @ot
+        @story.reload
+        @story.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @story4.lesser_shift @ot
+        @story5.reload
+        @story5.t.should eq 4
+      end
+      it '既存のt1を2にシフトしている' do
+        @story4.lesser_shift @ot
+        @story2.reload
+        @story2.t.should eq 2
+      end
+      it '既存のt2を3にシフトしている' do
+        @story4.lesser_shift @ot
+        @story3.reload
+        @story3.t.should eq 3
+      end
+    end
+    context '先ほどのケース+他のコミック1件で挿入したとき' do
+      before do
+        @comic2 = FactoryGirl.create :comic, :author_id => @author.id
+        @storyc2 = FactoryGirl.create :story, :t => 0, :comic_id => @comic2.id, :author_id => @author.id
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @story3 = FactoryGirl.create :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+        @story4 = FactoryGirl.create :story, :t => 3, :comic_id => @comic.id, :author_id => @author.id
+        @story5 = FactoryGirl.create :story, :t => 4, :comic_id => @comic.id, :author_id => @author.id
+        @ot = @story4.t
+        @story4.t = 1
+      end
+      it 'これから挿入するt(1)が欠番になっている' do
+        @story4.lesser_shift @ot
+        l = Story.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0, 2, 3, 3, 4]
+      end
+      it '既存のt0には変化がない' do
+        @story4.lesser_shift @ot
+        @story.reload
+        @story.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @story4.lesser_shift @ot
+        @story5.reload
+        @story5.t.should eq 4
+      end
+      it '既存のt1を2にシフトしている' do
+        @story4.lesser_shift @ot
+        @story2.reload
+        @story2.t.should eq 2
+      end
+      it '既存のt2を3にシフトしている' do
+        @story4.lesser_shift @ot
+        @story3.reload
+        @story3.t.should eq 3
+      end
+      it '他のコミックに影響がない' do
+        @story4.lesser_shift @ot
+        @storyc2.reload
+        @storyc2.t.should eq 0
+      end
+    end
+    #例外ケース。
+    #負のときは0として正常扱い
+    context 'テーブルに2件(t:0,1)で1を-1に移動したとき' do
+      before do
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @ot = @story2.t
+        @story2.t = -1
+      end
+      it '既存のt0を1にシフトしてこれから挿入するt(0)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story2.lesser_shift @ot
+        l = Story.find(:all).map {|s| s.t }
+        l.sort.should eq [1, 1]
+      end
+      it '既存のt0を1にシフトしている' do
+        @story2.lesser_shift @ot
+        @story.reload
+        @story.t.should eq 1
+      end
+      it '既存のt1は0に補正されている' do
+        @story2.lesser_shift @ot
+        @story2.t.should eq 0
+      end
+    end
+  end
+  describe '大きい方に移動に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+    end
+    context '依頼チェック' do
+      it 'Updateを依頼している' do
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        Story.stub(:update_all).with(any_args)
+        Story.should_receive(:update_all).with(any_args).exactly(1)
+        ot = @story.t
+        @story.t = 1
+        @story.higher_shift ot
+      end
+    end
+    context 'テーブルに2件(t:0,1)で0を1に移動したとき' do
+      before do
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @ot = @story.t
+        @story.t = 1
+      end
+      it '既存のt1を0にシフトしてこれから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story.higher_shift @ot
+        l = Story.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0]
+      end
+      it '既存のt1を0にシフトしている' do
+        @story.higher_shift @ot
+        @story2.reload
+        @story2.t.should eq 0
+      end
+    end
+    context 'テーブルに3件(t:0,1,2)で0を1に移動したとき' do
+      before do
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @story3 = FactoryGirl.create :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+        @ot = @story.t
+        @story.t = 1
+      end
+      it '既存のt1を0にシフトしてこれから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story.higher_shift @ot
+        l = Story.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0, 2]
+      end
+      it '既存のt1を0にシフトしている' do
+        @story.higher_shift @ot
+        @story2.reload
+        @story2.t.should eq 0
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)で1を3に移動したとき' do
+      before do
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @story3 = FactoryGirl.create :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+        @story4 = FactoryGirl.create :story, :t => 3, :comic_id => @comic.id, :author_id => @author.id
+        @story5 = FactoryGirl.create :story, :t => 4, :comic_id => @comic.id, :author_id => @author.id
+        @ot = @story2.t
+        @story2.t = 3
+      end
+      it 'これから挿入するt(3)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story2.higher_shift @ot
+        l = Story.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 1, 1, 2, 4]
+      end
+      it '既存のt0には変化がない' do
+        @story2.higher_shift @ot
+        @story.reload
+        @story.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @story2.higher_shift @ot
+        @story5.reload
+        @story5.t.should eq 4
+      end
+      it '既存のt2を1にシフトしている' do
+        @story2.higher_shift @ot
+        @story3.reload
+        @story3.t.should eq 1
+      end
+      it '既存のt3を2にシフトしている' do
+        @story2.higher_shift @ot
+        @story4.reload
+        @story4.t.should eq 2
+      end
+    end
+    context '先ほどのケース+他のコミック1件で挿入したとき' do
+      before do
+        @comic2 = FactoryGirl.create :comic, :author_id => @author.id
+        @storyc2 = FactoryGirl.create :story, :t => 0, :comic_id => @comic2.id, :author_id => @author.id
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @story3 = FactoryGirl.create :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+        @story4 = FactoryGirl.create :story, :t => 3, :comic_id => @comic.id, :author_id => @author.id
+        @story5 = FactoryGirl.create :story, :t => 4, :comic_id => @comic.id, :author_id => @author.id
+        @ot = @story2.t
+        @story2.t = 3
+      end
+      it 'これから挿入するt(3)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story2.higher_shift @ot
+        l = Story.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0, 1, 1, 2, 4]
+      end
+      it '既存のt0には変化がない' do
+        @story2.higher_shift @ot
+        @story.reload
+        @story.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @story2.higher_shift @ot
+        @story5.reload
+        @story5.t.should eq 4
+      end
+      it '既存のt2を1にシフトしている' do
+        @story2.higher_shift @ot
+        @story3.reload
+        @story3.t.should eq 1
+      end
+      it '既存のt3を2にシフトしている' do
+        @story2.higher_shift @ot
+        @story4.reload
+        @story4.t.should eq 2
+      end
+      it '他のコミックに影響がない' do
+        @story2.higher_shift @ot
+        @storyc2.reload
+        @storyc2.t.should eq 0
+      end
+    end
+    #例外ケース。
+    #max超えたときはmaxとして正常扱い
+    context 'テーブルに2件(t:0,1)で0を2に移動したとき' do
+      before do
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @ot = @story.t
+        @story.t = 2
+      end
+      it '既存のt1を0にシフトしてこれから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @story.higher_shift @ot
+        l = Story.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0]
+      end
+      it '既存のt1を0にシフトしている' do
+        @story.higher_shift @ot
+        @story2.reload
+        @story2.t.should eq 0
+      end
+      it '既存のt0は1に補正されている' do
+        @story.higher_shift @ot
+        @story.t.should eq 1
+      end
+    end
+  end
+  describe '入れ替えに於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+      @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+    end
+    context '新tが旧tより小さいとき' do
+      it '少ない方に移動を依頼している' do
+        Story.any_instance.stub(:lesser_shift).with(any_args)
+        Story.any_instance.should_receive(:lesser_shift).with(any_args).exactly(1)
+        ot = @story2.t
+        @story2.t = 0
+        @story2.update_shift ot
+      end
+    end
+    context '新tが旧tより大きいとき' do
+      it '大きい方に移動を依頼している' do
+        Story.any_instance.stub(:higher_shift).with(any_args)
+        Story.any_instance.should_receive(:higher_shift).with(any_args).exactly(1)
+        ot = @story.t
+        @story.t = 1
+        @story.update_shift ot
+      end
+    end
+  end
+  describe '順序入れ替えに於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+    end
+    context 'オブジェクトが新規でtが空のとき' do
+      it '末尾追加としてtを補充依頼している' do
+        @story = FactoryGirl.build :story, :comic_id => @comic.id, :author_id => @author.id
+        Story.stub(:new_t).with(any_args).and_return(0)
+        Story.should_receive(:new_t).with(any_args).exactly(1)
+        @story.t = nil
+        r = @story.rotate
+      end
+    end
+    context 'オブジェクトが新規でtが設定されているとき' do
+      it '挿入追加として挿入シフトを依頼している' do
+        @story = FactoryGirl.build :story, :comic_id => @comic.id, :author_id => @author.id
+        Story.any_instance.stub(:insert_shift).with(any_args)
+        Story.any_instance.should_receive(:insert_shift).with(any_args).exactly(1)
+        @story.t = 0
+        r = @story.rotate
+      end
+    end
+    context 'オブジェクトが新規でなくtが設定されているとき' do
+      it '移動として入れ替えを依頼している' do
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        Story.any_instance.stub(:update_shift).with(any_args)
+        Story.any_instance.should_receive(:update_shift).with(1).exactly(1)
+        @story2.t = 0
+        r = @story.rotate 1
+      end
+    end
+    context 'オブジェクトが新規でなくtが空のとき' do
+      it '入れ替えもシフトもせず、tを空のままにしている' do
+        #結果、tに欠番が生じてシリアライズチェックでひっかかる
+      end
+    end
+  end
+  describe '編集許可に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @story = FactoryGirl.build :story, :t => nil, :comic_id => @comic.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it 'trueを返す' do
+        r = @story.allow?
+        r.should be_true
+      end
+    end
+    context 'コミックで引っかかるとき' do
+      it 'falseを返す' do
+        Panel.any_instance.stub(:usable?).with(any_args).and_return(true)
+        Comic.any_instance.stub(:own?).with(any_args).and_return(false)
+        r = @story.allow?
+        r.should be_false
+      end
+    end
+    context 'コミックが指定されていなかったとき' do
+      it 'nilを返す' do
+        Comic.any_instance.stub(:own?).with(any_args).and_return(true)
+        @story.comic_id = nil
+        r = @story.allow?
+        r.should eq nil
+      end
+    end
+  end
+  describe '保存に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.build :story, :t => nil, :comic_id => @comic.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '編集許可チェックを依頼している' do
+        Story.any_instance.stub(:allow?).with(any_args).and_return(true)
+        Story.any_instance.should_receive(:allow?).with(any_args).exactly(1)
+        r = @story.store
+      end
+      it '順序入れ替えを依頼している' do
+        Story.any_instance.stub(:rotate).with(any_args).and_return(0)
+        Story.any_instance.should_receive(:rotate).with(any_args).exactly(1)
+        Story.any_instance.stub(:save).with(any_args).and_return(true)
+        Story.stub(:validate_t).with(any_args).and_return(true)
+        r = @story.store 
+      end
+      it '保存を依頼している' do
+        Story.stub(:new_t).with(any_args).and_return(0)
+        Story.any_instance.stub(:save).with(any_args).and_return(true)
+        Story.any_instance.should_receive(:save).with(any_args).exactly(1)
+        Story.stub(:validate_t).with(any_args).and_return(true)
+        r = @story.store
+      end
+      it 'tのシリアライズチェックを依頼している' do
+        Story.stub(:new_t).with(any_args).and_return(0)
+        Story.any_instance.stub(:save).with(any_args).and_return(true)
+        Story.stub(:validate_t).with(any_args).and_return(true)
+        Story.should_receive(:validate_t).with(any_args).exactly(1)
+        r = @story.store
+      end
+    end
+    #入れ替えテストと同じテストを実施。こちらはシフトだけでなく本尊も更新されている
+    context 'テーブルに5件(t:0,1,2,3,4)+他のコミック1件で2に挿入したとき' do
+      before do
+        @comic2 = FactoryGirl.create :comic, :author_id => @author.id
+        @storyc2 = FactoryGirl.create :story, :t => 0, :comic_id => @comic2.id, :author_id => @author.id
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @story3 = FactoryGirl.create :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+        @story4 = FactoryGirl.create :story, :t => 3, :comic_id => @comic.id, :author_id => @author.id
+        @story5 = FactoryGirl.create :story, :t => 4, :comic_id => @comic.id, :author_id => @author.id
+        @story6 = FactoryGirl.build :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+      end
+      it '既存のt0には変化がない' do
+        @story6.store
+        @story.reload
+        @story.t.should eq 0
+      end
+      it '既存のt1には変化がない' do
+        @story6.store
+        @story2.reload
+        @story2.t.should eq 1
+      end
+      it '既存のt2を3にシフトしている' do
+        @story6.store
+        @story3.reload
+        @story3.t.should eq 3
+      end
+      it '既存のt3を4にシフトしている' do
+        @story6.store
+        @story4.reload
+        @story4.t.should eq 4
+      end
+      it '既存のt5を5にシフトしている' do
+        @story6.store
+        @story5.reload
+        @story5.t.should eq 5
+      end
+      it '新規のt2が作成されている' do
+        @story6.store
+        @story6.reload
+        @story6.t.should eq 2
+      end
+      it '他のコミックに影響がない' do
+        @ot = @storyc2.t
+        @story6.store
+        @storyc2.reload
+        @storyc2.t.should eq @ot
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)+他のコミック1件で3を1に移動したとき' do
+      before do
+        @comic2 = FactoryGirl.create :comic, :author_id => @author.id
+        @storyc2 = FactoryGirl.create :story, :t => 0, :comic_id => @comic2.id, :author_id => @author.id
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @story3 = FactoryGirl.create :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+        @story4 = FactoryGirl.create :story, :t => 3, :comic_id => @comic.id, :author_id => @author.id
+        @story5 = FactoryGirl.create :story, :t => 4, :comic_id => @comic.id, :author_id => @author.id
+        @ot = @story4.t
+        @story4.t = 1
+      end
+      it '既存のt0には変化がない' do
+        @story4.store @ot
+        @story.reload
+        @story.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @story4.store @ot
+        @story5.reload
+        @story5.t.should eq 4
+      end
+      it '既存のt1を2にシフトしている' do
+        @story4.store @ot
+        @story2.reload
+        @story2.t.should eq 2
+      end
+      it '既存のt2を3にシフトしている' do
+        @story4.store @ot
+        @story3.reload
+        @story3.t.should eq 3
+      end
+      it '既存のt3を1にシフトしている' do
+        @story4.store @ot
+        @story4.reload
+        @story4.t.should eq 1
+      end
+      it '他のコミックに影響がない' do
+        @story4.store @ot
+        @storyc2.reload
+        @storyc2.t.should eq 0
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)+他のコミック1件で1を3に移動したとき' do
+      before do
+        @comic2 = FactoryGirl.create :comic, :author_id => @author.id
+        @storyc2 = FactoryGirl.create :story, :t => 0, :comic_id => @comic2.id, :author_id => @author.id
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @story3 = FactoryGirl.create :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+        @story4 = FactoryGirl.create :story, :t => 3, :comic_id => @comic.id, :author_id => @author.id
+        @story5 = FactoryGirl.create :story, :t => 4, :comic_id => @comic.id, :author_id => @author.id
+        @ot = @story2.t
+        @story2.t = 3
+      end
+      it '既存のt0には変化がない' do
+        @story2.store @ot
+        @story.reload
+        @story.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @story2.store @ot
+        @story5.reload
+        @story5.t.should eq 4
+      end
+      it '既存のt1を3にシフトしている' do
+        @story2.store @ot
+        @story2.reload
+        @story2.t.should eq 3
+      end
+      it '既存のt2を1にシフトしている' do
+        @story2.store @ot
+        @story3.reload
+        @story3.t.should eq 1
+      end
+      it '既存のt3を2にシフトしている' do
+        @story2.store @ot
+        @story4.reload
+        @story4.t.should eq 2
+      end
+      it '他のコミックに影響がない' do
+        @story2.store @ot
+        @storyc2.reload
+        @storyc2.t.should eq 0
+      end
+    end
+    #ロールバックテスト。入れ替えが直接DBをいじるので、すべてのケースで確実にロールバックを確認する
+    context 'テーブルに5件(t:0,1,2,3,4)+他のコミック1件で2に挿入したが保存に失敗したとき' do
+      before do
+        Story.any_instance.stub(:save).with(any_args).and_return(false)
+        @comic2 = FactoryGirl.create :comic, :author_id => @author.id
+        @storyc2 = FactoryGirl.create :story, :t => 0, :comic_id => @comic2.id, :author_id => @author.id
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @story3 = FactoryGirl.create :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+        @story4 = FactoryGirl.create :story, :t => 3, :comic_id => @comic.id, :author_id => @author.id
+        @story5 = FactoryGirl.create :story, :t => 4, :comic_id => @comic.id, :author_id => @author.id
+        @story6 = FactoryGirl.build :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+      end
+      it '既存のtに変化がない' do
+        @story6.store
+        @story.reload
+        @story.t.should eq 0
+        @story2.reload
+        @story2.t.should eq 1
+        @story3.reload
+        @story3.t.should eq 2
+        @story4.reload
+        @story4.t.should eq 3
+        @story5.reload
+        @story5.t.should eq 4
+        @storyc2.reload
+        @storyc2.t.should eq 0
+      end
+      it 'falseを返す' do
+        r = @story6.store
+        r.should be_false
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)+他のコミック1件で3を1に移動したがシリアルチェックに失敗したとき' do
+      before do
+        Story.stub(:validate_t).with(any_args).and_return(false)
+        @comic2 = FactoryGirl.create :comic, :author_id => @author.id
+        @storyc2 = FactoryGirl.create :story, :t => 0, :comic_id => @comic2.id, :author_id => @author.id
+        @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @story3 = FactoryGirl.create :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+        @story4 = FactoryGirl.create :story, :t => 3, :comic_id => @comic.id, :author_id => @author.id
+        @story5 = FactoryGirl.create :story, :t => 4, :comic_id => @comic.id, :author_id => @author.id
+        @ot = @story4.t
+        @story4.t = 1
+      end
+      it '既存のtに変化がない' do
+        @story4.store @ot
+        @story.reload
+        @story.t.should eq 0
+        @story2.reload
+        @story2.t.should eq 1
+        @story3.reload
+        @story3.t.should eq 2
+        @story4.reload
+        @story4.t.should eq 3
+        @story5.reload
+        @story5.t.should eq 4
+        @storyc2.reload
+        @storyc2.t.should eq 0
+      end
+      it 'falseを返す' do
+        r = @story4.store @ot
+        r.should be_false
+      end
+      it 'tにエラーメッセージが入っている' do
+        @story4.store @ot
+        @story4.errors[:t].should_not be_empty
+        @story4.valid?.should be_true
+      end
+    end
+    context '編集不可だったとき' do
+      before do
+        @story = FactoryGirl.build :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+        Story.any_instance.stub(:allow?).and_return(false)
+      end
+      it '403Forbidden例外を返す' do
+        lambda{
+          @story.store
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+  end
+  describe '切り詰め処理つき削除に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :t => 0, :comic_id => @comic.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '削除される' do
+        lambda{
+          @story.destroy_and_shorten
+        }.should change(Story, :count ).by(-1)
+      end
+      it 'Trueを返す' do
+        r = @story.destroy_and_shorten
+        r.should be_true 
+      end
+    end
+    context '削除に失敗したとき' do
+      before do
+        Story.any_instance.stub(:destroy).and_return(false)
+      end
+      it 'ロールバックされる' do
+        lambda{
+          @story.destroy_and_shorten
+        }.should_not change(Story, :count )
+      end
+      it 'Falseを返す' do
+        r = @story.destroy_and_shorten
+        r.should be_false
+      end
+    end
+    #連携テスト。切り詰めが直接DBをいじる
+    context '2件で先頭を削除したとき' do
+      before do
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+      end
+      it '行が削除される' do
+        lambda{
+          @story.destroy_and_shorten
+        }.should change(Story, :count ).by(-1)
+      end
+      it '先頭は削除される' do
+        @story.destroy_and_shorten
+        lambda{
+          Story.find @story.id
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+      it '2件目は前に詰められる' do
+        @story.destroy_and_shorten
+        @story2.reload
+        @story2.t.should eq 0
+      end
+    end
+    context '3件で先頭を削除したとき' do
+      before do
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @story3 = FactoryGirl.create :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+      end
+      it '行が削除される' do
+        lambda{
+          @story.destroy_and_shorten
+        }.should change(Story, :count ).by(-1)
+      end
+      it '先頭は削除される' do
+        @story.destroy_and_shorten
+        lambda{
+          Story.find @story.id
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+      it '2件目は前に詰められる' do
+        @story.destroy_and_shorten
+        @story2.reload
+        @story2.t.should eq 0
+      end
+      it '3件目は前に詰められる' do
+        @story.destroy_and_shorten
+        @story3.reload
+        @story3.t.should eq 1
+      end
+    end
+    context '5件で3件目を削除したとき' do
+      before do
+        @story2 = FactoryGirl.create :story, :t => 1, :comic_id => @comic.id, :author_id => @author.id
+        @story3 = FactoryGirl.create :story, :t => 2, :comic_id => @comic.id, :author_id => @author.id
+        @story4 = FactoryGirl.create :story, :t => 3, :comic_id => @comic.id, :author_id => @author.id
+        @story5 = FactoryGirl.create :story, :t => 4, :comic_id => @comic.id, :author_id => @author.id
+      end
+      it '行が削除される' do
+        lambda{
+          @story3.destroy_and_shorten
+        }.should change(Story, :count ).by(-1)
+      end
+      it '1件目は変化がない' do
+        @story3.destroy_and_shorten
+        @story.reload
+        @story.t.should eq 0
+      end
+      it '2件目は変化がない' do
+        @story3.destroy_and_shorten
+        @story2.reload
+        @story2.t.should eq 1
+      end
+      it '3件目は削除される' do
+        @story3.destroy_and_shorten
+        lambda{
+          Story.find @story3.id
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+      it '4件目は前に詰められる' do
+        @story3.destroy_and_shorten
+        @story4.reload
+        @story4.t.should eq 2
+      end
+      it '5件目は前に詰められる' do
+        @story3.destroy_and_shorten
+        @story5.reload
+        @story5.t.should eq 3
+      end
+    end
+    #ロールバックテスト。切り詰めが直接DBをいじるので、すべてのケースで確実にロールバックを確認する
+  end
+  describe '削除に於いて' do
+    before do
+      @comic = FactoryGirl.create :comic, :author_id => @author.id
+      @story = FactoryGirl.create :story, :comic_id => @comic.id, :author_id => @author.id
+      @sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @story_sheet = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @story.id, :sheet_id => @sheet.id
+      @other_comic = FactoryGirl.create :comic, :author_id => @author.id
+      @other_story = FactoryGirl.create :story, :comic_id => @other_comic.id, :author_id => @author.id
+      @other_sheet = FactoryGirl.create :sheet, :author_id => @author.id
+      @other_story_sheet = FactoryGirl.create :story_sheet, :author_id => @author.id, :story_id => @other_story.id, :sheet_id => @other_sheet.id
+    end
+    context 'つつがなく終わるとき' do
+      it '自身を削除する' do
+        lambda {
+          r = @story.destroy_with_story_sheet
+        }.should change(Story, :count).by(-1)
+        lambda {
+          r = Story.find @story.id
+        }.should raise_error
+      end
+      it '自身にリンクしているスト紙をすべて削除する' do
+        lambda {
+          r = @story.destroy_with_story_sheet
+        }.should change(StorySheet, :count).by(-1)
+        lambda {
+          r = StorySheet.find @story_sheet.id
+        }.should raise_error
+      end
+      it 'Trueを返す' do
+        r = @story.destroy_with_story_sheet
+        r.should be_true
+      end
+    end
+    context '削除に失敗したとき' do
+      before do
+        StorySheet.any_instance.stub(:destroy).with(any_args).and_return(false)
+      end
+      it 'Falseを返す' do
+        r = @story.destroy_with_story_sheet
+        r.should be_false
+      end
+      it 'ロールバックしている' do
+        lambda {
+          r = @story.destroy_with_story_sheet
+        }.should_not change(Story, :count)
+        lambda {
+          r = @story.destroy_with_story_sheet
+        }.should_not change(StorySheet, :count)
+      end
+    end
+  end
+end
index de784bd..60d9e5f 100644 (file)
@@ -3,6 +3,8 @@ require 'spec_helper'
 #システム画像
 describe SystemPicture do
   before do
+    SpeechBalloonTemplate.delete_all
+    SystemPicture.delete_all
     @admin = FactoryGirl.create :admin
   end