OSDN Git Service

t#32025:comic rename
authoryasushiito <yas@pen-chan.jp>
Tue, 10 Sep 2013 01:45:40 +0000 (10:45 +0900)
committeryasushiito <yas@pen-chan.jp>
Tue, 10 Sep 2013 01:45:40 +0000 (10:45 +0900)
49 files changed:
app/assets/javascripts/scroll_panels.js.coffee [new file with mode: 0644]
app/assets/javascripts/scrolls.js.coffee [new file with mode: 0644]
app/assets/stylesheets/scroll_panels.css.scss [new file with mode: 0644]
app/assets/stylesheets/scrolls.css.scss [new file with mode: 0644]
app/controllers/scroll_panels_controller.rb [new file with mode: 0644]
app/controllers/scrolls_controller.rb [new file with mode: 0644]
app/helpers/scroll_panels_helper.rb [new file with mode: 0644]
app/helpers/scrolls_helper.rb [new file with mode: 0644]
app/models/scroll.rb [new file with mode: 0644]
app/models/scroll_panel.rb [new file with mode: 0644]
app/views/home/scroll_panels.html.erb [new file with mode: 0644]
app/views/home/scrolls.html.erb [new file with mode: 0644]
app/views/scroll_panels/_append_panel.html.erb [new file with mode: 0644]
app/views/scroll_panels/_footer.html.erb [new file with mode: 0644]
app/views/scroll_panels/_form.html.erb [new file with mode: 0644]
app/views/scroll_panels/_licensed_pictures.html.erb [new file with mode: 0644]
app/views/scroll_panels/_list_item.html.erb [new file with mode: 0644]
app/views/scroll_panels/_order.html.erb [new file with mode: 0644]
app/views/scroll_panels/_scroll_header.html.erb [new file with mode: 0644]
app/views/scroll_panels/browse.html.erb [new file with mode: 0644]
app/views/scroll_panels/edit.html.erb [new file with mode: 0644]
app/views/scroll_panels/edit.js.erb [new file with mode: 0644]
app/views/scroll_panels/index.html.erb [new file with mode: 0644]
app/views/scroll_panels/list.html.erb [new file with mode: 0644]
app/views/scroll_panels/new.html.erb [new file with mode: 0644]
app/views/scroll_panels/scroll.html.erb [new file with mode: 0644]
app/views/scroll_panels/show.html.erb [new file with mode: 0644]
app/views/scrolls/_form.html.erb [new file with mode: 0644]
app/views/scrolls/_list_item.html.erb [new file with mode: 0644]
app/views/scrolls/browse.html.erb [new file with mode: 0644]
app/views/scrolls/edit.html.erb [new file with mode: 0644]
app/views/scrolls/edit.js.erb [new file with mode: 0644]
app/views/scrolls/index.atom.builder [new file with mode: 0644]
app/views/scrolls/index.html.erb [new file with mode: 0644]
app/views/scrolls/index.rss.builder [new file with mode: 0644]
app/views/scrolls/list.html.erb [new file with mode: 0644]
app/views/scrolls/new.html.erb [new file with mode: 0644]
app/views/scrolls/new.js.erb [new file with mode: 0644]
app/views/scrolls/show.atom.builder [new file with mode: 0644]
app/views/scrolls/show.html.erb [new file with mode: 0644]
app/views/scrolls/show.rss.builder [new file with mode: 0644]
db/migrate/20130906044356_create_scrolls.rb [new file with mode: 0644]
db/migrate/20130906044457_create_scroll_panels.rb [new file with mode: 0644]
spec/controllers/scroll_panels_controller_spec.rb [new file with mode: 0644]
spec/controllers/scrolls_controller_spec.rb [new file with mode: 0644]
spec/helpers/scroll_panels_helper_spec.rb [new file with mode: 0644]
spec/helpers/scrolls_helper_spec.rb [new file with mode: 0644]
spec/models/scroll_panel_spec.rb [new file with mode: 0644]
spec/models/scroll_spec.rb [new file with mode: 0644]

diff --git a/app/assets/javascripts/scroll_panels.js.coffee b/app/assets/javascripts/scroll_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/scrolls.js.coffee b/app/assets/javascripts/scrolls.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/scroll_panels.css.scss b/app/assets/stylesheets/scroll_panels.css.scss
new file mode 100644 (file)
index 0000000..011af68
--- /dev/null
@@ -0,0 +1,3 @@
+// Place all the styles related to the scroll_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/scrolls.css.scss b/app/assets/stylesheets/scrolls.css.scss
new file mode 100644 (file)
index 0000000..050e639
--- /dev/null
@@ -0,0 +1,3 @@
+// Place all the styles related to the scrolls 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/scroll_panels_controller.rb b/app/controllers/scroll_panels_controller.rb
new file mode 100644 (file)
index 0000000..5e0b739
--- /dev/null
@@ -0,0 +1,157 @@
+class ScrollPanelsController < 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, :scroll]
+    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 = ScrollPanel.page params[:page]
+    @page_size = ScrollPanel.page_size params[:page_size]
+    @scroll_panels = ScrollPanel.list(@page, @page_size)
+
+    respond_to do |format|
+      format.html {
+        @paginate = ScrollPanel.list_paginate(@page, @page_size)
+      }
+      format.json { render :json => @scroll_panels.to_json(ScrollPanel.list_json_opt) }
+    end
+  end
+
+  def show
+    @scroll_panel = ScrollPanel.show(params[:id], [@user, @admin])
+
+    respond_to do |format|
+      format.html # show.html.erb
+      format.json { render json: @scroll_panel.scroll_panel_as_json(@author) }
+    end
+  end
+  
+  def scroll
+    @scroll = Scroll.show(params[:id], [@user, @admin])
+    cnt = ScrollPanel.count(:conditions => ['scroll_id = ?', @scroll.id]).to_i
+    @offset = ScrollPanel.offset cnt, params[:offset]
+    @panel_count = ScrollPanel.panel_count cnt, params[:count]
+    @scroll_panels = ScrollPanel.play_list(@scroll, @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: ScrollPanel.list_as_json_text(@scroll_panels, @author)}
+      format.jsonp {
+        render :json => "callback(" + @scroll_panels.to_json_list + ");"
+      }
+    end
+  end
+  
+  def list
+    @scroll_panels = ScrollPanel.all
+
+    respond_to do |format|
+      format.html { render layout: 'system' }# index.html.erb
+      format.json { render json: @scroll_panels }
+    end
+  end
+
+  def browse
+    @scroll_panel = ScrollPanel.find(params[:id])
+
+    respond_to do |format|
+      format.html { render layout: 'system' } # show.html.erb
+      format.json { render json: @scroll_panel }
+    end
+  end
+  
+  def new
+    @scroll_panel = ScrollPanel.new 
+    @scroll_panel.supply_default
+    respond_to do |format|
+      format.html # new.html.erb
+      format.js
+      format.json { render json: @scroll_panel.scroll_panel_as_json(@author) }
+    end
+  end
+
+  def edit
+    @scroll_panel = ScrollPanel.edit(params[:id], @author)
+    respond_to do |format|
+      format.html 
+      format.js
+    end
+  end
+
+  def create
+    @scroll_panel = ScrollPanel.new 
+    @scroll_panel.supply_default
+    @scroll_panel.attributes = params[:scroll_panel]
+    @scroll_panel.overwrite @author
+    @scroll = Scroll.edit(@scroll_panel.scroll_id, @author) if @scroll_panel.scroll_id
+    @panel = Panel.show(@scroll_panel.panel_id, @author) if @scroll_panel.panel_id
+    
+    respond_to do |format|
+      if @scroll_panel.store
+        flash[:notice] = I18n.t('flash.notice.created', :model => ScrollPanel.model_name.human)
+        format.html { redirect_to action: :scroll, id: @scroll_panel.scroll_id }
+        format.json { render json: @scroll_panel.scroll_panel_as_json(@author) }
+      else
+        flash[:notice] = I18n.t('flash.notice.not_created', :model => ScrollPanel.model_name.human)
+        format.html { render action: "new" }
+        format.json { render json: @scroll_panel.errors, status: :unprocessable_entity }
+      end
+    end
+  end
+  
+  def update
+    @scroll_panel = ScrollPanel.edit(params[:id], @author)
+    ot = @scroll_panel.t
+    @scroll_panel.attributes = params[:scroll_panel]
+    @scroll_panel.overwrite @author
+    respond_to do |format|
+      if @scroll_panel.store ot
+        flash[:notice] = I18n.t('flash.notice.updated', :model => ScrollPanel.model_name.human)
+        format.html { redirect_to action: :scroll, id: @scroll_panel.scroll_id }
+        format.json { head :ok }
+      else
+        flash[:notice] = I18n.t('flash.notice.not_updated', :model => ScrollPanel.model_name.human)
+        format.html { render action: "edit" }
+        format.json { render json: @scroll_panel.errors, status: :unprocessable_entity }
+      end
+    end
+  end
+
+  def destroy
+    @scroll_panel = ScrollPanel.edit(params[:id], @author)
+    respond_to do |format|
+      if @scroll_panel.destroy_and_shorten
+        flash[:notice] = I18n.t('flash.notice.destroyed', :model => ScrollPanel.model_name.human)
+        format.html { redirect_to :controller => 'scroll_panels', :action => :scroll, :id => @scroll_panel.scroll_id }
+        format.json { head :ok }
+      else
+        flash[:notice] = I18n.t('flash.notice.not_destroyed', :model => ScrollPanel.model_name.human)
+        format.html { redirect_to scroll_panel_path(@scroll_panel) }
+        format.json { render json: @scroll_panel.errors, status: :unprocessable_entity }
+      end
+    end
+  end
+end
diff --git a/app/controllers/scrolls_controller.rb b/app/controllers/scrolls_controller.rb
new file mode 100644 (file)
index 0000000..38fa9fa
--- /dev/null
@@ -0,0 +1,131 @@
+class ScrollsController < 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 = Scroll.page params[:page]
+    @page_size = Scroll.page_size params[:page_size]
+    @scrolls = Scroll.list(@page, @page_size)
+    respond_to do |format|
+      format.html {
+        @paginate = Scroll.list_paginate(@page, @page_size)
+      }
+      format.json { render json: @scrolls.to_json(Scroll.list_json_opt) }
+      format.atom 
+      format.rss
+    end
+  end
+
+  def show
+    @scroll = Scroll.show(params[:id], [@user, @admin])
+
+    respond_to do |format|
+      format.html # show.html.erb
+      format.json { render json: @scroll.to_json(Scroll.show_json_opt) }
+      format.atom 
+      format.rss 
+    end
+  end
+
+  def count
+    @scroll = {:count => Scroll.visible_count}
+    respond_to do |format|
+      format.json { render json: @scroll.to_json }
+    end
+  end
+  
+  def list
+    @scrolls = Scroll.all
+
+    respond_to do |format|
+      format.html { render layout: 'system' }# index.html.erb
+      format.json { render json: @scrolls }
+    end
+  end
+
+  def browse
+    @scroll = Scroll.find(params[:id])
+
+    respond_to do |format|
+      format.html { render layout: 'system' } # show.html.erb
+      format.json { render json: @scroll }
+    end
+  end
+  
+  def new
+    @scroll = Scroll.new
+    @scroll.supply_default
+    respond_to do |format|
+      format.html # new.html.erb
+      format.js
+      format.json { render json: @scroll.to_json(Scroll.show_json_opt) }
+    end
+  end
+
+  def edit
+    @scroll = Scroll.edit(params[:id], @author)
+    respond_to do |format|
+      format.html 
+      format.js
+    end
+  end
+
+  def create
+    @scroll = Scroll.new
+    @scroll.supply_default 
+    @scroll.attributes = params[:scroll]
+    @scroll.overwrite @author
+
+    respond_to do |format|
+      if @scroll.save
+        flash[:notice] = I18n.t('flash.notice.created', :model => Scroll.model_name.human)
+        format.html { redirect_to @scroll }
+        format.json { render json: @scroll.to_json(Scroll.show_json_opt), status: :created, location: @scroll }
+      else
+        flash[:notice] = I18n.t('flash.notice.not_created', :model => Scroll.model_name.human)
+        format.html { render action: "new" }
+        format.json { render json: @scroll.errors, status: :unprocessable_entity }
+      end
+    end
+  end
+
+  def update
+    @scroll = Scroll.edit(params[:id], @author)
+    @scroll.attributes = params[:scroll]
+    @scroll.overwrite @author
+    respond_to do |format|
+      if @scroll.save
+        flash[:notice] = I18n.t('flash.notice.updated', :model => Scroll.model_name.human)
+        format.html { redirect_to @scroll }
+        format.json { head :ok }
+      else
+        flash[:notice] = I18n.t('flash.notice.not_updated', :model => Scroll.model_name.human)
+        format.html { render action: "edit" }
+        format.json { render json: @scroll.errors, status: :unprocessable_entity }
+      end
+    end
+  end
+
+  def destroy
+    @scroll = Scroll.edit(params[:id], @author)
+    respond_to do |format|
+      if @scroll.destroy_with_scroll_panel
+        flash[:notice] = I18n.t('flash.notice.destroyed', :model => Scroll.model_name.human)
+        format.html { redirect_to '/home/scrolls' }
+        format.json { head :ok }
+      else
+        flash[:notice] = I18n.t('flash.notice.not_destroyed', :model => Scroll.model_name.human)
+        format.html { redirect_to @scroll }
+        format.json { render json: @scroll.errors, status: :unprocessable_entity }
+      end
+    end
+  end
+end
diff --git a/app/helpers/scroll_panels_helper.rb b/app/helpers/scroll_panels_helper.rb
new file mode 100644 (file)
index 0000000..186f7bb
--- /dev/null
@@ -0,0 +1,2 @@
+module ScrollPanelsHelper
+end
diff --git a/app/helpers/scrolls_helper.rb b/app/helpers/scrolls_helper.rb
new file mode 100644 (file)
index 0000000..b06e20f
--- /dev/null
@@ -0,0 +1,2 @@
+module ScrollsHelper
+end
diff --git a/app/models/scroll.rb b/app/models/scroll.rb
new file mode 100644 (file)
index 0000000..5588f43
--- /dev/null
@@ -0,0 +1,167 @@
+class Scroll < ActiveRecord::Base
+  has_many :scroll_panels
+  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 = Scroll.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
+    'scrolls.visible > 0'
+  end
+  
+  def self.mylist_where au
+    ['scrolls.author_id = ?', au.id]
+  end
+  
+  def self.himlist_where au
+    ['scrolls.author_id = ? and scrolls.visible > 0', au.id]
+  end
+  
+  def self.list page = 1, page_size = self.default_page_size
+    Scroll.where(self.list_where()).includes(Scroll.list_opt).order('scrolls.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.mylist au, page = 1, page_size = Author.default_scroll_page_size
+    Scroll.where(self.mylist_where(au)).includes(Scroll.list_opt).order('scrolls.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.himlist au, page = 1, page_size = Author.default_scroll_page_size
+    Scroll.where(self.himlist_where(au)).includes(Scroll.list_opt).order('scrolls.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(Scroll.where(self.list_where()).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.mylist_paginate au, page = 1, page_size = Author.default_scroll_page_size
+    Kaminari.paginate_array(Array.new(Scroll.where(self.mylist_where(au)).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.himlist_paginate au, page = 1, page_size = Author.default_scroll_page_size
+    Kaminari.paginate_array(Array.new(Scroll.where(self.himlist_where(au)).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.list_opt
+    {:scroll_panels => {:panel => {}}, :author => {} }
+  end
+  
+  def self.list_json_opt
+    {:include => {:scroll_panels => {:include => {:panel => {}}}, :author => {}}}
+  end
+  
+  def self.show sid, roles
+    opt = {}
+    opt.merge!(Scroll.show_opt)
+    res = Scroll.find(sid, opt)
+    raise ActiveRecord::Forbidden unless res.visible?(roles)
+    res
+  end
+  
+  def self.edit sid, au
+    opt = {}
+    opt.merge!(Scroll.show_opt)
+    res = Scroll.find(sid, opt)
+    raise ActiveRecord::Forbidden unless res.own?(au)
+    res
+  end
+  
+  def self.show_opt
+    {:include => {:scroll_panels => {:panel => {}}, :author => {}}}
+  end
+  
+  def self.show_json_opt
+    {:include => {:scroll_panels => {:include => {:panel => {}}}, :author => {}}}
+  end
+  
+  def self.visible_count
+    Scroll.count 'visible > 0'
+  end
+  
+  def destroy_with_scroll_panel
+    res = false
+    Scroll.transaction do
+      self.scroll_panels.each do |scroll_panel|
+        raise ActiveRecord::Rollback unless scroll_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/scroll_panel.rb b/app/models/scroll_panel.rb
new file mode 100644 (file)
index 0000000..798a0c9
--- /dev/null
@@ -0,0 +1,316 @@
+class ScrollPanel < ActiveRecord::Base
+  belongs_to :author
+  belongs_to :panel
+  belongs_to :scroll
+  
+  validates :scroll_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 :t, :presence => true, :numericality => {:greater_than_or_equal_to => 0}
+  
+  def supply_default
+    self.scroll_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 = ScrollPanel.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.scroll.own?(roles)
+    self.scroll.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
+    ['scroll_panels.scroll_id = ?', cid]
+  end
+  
+  def self.list_where
+    'scrolls.visible > 0'
+  end
+  
+  def self.mylist_where au
+    ['scroll_panels.author_id = ?', au.id]
+  end
+  
+  def self.himlist_where au
+    ['scroll_panels.author_id = ? and scrolls.visible > 0', au.id]
+  end
+  
+  def self.play_list scroll, author, offset = 0, limit = ScrollPanel.default_panel_size
+    ScrollPanel.where(self.play_list_where(scroll.id)).includes(ScrollPanel.list_opt).order('scroll_panels.t').offset(offset).limit(limit)
+  end
+  
+  def self.list page = 1, page_size = self.default_page_size
+    ScrollPanel.where(self.list_where()).includes(ScrollPanel.list_opt).order('scroll_panels.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.mylist au, page = 1, page_size = Author.default_scroll_panel_page_size
+    ScrollPanel.where(self.mylist_where(au)).includes(ScrollPanel.list_opt).order('scroll_panels.updated_at desc').offset((page -1) * page_size).limit(page_size)
+  end
+  
+  def self.himlist au, page = 1, page_size = Author.default_scroll_panel_page_size
+    ScrollPanel.where(self.himlist_where(au)).includes(ScrollPanel.list_opt).order('scroll_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(ScrollPanel.where(self.list_where()).includes(ScrollPanel.list_opt).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.mylist_paginate au, page = 1, page_size = Author.default_scroll_panel_page_size
+    Kaminari.paginate_array(Array.new(ScrollPanel.where(self.mylist_where(au)).includes(ScrollPanel.list_opt).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.himlist_paginate au, page = 1, page_size = Author.default_scroll_panel_page_size
+    Kaminari.paginate_array(Array.new(ScrollPanel.where(self.himlist_where(au)).includes(ScrollPanel.list_opt).count, nil)).page(page).per(page_size)
+  end
+  
+  def self.list_opt
+    {
+      :author => {}, 
+      :scroll => {
+        :author => {}
+      }, 
+      :panel => {
+        :author => {}, 
+        :panel_pictures => {:picture => {:artist => {}, :license => {}}}, 
+        :speech_balloons =>{:balloon => {}, :speech => {}}
+      }
+    }
+  end
+  
+  def self.list_json_opt
+    {:include => {
+      :author => {}, 
+      :scroll => {
+        :author => {}
+      }, 
+      :panel => {
+        :author => {}, 
+        :panel_pictures => {:picture => {:artist => {}, :license => {}}}, 
+        :speech_balloons =>{:balloon => {}, :speech => {}}
+      }
+    }}
+  end
+  
+  def self.show spid, roles
+    opt = {}
+    opt.merge!(ScrollPanel.show_opt)
+    res = ScrollPanel.find spid, opt
+    raise ActiveRecord::Forbidden unless res.visible?(roles)
+    res
+  end
+  
+  def self.edit spid, au
+    opt = {}
+    opt.merge!(ScrollPanel.show_opt)
+    res = ScrollPanel.find spid, opt
+    raise ActiveRecord::Forbidden unless res.own?(au)
+    res
+  end
+  
+  def self.show_opt
+    {:include => {
+      :author => {}, 
+      :scroll => {
+        :author => {}
+      }, 
+      :panel => {
+        :author => {}, 
+        :panel_pictures => {:picture => {:artist => {}, :license => {}}}, 
+        :speech_balloons =>{:balloon => {}, :speech => {}}
+      }
+    }}
+  end
+  
+  def elements
+    self.panel.elements
+  end
+  
+  def scroll_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 => {:scroll => {:include => {:author => {}}}, :author => {}, :panel => panel_include}})
+  end
+  
+  def self.list_as_json_text ary, au
+    '[' + ary.map {|i| i.scroll_panel_as_json(au) }.join(',') + ']'
+  end
+  
+  def self.licensed_pictures scroll_panels
+    r = {}
+    scroll_panels.each do |scroll_panel|
+      r.merge!(scroll_panel.panel.licensed_pictures) if scroll_panel.panel
+    end
+    r
+  end
+  
+  def self.new_t scroll_id
+    r = ScrollPanel.max_t(scroll_id)
+    r.blank? ? 0 : r.to_i + 1
+  end
+  
+  def self.max_t scroll_id
+    ScrollPanel.maximum(:t, :conditions => ['scroll_id = ?', scroll_id])
+  end
+  
+  def self.find_t scroll_id, t
+    ScrollPanel.find(:first, :conditions => ['scroll_id = ? and t = ?', scroll_id, t])
+  end
+  
+  def self.collect_t scroll_panel
+    r = ScrollPanel.find(:all, :conditions => ['scroll_id = ?', scroll_panel.scroll_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 scroll_panel
+    ScrollPanel.serial?(ScrollPanel.collect_t(scroll_panel))
+  end
+  
+  def insert_shift
+    ScrollPanel.update_all('t = t + 1', ['scroll_id = ? and t >= ?', self.scroll_id, self.t])
+  end
+  
+  def lesser_shift old_t
+    self.t = 0 if self.t < 0
+    ScrollPanel.update_all('t = t + 1', ['scroll_id = ? and (t >= ? and t < ?)', self.scroll_id, self.t, old_t])
+  end
+  
+  def higher_shift old_t
+    nf = ScrollPanel.find_t(self.scroll_id, self.t)
+    max_t = ScrollPanel.max_t(self.scroll_id).to_i
+    self.t = max_t if self.t > max_t
+    ScrollPanel.update_all('t = t - 1', ['scroll_id = ? and (t > ? and t <= ?)', self.scroll_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 = ScrollPanel.new_t self.scroll_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.scroll_id == nil or self.panel_id == nil
+    self.scroll.own?(self.author) and self.panel.usable?(self.author)
+  end
+  
+  def store old_t = nil
+    res = false
+    ScrollPanel.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 = ScrollPanel.validate_t(self) 
+      unless res
+        self.errors.add :t, 'unserialized'
+        raise ActiveRecord::Rollback 
+      end
+    end
+    res
+  end
+  
+  def destroy_and_shorten
+    res = false
+    ScrollPanel.transaction do
+      ScrollPanel.update_all('t = t - 1', ['scroll_id = ? and (t > ?)', self.scroll_id, self.t])
+      raise ActiveRecord::Rollback unless self.destroy
+      res = true
+    end
+    res
+  end
+  
+end
diff --git a/app/views/home/scroll_panels.html.erb b/app/views/home/scroll_panels.html.erb
new file mode 100644 (file)
index 0000000..a99f490
--- /dev/null
@@ -0,0 +1,9 @@
+<h1><%= t '.title' -%></h1>
+
+<table>
+  <% @stories.each do |story| %>
+    <%= render 'stories/list_item', :story => story, :author => @author %>
+  <% end %>
+</table>
+<%= paginate(@paginate) %>
+<%= link_to t('stories.new.title'), new_story_path %>
diff --git a/app/views/home/scrolls.html.erb b/app/views/home/scrolls.html.erb
new file mode 100644 (file)
index 0000000..9d0287d
--- /dev/null
@@ -0,0 +1,10 @@
+<h1><%= t '.title' -%></h1>
+<p id="notice"><%= notice %></p>
+
+<table>
+  <% @comics.each do |comic| %>
+    <%= render 'comics/list_item', :comic => comic, :author => @author %>
+  <% end %>
+</table>
+<%= paginate(@paginate) %>
+<%= link_to t('comics.new.title'), new_comic_path %>
diff --git a/app/views/scroll_panels/_append_panel.html.erb b/app/views/scroll_panels/_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/scroll_panels/_footer.html.erb b/app/views/scroll_panels/_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/scroll_panels/_form.html.erb b/app/views/scroll_panels/_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/scroll_panels/_licensed_pictures.html.erb b/app/views/scroll_panels/_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/scroll_panels/_list_item.html.erb b/app/views/scroll_panels/_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/scroll_panels/_order.html.erb b/app/views/scroll_panels/_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/scroll_panels/_scroll_header.html.erb b/app/views/scroll_panels/_scroll_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/scroll_panels/browse.html.erb b/app/views/scroll_panels/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/scroll_panels/edit.html.erb b/app/views/scroll_panels/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/scroll_panels/edit.js.erb b/app/views/scroll_panels/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/scroll_panels/index.html.erb b/app/views/scroll_panels/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/scroll_panels/list.html.erb b/app/views/scroll_panels/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/scroll_panels/new.html.erb b/app/views/scroll_panels/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/scroll_panels/scroll.html.erb b/app/views/scroll_panels/scroll.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/scroll_panels/show.html.erb b/app/views/scroll_panels/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 %>
diff --git a/app/views/scrolls/_form.html.erb b/app/views/scrolls/_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/scrolls/_list_item.html.erb b/app/views/scrolls/_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/scrolls/browse.html.erb b/app/views/scrolls/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/scrolls/edit.html.erb b/app/views/scrolls/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/scrolls/edit.js.erb b/app/views/scrolls/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/scrolls/index.atom.builder b/app/views/scrolls/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/scrolls/index.html.erb b/app/views/scrolls/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/scrolls/index.rss.builder b/app/views/scrolls/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/scrolls/list.html.erb b/app/views/scrolls/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/scrolls/new.html.erb b/app/views/scrolls/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/scrolls/new.js.erb b/app/views/scrolls/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/scrolls/show.atom.builder b/app/views/scrolls/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/scrolls/show.html.erb b/app/views/scrolls/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/scrolls/show.rss.builder b/app/views/scrolls/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/db/migrate/20130906044356_create_scrolls.rb b/db/migrate/20130906044356_create_scrolls.rb
new file mode 100644 (file)
index 0000000..acce49d
--- /dev/null
@@ -0,0 +1,12 @@
+class CreateScrolls < ActiveRecord::Migration
+  def change
+    create_table :scrolls 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
+  end
+end
diff --git a/db/migrate/20130906044457_create_scroll_panels.rb b/db/migrate/20130906044457_create_scroll_panels.rb
new file mode 100644 (file)
index 0000000..d260b87
--- /dev/null
@@ -0,0 +1,12 @@
+class CreateScrollPanels < ActiveRecord::Migration
+  def change
+    create_table :scroll_panels do |t|
+      t.integer :scroll_id, :null => false, :default => 0
+      t.integer :panel_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
diff --git a/spec/controllers/scroll_panels_controller_spec.rb b/spec/controllers/scroll_panels_controller_spec.rb
new file mode 100644 (file)
index 0000000..2a4db41
--- /dev/null
@@ -0,0 +1,1456 @@
+# -*- encoding: utf-8 -*-
+require 'spec_helper'
+#ストーリー
+describe ScrollPanelsController 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
+    @user = FactoryGirl.create :user_yas
+    @author = FactoryGirl.create :author, :user_id => @user.id
+    @scroll = FactoryGirl.create :scroll, :author_id => @user.author.id
+    @panel = FactoryGirl.create :panel, :author_id => @author.id
+  end
+  
+if MagicNumber['run_mode'] == 1
+  describe '一覧表示に於いて' do
+    before do
+      sign_in @user
+      @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+      ScrollPanel.stub(:list).and_return([@scroll_panel, @scroll_panel, @scroll_panel])
+    end
+    context 'パラメータpageについて' do
+      it '@pageに値が入る' do
+        get :index, :page => 5
+        assigns(:page).should eq 5
+      end
+      it '省略されると@pageに1値が入る' do
+        get :index
+        assigns(:page).should eq 1
+      end
+      it '与えられたpage_sizeがセットされている' do
+        get :index, :page_size => 15
+        assigns(:page_size).should eq 15
+      end
+      it '省略されると@page_sizeにデフォルト値が入る' do
+        get :index
+        assigns(:page_size).should eq ScrollPanel.default_page_size
+      end
+      it '最大を超えると@page_sizeにデフォルト最大値が入る' do
+        get :index, :page_size => 1500
+        assigns(:page_size).should eq ScrollPanel.max_page_size
+      end
+      it '不正な値が入ると@page_sizeにデフォルト最大値が入る' do
+        get :index, :page_size => 0
+        assigns(:page_size).should eq ScrollPanel.default_page_size
+      end
+    end
+    context 'つつがなく終わるとき' do
+      it 'ステータスコード200 OKを返す' do
+        get :index
+        response.should be_success 
+      end
+      it 'ストーリーモデルに一覧を問い合わせている' do
+        ScrollPanel.should_receive(:list).exactly(1)
+        get :index
+      end
+      it '@scroll_panelsにリストを取得している' do
+        get :index
+        assigns(:scroll_panels).should have_at_least(3).items
+      end
+      context 'html形式' do
+        it '@paginateにページ制御を取得している' do
+          get :index
+          assigns(:paginate).is_a?(Kaminari::PaginatableArray).should be_true
+        end
+        it 'indexテンプレートを描画する' do
+          get :index
+          response.should render_template("index")
+        end
+      end
+      context 'json形式' do
+        it 'jsonデータを返す' do
+          get :index, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+        it 'ストーリーモデルにjson一覧出力オプションを問い合わせている' do
+          ScrollPanel.should_receive(:list_json_opt).exactly(1)
+          get :index, :format => :json
+        end
+        it 'データがリスト構造になっている' do
+          get :index, :format => :json
+          json = JSON.parse response.body
+          json.should have_at_least(3).items
+        end
+        it 'リストの先頭くらいはストーリーっぽいものであって欲しい' do
+          get :index, :format => :json
+          json = JSON.parse response.body
+          json.first.has_key?("panel_id").should be_true
+          json.first.has_key?("scroll_id").should be_true
+          json.first.has_key?("t").should be_true
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :index
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          get :index
+          response.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          get :index, :format => :json
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          get :index, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+    context 'ユーザ権限はないが管理者権限があるとき' do
+      before do
+        sign_out @user
+        sign_in @admin
+      end
+      it 'ステータスコード200 OKを返す' do
+        get :index
+        response.should be_success 
+      end
+    end
+    context 'ユーザだが作家登録していないとき' do
+      before do
+        @author.destroy
+      end
+      it 'ステータスコード200 OKを返す' do
+        get :index
+        response.should be_success 
+      end
+    end
+  end
+  
+  describe '単体表示に於いて' do
+    before do
+      sign_in @user
+      @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+      Scroll.stub(:show).with(@scroll.id.to_s, [@user, nil]).and_return(@scroll)
+      Scroll.stub(:show).with(@scroll.id.to_s, [nil, @admin]).and_return(@scroll)
+      ScrollPanel.stub(:show).with(@scroll_panel.id.to_s, [@user, nil]).and_return(@scroll_panel)
+      ScrollPanel.stub(:show).with(@scroll_panel.id.to_s, [nil, @admin]).and_return(@scroll_panel)
+    end
+    context 'つつがなく終わるとき' do
+      it 'ストーリーモデルに単体取得を問い合わせている' do
+        ScrollPanel.should_receive(:show).with(@scroll_panel.id.to_s, [@user, nil]).exactly(1)
+        get :show, :id => @scroll_panel.id
+      end
+      it '@scroll_panelにアレを取得している' do
+        get :show, :id => @scroll_panel.id
+        assigns(:scroll_panel).should eq @scroll_panel
+      end
+      context 'html形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :show, :id => @scroll_panel.id
+          response.should be_success
+        end
+        it 'showテンプレートを描画する' do
+          get :show, :id => @scroll_panel.id
+          response.should render_template("show")
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :show, :id => @scroll_panel.id, :format => :json
+          response.should be_success
+        end
+        it 'jsonデータを返す' do
+          get :show, :id => @scroll_panel.id, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+        it 'ストーリーモデルにストーリーjson出力を問い合わせている' do
+          ScrollPanel.any_instance.should_receive(:scroll_panel_as_json).exactly(1)
+          get :show, :id => @scroll_panel.id, :format => :json
+        end
+        it 'データがアレになっている' do
+          get :show, :id => @scroll_panel.id, :format => :json
+          json = JSON.parse response.body
+          json.has_key?("panel_id").should be_true
+          json.has_key?("scroll_id").should be_true
+          json.has_key?("author_id").should be_true
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :show, :id => @scroll_panel.id
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          get :show, :id => @scroll_panel.id
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          get :show, :id => @scroll_panel.id, :format => :json
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          get :show, :id => @scroll_panel.id, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+    context 'ユーザ権限はないが管理者権限があるとき' do
+      before do
+        sign_out @user
+        sign_in @admin
+      end
+      it 'ステータスコード200 OKを返す' do
+          get :show, :id => @scroll_panel.id
+        response.should be_success 
+      end
+    end
+    context 'ユーザだが作家登録していないとき' do
+      before do
+        @author.destroy
+      end
+      it 'ステータスコード200 OKを返す' do
+          get :show, :id => @scroll_panel.id
+        response.should be_success 
+      end
+    end
+  end
+  
+  describe '閲覧に於いて' do
+    before do
+      @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+      Scroll.stub(:show).with(@scroll.id.to_s, [@user, nil]).and_return(@scroll)
+      Scroll.stub(:show).with(@scroll.id.to_s, [nil, @admin]).and_return(@scroll)
+      ScrollPanel.stub(:count).and_return(10)
+      ScrollPanel.stub(:play_list).with(any_args).and_return([@scroll_panel, @scroll_panel, @scroll_panel])
+      sign_in @user
+    end
+    context 'パラメータチェックする' do
+      it '与えられたoffsetがセットされている' do
+        get :scroll, :id => @scroll.id, :offset => 5
+        assigns(:offset).should eq 5
+      end
+      it '省略されると@offsetに0値が入る' do
+        get :scroll, :id => @scroll.id
+        assigns(:offset).should eq 0
+      end
+      it '与えられたcountがセットされている' do
+        get :scroll, :id => @scroll.id, :count => 4
+        assigns(:panel_count).should eq 4
+      end
+      it '省略されると@panel_countにデフォルト値が入る' do
+        get :scroll, :id => @scroll.id
+        assigns(:panel_count).should eq ScrollPanel.default_panel_size
+      end
+      it '最大を超えると@panel_countにデフォルト最大値が入る' do
+        get :scroll, :id => @scroll.id, :count => 1500
+        assigns(:panel_count).should eq ScrollPanel.max_panel_size
+      end
+      it '不正な値が入ると@panel_countにデフォルト最大値が入る' do
+        get :scroll, :id => @scroll.id, :count => -1
+        assigns(:panel_count).should eq ScrollPanel.default_panel_size
+      end
+    end
+    context '事前チェックする' do
+      it 'コミックモデルに単体取得を問い合わせている' do
+        Scroll.should_receive(:show).with(@scroll.id.to_s, [@user, nil]).exactly(1)
+        get :scroll, :id => @scroll.id
+      end
+      it 'ストーリーモデルにプレイリスト取得を問い合わせている' do
+        ScrollPanel.should_receive(:play_list).with(@scroll, @author, 0, 30).exactly(1)
+        get :scroll, :id => @scroll.id
+      end
+    end
+    context 'つつがなく終わるとき' do
+      it '@scroll_panelsにリストを取得している' do
+        get :scroll, :id => @scroll.id
+        assigns(:scroll_panels).should have_at_least(3).items
+      end
+      context 'html形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :scroll, :id => @scroll.id
+          response.should be_success 
+        end
+        it 'scrollテンプレートを描画する' do
+          get :scroll, :id => @scroll.id
+          response.should render_template("scroll")
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :scroll, :id => @scroll.id, :format => :json
+          response.should be_success 
+        end
+        it 'jsonデータを返す' do
+          get :scroll, :id => @scroll.id, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+        it 'ストーリーモデルにストーリーリストのjson出力を問い合わせている' do
+          ScrollPanel.should_receive(:list_as_json_text).exactly(1)
+          get :scroll, :id => @scroll.id, :format => :json
+        end
+        it 'データがリスト構造になっている' do
+          get :scroll, :id => @scroll.id, :format => :json
+          json = JSON.parse response.body
+          json.should have_at_least(3).items
+        end
+        it 'リストの先頭くらいはストーリーっぽいものであって欲しい' do
+          get :scroll, :id => @scroll.id, :format => :json
+          json = JSON.parse response.body
+          json.first.has_key?("panel_id").should be_true
+          json.first.has_key?("scroll_id").should be_true
+          json.first.has_key?("author_id").should be_true
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :scroll, :id => @scroll.id
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          get :scroll, :id => @scroll.id
+          response.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          get :scroll, :id => @scroll.id, :format => :json
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          get :scroll, :id => @scroll.id, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+    context 'ユーザ権限はないが管理者権限があるとき' do
+      before do
+        sign_out @user
+        sign_in @admin
+      end
+      it 'ステータスコード200 OKを返す' do
+        get :scroll, :id => @scroll.id
+        response.should be_success 
+      end
+    end
+    context 'ユーザだが作家登録していないとき' do
+      before do
+        @author.destroy
+      end
+      it 'ステータスコード200 OKを返す' do
+        get :scroll, :id => @scroll.id
+        response.should be_success 
+      end
+    end
+  end
+
+  describe '新規作成フォーム表示に於いて' do
+    before do
+      sign_in @user
+    end
+    context 'つつがなく終わるとき' do
+      it '@scroll_panelに新規データを用意している' do
+        get :new
+        assigns(:scroll_panel).should be_a_new(ScrollPanel)
+      end
+      it 'モデルにデフォルト値補充を依頼している' do
+        ScrollPanel.any_instance.should_receive(:supply_default).exactly(1)
+        get :new
+      end
+      context 'html形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :new
+          response.should be_success 
+        end
+        it 'newテンプレートを描画する' do
+          get :new
+          response.should render_template("new")
+        end
+      end
+      context 'js形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :new, :format => :js
+          response.should be_success 
+        end
+        it 'new.jsテンプレートを描画する' do
+          get :new, :format => :js
+          response.should render_template("new")
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :new, :format => :json
+          response.should be_success 
+        end
+        it 'jsonデータを返す' do
+          get :new, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+        it 'ストーリーモデルのストーリーのjson出力を問い合わせている' do
+          ScrollPanel.any_instance.should_receive(:scroll_panel_as_json).exactly(1)
+          get :new, :format => :json
+        end
+        it 'データがアレになっている' do
+          get :new, :format => :json
+          json = JSON.parse response.body
+          json.has_key?("panel_id").should be_true
+          json.has_key?("scroll_id").should be_true
+          json.has_key?("author_id").should be_true
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :new
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          get :new
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          get :new, :format => :json
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          get :new, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+    context 'ユーザ権限はないが管理者権限があるとき' do
+      before do
+        sign_out @user
+        sign_in @admin
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :new
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          get :new
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+    end
+    context 'ユーザだが作家登録していないとき' do
+      before do
+        @author.destroy
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :new
+          response.status.should eq 302
+        end
+        it '作家登録ページへ遷移する' do
+          get :new
+          response.body.should redirect_to new_author_path
+        end
+      end
+    end
+  end
+  
+  describe '新規作成に於いて' do
+    before do
+      @attr = FactoryGirl.attributes_for(:scroll_panel, :t => nil, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id)
+      sign_in @user
+    end
+    context 'つつがなく終わるとき' do
+      it 'デフォルト値補充を依頼する' do
+        ScrollPanel.any_instance.should_receive(:supply_default).exactly(1)
+        post :create, :scroll_panel => @attr
+      end
+      it 'POSTデータから、カラム値を復元している' do
+        ScrollPanel.any_instance.stub(:store).and_return(true)
+        post :create, :scroll_panel => @attr
+        assigns(:scroll_panel).scroll_id.should eq @scroll.id
+        assigns(:scroll_panel).panel_id.should eq @panel.id
+      end
+      it '上書き補充を依頼する' do
+        ScrollPanel.any_instance.should_receive(:overwrite).exactly(1)
+        post :create, :scroll_panel => @attr
+      end
+      it 'コミックモデルに編集取得を依頼している' do
+        Scroll.should_receive(:edit).with(@scroll.id, @author).exactly(1)
+        post :create, :scroll_panel => @attr
+      end
+      it 'コマモデルに単体取得を依頼している' do
+        Panel.should_receive(:show).with(@panel.id, @author).exactly(1)
+        post :create, :scroll_panel => @attr
+      end
+      it 'モデルに保存依頼する' do
+        ScrollPanel.any_instance.should_receive(:store).exactly(1)
+        post :create, :scroll_panel => @attr
+      end
+      it "@scroll_panelに作成されたコマを保持していて、それがDBにある" do
+        post :create, :scroll_panel => @attr
+        assigns(:scroll_panel).should be_a(ScrollPanel)
+        assigns(:scroll_panel).should be_persisted
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          ScrollPanel.any_instance.stub(:store).and_return(true)
+          post :create, :scroll_panel => @attr
+          response.status.should eq 302
+        end
+        it 'コミックのストーリー表示へ遷移する' do
+#          ScrollPanel.any_instance.stub(:store).and_return(true)
+          post :create, :scroll_panel => @attr
+          response.should redirect_to(:action => :scroll, :id => @attr[:scroll_id])
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+#          ScrollPanel.any_instance.stub(:store).and_return(true)
+          post :create, :scroll_panel => @attr, :format => :json
+          response.should be_success 
+        end
+        it '作成されたコマをjsonデータで返す' do
+          post :create, :scroll_panel => @attr, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+        it 'ストーリーモデルのストーリーのjson出力を問い合わせている' do
+          ScrollPanel.any_instance.should_receive(:scroll_panel_as_json).exactly(1)
+          post :create, :scroll_panel => @attr, :format => :json
+        end
+        it 'データがアレになっている' do
+          post :create, :scroll_panel => @attr, :format => :json
+          json = JSON.parse response.body
+          json["t"].should eq 0
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          post :create, :scroll_panel => @attr
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          post :create, :scroll_panel => @attr
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          post :create, :scroll_panel => @attr, :format => :json
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          post :create, :scroll_panel => @attr, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+    context 'ユーザ権限はないが管理者権限があるとき' do
+      before do
+        sign_out @user
+        sign_in @admin
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          post :create, :scroll_panel => @attr
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          post :create, :scroll_panel => @attr
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+    end
+    context 'ユーザだが作家登録していないとき' do
+      before do
+        @author.destroy
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          post :create, :scroll_panel => @attr
+          response.status.should eq 302
+        end
+        it '作家登録ページへ遷移する' do
+          post :create, :scroll_panel => @attr
+          response.body.should redirect_to new_author_path
+        end
+      end
+    end
+    context '検証、保存に失敗した' do
+      before do
+        ScrollPanel.any_instance.stub(:store).and_return(false)
+      end
+      it "未保存のコマを保持している" do
+        post :create, :scroll_panel => @attr
+        assigns(:scroll_panel).should be_a_new(ScrollPanel)
+      end
+      context 'html形式' do
+        it 'ステータスコード200 OKを返す' do
+          post :create, :scroll_panel => @attr
+          response.status.should eq 200
+        end
+        it '新規ページを描画する' do
+          post :create, :scroll_panel => @attr
+          response.should render_template("new")
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード422 unprocessable_entity を返す' do
+          post :create, :scroll_panel => @attr, :format => :json
+          response.status.should eq 422
+        end
+        it '応答メッセージUnprocessable Entityを返す' do
+          post :create, :scroll_panel => @attr, :format => :json
+          response.message.should match(/Unprocessable/)
+        end
+      end
+    end
+  end
+
+  describe '編集フォーム表示に於いて' do
+    before do
+      @scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @author.id
+      sign_in @user
+      ScrollPanel.stub(:show).and_return(@scroll_panel)
+    end
+    context 'つつがなく終わるとき' do
+      it 'ストーリーモデルに編集取得を問い合わせている' do
+        ScrollPanel.should_receive(:edit).exactly(1)
+        get :edit, :id => @scroll_panel.id
+      end
+      it '@scroll_panelにデータを用意している' do
+        get :edit, :id => @scroll_panel.id
+        assigns(:scroll_panel).should eq @scroll_panel
+      end
+      context 'html形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :edit, :id => @scroll_panel.id
+          response.should be_success 
+        end
+        it 'editテンプレートを描画する' do
+          get :edit, :id => @scroll_panel.id
+          response.should render_template("edit")
+        end
+      end
+      context 'js形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :edit, :id => @scroll_panel.id, :format => :js
+          response.should be_success 
+        end
+        it 'edit.jsテンプレートを描画する' do
+          get :edit, :id => @scroll_panel.id, :format => :js
+          response.should render_template("edit")
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :edit, :id => @scroll_panel.id
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          get :edit, :id => @scroll_panel.id
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'js形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          get :edit, :id => @scroll_panel.id, :format => :js
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          get :edit, :id => @scroll_panel.id, :format => :js
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+    context 'ユーザ権限はないが管理者権限があるとき' do
+      before do
+        sign_out @user
+        sign_in @admin
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :edit, :id => @scroll_panel.id
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          get :edit, :id => @scroll_panel.id
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+    end
+    context 'ユーザだが作家登録していないとき' do
+      before do
+        @author.destroy
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :edit, :id => @scroll_panel.id
+          response.status.should eq 302
+        end
+        it '作家登録ページへ遷移する' do
+          get :edit, :id => @scroll_panel.id
+          response.body.should redirect_to new_author_path
+        end
+      end
+    end
+  end
+
+  describe '更新に於いて' do
+    before do
+      @scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @user.author.id
+      @attr = FactoryGirl.attributes_for(:scroll_panel, :author_id => @author.id)
+      sign_in @user
+    end
+    context 'つつがなく終わるとき' do
+      it 'モデルに編集取得依頼する' do
+        ScrollPanel.stub(:edit).with(any_args).and_return(@scroll_panel)
+        ScrollPanel.should_receive(:edit).exactly(1)
+        put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+      end
+      it 'POSTデータから、カラム値を復元している' do
+        ScrollPanel.any_instance.stub(:store).and_return(true)
+        ScrollPanel.any_instance.should_receive(:attributes=).exactly(1)
+        put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+      end
+      it '上書き補充を依頼する' do
+        ScrollPanel.any_instance.should_receive(:overwrite).exactly(1)
+        put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+      end
+      it 'モデルに保存依頼する' do
+        ScrollPanel.any_instance.should_receive(:store).exactly(1)
+        put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+      end
+      it "@scroll_panelに作成されたストーリーを保持していて、それがDBにある" do
+        put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+        assigns(:scroll_panel).should be_a(ScrollPanel)
+        assigns(:scroll_panel).should be_persisted
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          ScrollPanel.any_instance.stub(:store).and_return(true)
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+          response.status.should eq 302
+        end
+        it 'ストーリー表示へ遷移する' do
+#          ScrollPanel.any_instance.stub(:store).and_return(true)
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+          response.should redirect_to(:action => :scroll, :id => @attr[:scroll_id])
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+#          ScrollPanel.any_instance.stub(:store).and_return(true)
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr, :format => :json
+          response.should be_success 
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr, :format => :json
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+    context 'ユーザ権限はないが管理者権限があるとき' do
+      before do
+        sign_out @user
+        sign_in @admin
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+    end
+    context 'ユーザだが作家登録していないとき' do
+      before do
+        @author.destroy
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+          response.status.should eq 302
+        end
+        it '作家登録ページへ遷移する' do
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+          response.body.should redirect_to new_author_path
+        end
+      end
+    end
+    context '検証、保存に失敗した' do
+      before do
+        ScrollPanel.any_instance.stub(:store).and_return(false)
+      end
+      it "指定のコマを保持している" do
+        put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+        assigns(:scroll_panel).should eq @scroll_panel
+      end
+      context 'html形式' do
+        it 'ステータスコード200 OKを返す' do
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+          response.status.should eq 200
+        end
+        it '編集ページを描画する' do
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+          response.should render_template("edit")
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード422 unprocessable_entity を返す' do
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr, :format => :json
+          response.status.should eq 422
+        end
+        it '応答メッセージUnprocessable Entityを返す' do
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr, :format => :json
+          response.message.should match(/Unprocessable/)
+        end
+      end
+    end
+  end
+
+  describe '削除に於いて' do
+    before do
+      @scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @author.id
+      sign_in @user
+      ScrollPanel.stub(:edit).and_return(@scroll_panel)
+    end
+    context '事前チェックしておく' do
+      before do
+        ScrollPanel.stub(:edit).with(any_args()).and_return @scroll_panel
+        ScrollPanel.any_instance.stub(:destroy_and_shorten).with(any_args()).and_return(true)
+      end
+      it 'ストーリーモデルに編集取得を問い合わせている' do
+        ScrollPanel.should_receive(:edit).exactly(1)
+        delete :destroy, :id => @scroll_panel.id
+      end
+      it 'モデルに削除を依頼する' do
+        ScrollPanel.any_instance.should_receive(:destroy_and_shorten).exactly(1)
+        delete :destroy, :id => @scroll_panel.id
+      end
+      it '@scroll_panelにアレを取得している' do
+        delete :destroy, :id => @scroll_panel.id
+        assigns(:scroll_panel).id.should eq(@scroll_panel.id)
+      end
+    end
+    context 'つつがなく終わるとき' do
+      before do
+        ScrollPanel.any_instance.stub(:destroy_and_shorten).with(any_args()).and_return(true)
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          delete :destroy, :id => @scroll_panel.id
+          response.status.should eq 302
+        end
+        it '閲覧ページへ遷移する' do
+          delete :destroy, :id => @scroll_panel.id
+          response.should redirect_to(:controller => 'scroll_panels', :action => :scroll, :id => @scroll_panel.scroll_id)
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+          delete :destroy, :id => @scroll_panel.id, :format => :json
+          response.should be_success
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          delete :destroy, :id => @scroll_panel.id
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          delete :destroy, :id => @scroll_panel.id
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          delete :destroy, :id => @scroll_panel.id, :format => :json
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          delete :destroy, :id => @scroll_panel.id, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+    context 'ユーザ権限はないが管理者権限があるとき' do
+      before do
+        sign_out @user
+        sign_in @admin
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          delete :destroy, :id => @scroll_panel.id
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          delete :destroy, :id => @scroll_panel.id
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+    end
+    context 'ユーザだが作家登録していないとき' do
+      before do
+        @author.destroy
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          delete :destroy, :id => @scroll_panel.id
+          response.status.should eq 302
+        end
+        it '作家登録ページへ遷移する' do
+          delete :destroy, :id => @scroll_panel.id
+          response.body.should redirect_to new_author_path
+        end
+      end
+    end
+    context '削除に失敗したとき' do
+      before do
+        ScrollPanel.any_instance.stub(:destroy_and_shorten).with(any_args()).and_return(false)
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          delete :destroy, :id => @scroll_panel.id
+          response.status.should eq 302
+        end
+        it 'そのコミックの詳細ページへ遷移する' do
+          delete :destroy, :id => @scroll_panel.id
+          response.should redirect_to(scroll_panel_path(@scroll_panel))
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード422 unprocessable_entity を返す' do
+          delete :destroy, :id => @scroll_panel.id, :format => :json
+          response.status.should eq 422
+        end
+        it '応答メッセージUnprocessable Entityを返す' do
+          delete :destroy, :id => @scroll_panel.id, :format => :json
+          response.message.should match(/Unprocessable/)
+        end
+      end
+    end
+=begin
+    context '対象ストーリーがないとき' do
+    end
+    context '他人のストーリーだったとき' do
+    end
+=end
+  end
+  
+else
+  describe '一覧表示に於いて' do
+    before do
+      sign_in @user
+      @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+      ScrollPanel.stub(:list).and_return([@scroll_panel, @scroll_panel, @scroll_panel])
+    end
+    context 'つつがなく終わるとき' do
+      it 'ステータスコード200 OKを返す' do
+        get :index
+        response.should be_success 
+      end
+      context 'html形式' do
+        it 'indexテンプレートを描画する' do
+          get :index
+          response.should render_template("index")
+        end
+      end
+      context 'json形式' do
+        it 'jsonデータを返す' do
+          get :index, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      it 'ステータスコード200 OKを返す' do
+        get :index
+        response.should be_success 
+      end
+      context 'html形式' do
+        it 'indexテンプレートを描画する' do
+          get :index
+          response.should render_template("index")
+        end
+      end
+      context 'json形式' do
+        it 'jsonデータを返す' do
+          get :index, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+      end
+    end
+  end
+  
+  describe '単体表示に於いて' do
+    before do
+      sign_in @user
+      @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+      Scroll.stub(:show).with(@scroll.id.to_s, [nil, nil]).and_return(@scroll)
+      Scroll.stub(:show).with(@scroll.id.to_s, [@user, nil]).and_return(@scroll)
+      Scroll.stub(:show).with(@scroll.id.to_s, [nil, @admin]).and_return(@scroll)
+      ScrollPanel.stub(:show).with(@scroll_panel.id.to_s, [nil, nil]).and_return(@scroll_panel)
+      ScrollPanel.stub(:show).with(@scroll_panel.id.to_s, [@user, nil]).and_return(@scroll_panel)
+      ScrollPanel.stub(:show).with(@scroll_panel.id.to_s, [nil, @admin]).and_return(@scroll_panel)
+    end
+    context 'つつがなく終わるとき' do
+      context 'html形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :show, :id => @scroll_panel.id
+          response.should be_success
+        end
+        it 'showテンプレートを描画する' do
+          get :show, :id => @scroll_panel.id
+          response.should render_template("show")
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :show, :id => @scroll_panel.id, :format => :json
+          response.should be_success
+        end
+        it 'jsonデータを返す' do
+          get :show, :id => @scroll_panel.id, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :show, :id => @scroll_panel.id
+          response.should be_success
+        end
+        it 'showテンプレートを描画する' do
+          get :show, :id => @scroll_panel.id
+          response.should render_template("show")
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :show, :id => @scroll_panel.id, :format => :json
+          response.should be_success
+        end
+        it 'jsonデータを返す' do
+          get :show, :id => @scroll_panel.id, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+      end
+    end
+  end
+  
+  describe '閲覧に於いて' do
+    before do
+      @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+      Scroll.stub(:show).with(@scroll.id.to_s, [nil, nil]).and_return(@scroll)
+      Scroll.stub(:show).with(@scroll.id.to_s, [@user, nil]).and_return(@scroll)
+      Scroll.stub(:show).with(@scroll.id.to_s, [nil, @admin]).and_return(@scroll)
+      ScrollPanel.stub(:count).and_return(10)
+      ScrollPanel.stub(:play_list).with(any_args).and_return([@scroll_panel, @scroll_panel, @scroll_panel])
+      sign_in @user
+    end
+    context 'つつがなく終わるとき' do
+      context 'html形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :scroll, :id => @scroll.id
+          response.should be_success 
+        end
+        it 'scrollテンプレートを描画する' do
+          get :scroll, :id => @scroll.id
+          response.should render_template("scroll")
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :scroll, :id => @scroll.id, :format => :json
+          response.should be_success 
+        end
+        it 'jsonデータを返す' do
+          get :scroll, :id => @scroll.id, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :scroll, :id => @scroll.id
+          response.should be_success 
+        end
+        it 'scrollテンプレートを描画する' do
+          get :scroll, :id => @scroll.id
+          response.should render_template("scroll")
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :scroll, :id => @scroll.id, :format => :json
+          response.should be_success 
+        end
+        it 'jsonデータを返す' do
+          get :scroll, :id => @scroll.id, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+      end
+    end
+  end
+
+  describe '新規作成フォーム表示に於いて' do
+    before do
+      sign_in @user
+    end
+    context 'つつがなく終わるとき' do
+      context 'html形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :new
+          response.should be_success 
+        end
+        it 'newテンプレートを描画する' do
+          get :new
+          response.should render_template("new")
+        end
+      end
+      context 'js形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :new, :format => :js
+          response.should be_success 
+        end
+        it 'new.jsテンプレートを描画する' do
+          get :new, :format => :js
+          response.should render_template("new")
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :new, :format => :json
+          response.should be_success 
+        end
+        it 'jsonデータを返す' do
+          get :new, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :new
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          get :new
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          get :new, :format => :json
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          get :new, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+  end
+  
+  describe '新規作成に於いて' do
+    before do
+      @attr = FactoryGirl.attributes_for(:scroll_panel, :t => nil, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id)
+      sign_in @user
+    end
+    context 'つつがなく終わるとき' do
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          ScrollPanel.any_instance.stub(:store).and_return(true)
+          post :create, :scroll_panel => @attr
+          response.status.should eq 302
+        end
+        it 'コミックのストーリー表示へ遷移する' do
+#          ScrollPanel.any_instance.stub(:store).and_return(true)
+          post :create, :scroll_panel => @attr
+          response.should redirect_to(:action => :scroll, :id => @attr[:scroll_id])
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+#          ScrollPanel.any_instance.stub(:store).and_return(true)
+          post :create, :scroll_panel => @attr, :format => :json
+          response.should be_success 
+        end
+        it '作成されたコマをjsonデータで返す' do
+          post :create, :scroll_panel => @attr, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          post :create, :scroll_panel => @attr
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          post :create, :scroll_panel => @attr
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          post :create, :scroll_panel => @attr, :format => :json
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          post :create, :scroll_panel => @attr, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+  end
+
+  describe '編集フォーム表示に於いて' do
+    before do
+      @scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @author.id
+      sign_in @user
+      ScrollPanel.stub(:show).and_return(@scroll_panel)
+    end
+    context 'つつがなく終わるとき' do
+      context 'html形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :edit, :id => @scroll_panel.id
+          response.should be_success 
+        end
+        it 'editテンプレートを描画する' do
+          get :edit, :id => @scroll_panel.id
+          response.should render_template("edit")
+        end
+      end
+      context 'js形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :edit, :id => @scroll_panel.id, :format => :js
+          response.should be_success 
+        end
+        it 'edit.jsテンプレートを描画する' do
+          get :edit, :id => @scroll_panel.id, :format => :js
+          response.should render_template("edit")
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :edit, :id => @scroll_panel.id
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          get :edit, :id => @scroll_panel.id
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'js形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          get :edit, :id => @scroll_panel.id, :format => :js
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          get :edit, :id => @scroll_panel.id, :format => :js
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+  end
+
+  describe '更新に於いて' do
+    before do
+      @scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @user.author.id
+      @attr = FactoryGirl.attributes_for(:scroll_panel, :author_id => @author.id)
+      sign_in @user
+    end
+    context 'つつがなく終わるとき' do
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          ScrollPanel.any_instance.stub(:store).and_return(true)
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+          response.status.should eq 302
+        end
+        it 'ストーリー表示へ遷移する' do
+#          ScrollPanel.any_instance.stub(:store).and_return(true)
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+          response.should redirect_to(:action => :scroll, :id => @attr[:scroll_id])
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+#          ScrollPanel.any_instance.stub(:store).and_return(true)
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr, :format => :json
+          response.should be_success 
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr, :format => :json
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          put :update, :id => @scroll_panel.id, :scroll_panel => @attr, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+  end
+
+  describe '削除に於いて' do
+    before do
+      @scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @author.id
+      sign_in @user
+      ScrollPanel.stub(:edit).and_return(@scroll_panel)
+    end
+    context 'つつがなく終わるとき' do
+      before do
+        ScrollPanel.any_instance.stub(:destroy_and_shorten).with(any_args()).and_return(true)
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          delete :destroy, :id => @scroll_panel.id
+          response.status.should eq 302
+        end
+        it '閲覧ページへ遷移する' do
+          delete :destroy, :id => @scroll_panel.id
+          response.should redirect_to(:controller => 'scroll_panels', :action => :scroll, :id => @scroll_panel.scroll_id)
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+          delete :destroy, :id => @scroll_panel.id, :format => :json
+          response.should be_success
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          delete :destroy, :id => @scroll_panel.id
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          delete :destroy, :id => @scroll_panel.id
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          delete :destroy, :id => @scroll_panel.id, :format => :json
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          delete :destroy, :id => @scroll_panel.id, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+  end
+  
+end
+end
diff --git a/spec/controllers/scrolls_controller_spec.rb b/spec/controllers/scrolls_controller_spec.rb
new file mode 100644 (file)
index 0000000..8ebf574
--- /dev/null
@@ -0,0 +1,1301 @@
+# -*- encoding: utf-8 -*-
+require 'spec_helper'
+#コミック
+describe ScrollsController 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
+    @user = FactoryGirl.create :user_yas
+    @author = FactoryGirl.create :author, :user_id => @user.id
+  end
+  
+if MagicNumber['run_mode'] == 1
+  describe '一覧表示に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @user.author.id
+      Scroll.stub(:list).and_return([@scroll, @scroll, @scroll])
+      sign_in @user
+    end
+    context '事前チェックする' do
+      it '与えられたpageがセットされている' do
+        get :index, :page => 5
+        assigns(:page).should eq 5
+      end
+      it '省略されると@pageに1値が入る' do
+        get :index
+        assigns(:page).should eq 1
+      end
+      it '与えられたpage_sizeがセットされている' do
+        get :index, :page_size => 15
+        assigns(:page_size).should eq 15
+      end
+      it '省略されると@page_sizeにデフォルト値が入る' do
+        get :index
+        assigns(:page_size).should eq Scroll.default_page_size
+      end
+      it '最大を超えると@page_sizeにデフォルト最大値が入る' do
+        get :index, :page_size => 1500
+        assigns(:page_size).should eq Scroll.max_page_size
+      end
+      it '不正な値が入ると@page_sizeにデフォルト最大値が入る' do
+        get :index, :page_size => 0
+        assigns(:page_size).should eq Scroll.default_page_size
+      end
+    end
+    context 'つつがなく終わるとき' do
+      it 'ステータスコード200 OKを返す' do
+        get :index
+        response.should be_success 
+      end
+      it 'コミックモデルに一覧を問い合わせている' do
+        Scroll.should_receive(:list).exactly(1)
+        get :index
+      end
+      it '@scrollsにリストを取得している' do
+        get :index
+        assigns(:scrolls).should have_at_least(3).items
+      end
+      context 'html形式' do
+        it '@paginateにページ制御を取得している' do
+          get :index
+          assigns(:paginate).is_a?(Kaminari::PaginatableArray).should be_true
+        end
+        it 'indexテンプレートを描画する' do
+          get :index
+          response.should render_template("index")
+        end
+      end
+      context 'json形式' do
+        it 'jsonデータを返す' do
+          get :index, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+        it 'コミックモデルにjson一覧出力オプションを問い合わせている' do
+          Scroll.should_receive(:list_json_opt).exactly(1)
+          get :index, :format => :json
+        end
+        it 'データがリスト構造になっている' do
+          get :index, :format => :json
+          json = JSON.parse response.body
+          json.should have_at_least(3).items
+        end
+        it 'リストの先頭くらいはコミックっぽいものであって欲しい' do
+          get :index, :format => :json
+          json = JSON.parse response.body
+          json.first.has_key?("title").should be_true
+          json.first.has_key?("visible").should be_true
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :index
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          get :index
+          response.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          get :index, :format => :json
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          get :index, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+    context 'ユーザ権限はないが管理者権限があるとき' do
+      before do
+        sign_out @user
+        sign_in @admin
+      end
+      it 'ステータスコード200 OKを返す' do
+        get :index
+        response.should be_success 
+      end
+    end
+    context 'ユーザだが作家登録していないとき' do
+      before do
+        @author.destroy
+      end
+      context 'html形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :index
+          response.should be_success 
+        end
+      end
+    end
+  end
+  
+  describe '単体表示に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @user.author.id, :title => 'normal'
+      Scroll.stub(:show).and_return(@scroll)
+      sign_in @user
+    end
+    context 'つつがなく終わるとき' do
+      it 'ステータスコード200 OKを返す' do
+        get :show, :id => @scroll.id
+        response.should be_success
+      end
+      it 'コミックモデルに単体取得を問い合わせている' do
+        Scroll.should_receive(:show).exactly(1)
+        get :show
+      end
+      it '@scrollにアレを取得している' do
+        get :show, :id => @scroll.id
+        assigns(:scroll).id.should eq(@scroll.id)
+      end
+      context 'html形式' do
+        it 'showテンプレートを描画する' do
+          get :show, :id => @scroll.id
+          response.should render_template("show")
+        end
+      end
+      context 'json形式' do
+        it 'jsonデータを返す' do
+          get :show, :id => @scroll.id, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+        it 'コミックモデルにjson単体出力オプションを問い合わせている' do
+          Scroll.should_receive(:show_json_opt).exactly(1)
+          get :show, :id => @scroll.id, :format => :json
+        end
+        it 'データがアレになっている' do
+          get :show, :id => @scroll.id, :format => :json
+          json = JSON.parse response.body
+          json["title"].should match(/normal/)
+          json["visible"].should_not be_nil
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :show, :id => @scroll.id
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          get :show, :id => @scroll.id
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          get :show, :id => @scroll.id, :format => :json
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          get :show, :id => @scroll.id, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+    context 'ユーザ権限はないが管理者権限があるとき' do
+      before do
+        sign_out @user
+        sign_in @admin
+      end
+      it 'ステータスコード200 OKを返す' do
+        get :show, :id => @scroll.id
+        response.should be_success 
+      end
+    end
+    context 'ユーザだが作家登録していないとき' do
+      before do
+        @author.destroy
+      end
+      context 'html形式' do
+        it 'ステータスコード200 OKを返す' do
+          get :show, :id => @scroll.id
+          response.should be_success
+        end
+      end
+    end
+=begin
+    context '対象コミックがないとき' do
+      context 'html形式' do
+        it '例外404 not_foundを返す' do
+          lambda{
+            get :show, :id => 0
+          }.should raise_error(ActiveRecord::RecordNotFound)
+        end
+      end
+      context 'json形式' do
+        it '例外404 not_foundを返す' do
+          lambda{ 
+            get :show, :id => 0, :format => :json
+          }.should raise_error(ActiveRecord::RecordNotFound)
+        end
+      end
+    end
+    context '非公開コミックを見ようとしたとき' do
+      context 'html形式' do
+        it '例外403 forbiddenを返す' do
+          Scroll.any_instance.stub(:visible?).with(any_args()).and_return(false)
+          hidden = FactoryGirl.create :hidden_scroll, :author_id => @author.id
+          lambda{
+            get :show, :id => hidden
+          }.should raise_error(ActiveRecord::Forbidden)
+        end
+      end
+      context 'json形式' do
+        it '例外403 forbiddenを返す' do
+          Scroll.any_instance.stub(:visible?).with(any_args()).and_return(false)
+          hidden = FactoryGirl.create :hidden_scroll, :author_id => @author.id
+          lambda{
+            get :show, :id => hidden, :format => :json
+          }.should raise_error(ActiveRecord::Forbidden)
+        end
+      end
+    end
+=end
+  end
+  describe 'コミック数取得に於いて' do
+    before do
+      Scroll.should_receive(:visible_count).and_return(3)
+#      sign_in @user
+    end
+    context 'つつがなく終わるとき' do
+      it 'ステータスコード200 OKを返す' do
+        get :count, :format => :json
+        response.should be_success 
+      end
+      context 'json形式' do
+        it 'jsonデータを返す' do
+          get :count, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+        it 'データがHash構造になっていてコミック数が1である' do
+          get :count, :format => :json
+          json = JSON.parse response.body
+          json["count"].should == 3
+        end
+      end
+    end
+  end
+
+  describe '新規作成フォーム表示に於いて' do
+    before do
+      sign_in @user
+    end
+    context 'つつがなく終わるとき' do
+      it 'ステータスコード200 OKを返す' do
+        get :new
+        response.should be_success 
+      end
+      it '@scrollに新規データを用意している' do
+        get :new
+        assigns(:scroll).should be_a_new(Scroll)
+      end
+      it 'コミックモデルにデフォルト値補充を依頼している' do
+        Scroll.any_instance.should_receive(:supply_default).exactly(1)
+        get :new
+      end
+      context 'html形式' do
+        it 'newテンプレートを描画する' do
+          get :new
+          response.should render_template("new")
+        end
+      end
+      context 'js形式' do
+        it 'new.jsテンプレートを描画する' do
+          get :new, :format => :js
+          response.should render_template("new")
+        end
+      end
+      context 'json形式' do
+        it 'jsonデータを返す' do
+          get :new, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+        it 'コミックモデルにjson単体出力オプションを問い合わせている' do
+          Scroll.should_receive(:show_json_opt).exactly(1)
+          get :new, :format => :json
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :new
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          get :new
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'js形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          get :new, :format => :js
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          get :new, :format => :js
+          response.message.should match(/Unauthorized/)
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          get :new, :format => :json
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          get :new, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+    context 'ユーザ権限はないが管理者権限があるとき' do
+      before do
+        sign_out @user
+        sign_in @admin
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :new
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          get :new
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+    end
+    context 'ユーザだが作家登録していないとき' do
+      before do
+        @author.destroy
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :new, @attr
+          response.status.should eq 302
+        end
+        it '作家登録ページへ遷移する' do
+          get :new, @attr
+          response.body.should redirect_to new_author_path
+        end
+      end
+    end
+  end
+
+  describe '新規作成に於いて' do
+    before do
+      sign_in @user
+      @attr = FactoryGirl.attributes_for(:scroll, :author_id => @author.id, :title => 'normal')
+    end
+    context '事前チェックしておく' do
+      it 'コミックモデルにデフォルト値補充を依頼している' do
+        Scroll.any_instance.should_receive(:supply_default).exactly(1)
+        post :create, :scroll => @attr
+      end
+      it 'コミックモデルにカラム値復元を依頼している' do
+        Scroll.any_instance.should_receive(:attributes=).exactly(1)
+        post :create, :scroll => @attr
+      end
+      it 'コミックモデルに上書き補充を依頼している' do
+        Scroll.any_instance.should_receive(:overwrite).exactly(1)
+        post :create, :scroll => @attr
+      end
+      it 'モデルに保存依頼する' do
+        Scroll.any_instance.should_receive(:save).exactly(1)
+        post :create, :scroll => @attr
+      end
+    end
+    context 'つつがなく終わるとき' do
+      it "@scrollに作成されたコミックを保持していて、それがDBにある" do
+        post :create, :scroll => @attr
+        assigns(:scroll).should be_a(Scroll)
+        assigns(:scroll).should be_persisted
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          Scroll.any_instance.stub(:save).and_return(true)
+          post :create, :scroll => @attr
+          response.status.should eq 302
+        end
+        it '作成されたコミックの表示ページへ遷移する' do
+#          Scroll.any_instance.stub(:save).and_return(true)
+          post :create, :scroll => @attr
+          response.should redirect_to(Scroll.last)
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+#          Scroll.any_instance.stub(:save).and_return(true)
+          post :create, :scroll => @attr, :format => :json
+          response.should be_success 
+        end
+        it '作成されたコミックをjsonデータで返す' do
+          post :create, :scroll => @attr, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+        it 'データがアレになっている' do
+          post :create, :scroll => @attr, :format => :json
+          json = JSON.parse response.body
+          json["title"].should match(/normal/)
+          json["visible"].should_not be_nil
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          post :create, :scroll => @attr
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          post :create, :scroll => @attr
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          post :create, :scroll => @attr, :format => :json
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          post :create, :scroll => @attr, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+    context 'ユーザ権限はないが管理者権限があるとき' do
+      before do
+        sign_out @user
+        sign_in @admin
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          post :create, :scroll => @attr
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          post :create, :scroll => @attr
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+    end
+    context 'ユーザだが作家登録していないとき' do
+      before do
+        @author.destroy
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          post :create, :scroll => @attr
+          response.status.should eq 302
+        end
+        it '作家登録ページへ遷移する' do
+          post :create, :scroll => @attr
+          response.body.should redirect_to new_author_path
+        end
+      end
+    end
+    context '検証、保存に失敗した' do
+      before do
+        Scroll.any_instance.stub(:save).and_return(false)
+      end
+      it "未保存のコミックを保持している" do
+        post :create, :scroll => @attr
+        assigns(:scroll).should be_a_new(Scroll)
+      end
+      context 'html形式' do
+        it 'ステータスコード200 OKを返す' do
+          post :create, :scroll => @attr
+          response.status.should eq 200
+        end
+        it '新規ページを描画する' do
+          post :create, :scroll => @attr
+          response.should render_template("new")
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード422 unprocessable_entity を返す' do
+          post :create, :scroll => @attr, :format => :json
+          response.status.should eq 422
+        end
+        it '応答メッセージUnprocessable Entityを返す' do
+          post :create, :scroll => @attr, :format => :json
+          response.message.should match(/Unprocessable/)
+        end
+      end
+    end
+  end
+
+  describe '編集フォーム表示に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @user.author.id
+      sign_in @user
+      Scroll.stub(:edit).and_return(@scroll)
+    end
+    context 'つつがなく終わるとき' do
+      it 'ステータスコード200 OKを返す' do
+        get :edit, :id => @scroll.id
+        response.should be_success 
+      end
+      it 'コミックモデルに編集取得を問い合わせている' do
+        Scroll.should_receive(:edit).exactly(1)
+        get :edit, :id => @scroll.id
+      end
+      it '@scrollにデータを用意している' do
+        get :edit, :id => @scroll.id
+        assigns(:scroll).should eq @scroll
+      end
+      context 'html形式' do
+        it 'editテンプレートを描画する' do
+          get :edit, :id => @scroll.id
+          response.should render_template("edit")
+        end
+      end
+      context 'js形式' do
+        it 'edit.jsテンプレートを描画する' do
+          get :edit, :id => @scroll.id, :format => :js
+          response.should render_template("edit")
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :edit, :id => @scroll.id
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          get :edit, :id => @scroll.id
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'js形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          get :edit, :id => @scroll.id, :format => :js
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          get :edit, :id => @scroll.id, :format => :js
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+    context 'ユーザ権限はないが管理者権限があるとき' do
+      before do
+        sign_out @user
+        sign_in @admin
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :edit, :id => @scroll.id
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          get :edit, :id => @scroll.id
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+    end
+    context 'ユーザだが作家登録していないとき' do
+      before do
+        @author.destroy
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :edit, :id => @scroll.id
+          response.status.should eq 302
+        end
+        it '作家登録ページへ遷移する' do
+          get :edit, :id => @scroll.id
+          response.body.should redirect_to new_author_path
+        end
+      end
+    end
+  end
+
+  describe '更新に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author => @author
+      @attr = FactoryGirl.attributes_for(:scroll, :author_id => @author.id, :title => 'updated title', :visible => 0)
+      sign_in @user
+    end
+    context '事前チェックしておく' do
+      it 'コミックモデルに編集取得を問い合わせている' do
+        Scroll.stub(:edit).with(any_args()).and_return @scroll
+        Scroll.should_receive(:edit).exactly(1)
+        put :update, :id => @scroll.id, :scroll => @attr
+      end
+      it 'コミックモデルにカラム値復元を依頼している' do
+        Scroll.any_instance.should_receive(:attributes=).exactly(1)
+        put :update, :id => @scroll.id, :scroll => @attr
+      end
+      it 'コミックモデルに上書き補充を依頼している' do
+        Scroll.any_instance.should_receive(:overwrite).exactly(1)
+        put :update, :id => @scroll.id, :scroll => @attr
+      end
+      it 'モデルに更新を依頼する' do
+        Scroll.any_instance.stub(:save).with(any_args).and_return true
+        Scroll.any_instance.should_receive(:save).exactly(1)
+        put :update, :id => @scroll.id, :scroll => @attr
+      end
+      it '@scrollにアレを取得している' do
+        put :update, :id => @scroll.id, :scroll => @attr
+        assigns(:scroll).id.should eq(@scroll.id)
+      end
+    end
+    context 'つつがなく終わるとき' do
+      it '更新される' do
+        put :update, :id => @scroll.id, :scroll => @attr
+        Scroll.find(@scroll.id).visible.should eq 0
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          Scroll.any_instance.stub(:save).with(any_args()).and_return(true)
+          put :update, :id => @scroll.id, :scroll => @attr
+          response.status.should eq 302
+        end
+        it '更新されたコミックの表示ページへ遷移する' do
+          put :update, :id => @scroll.id, :scroll => @attr
+          response.should redirect_to(@scroll)
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+          Scroll.any_instance.stub(:save).with(any_args()).and_return(true)
+          put :update, :id => @scroll.id, :scroll => @attr, :format => :json
+          response.should be_success 
+        end
+        it 'ページ本体は特に返さない' do
+          Scroll.any_instance.stub(:save).with(any_args()).and_return(true)
+          put :update, :id => @scroll.id, :scroll => @attr, :format => :json
+          response.body.should match /./
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          put :update, :id => @scroll.id, :scroll => @attr
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          put :update, :id => @scroll.id, :scroll => @attr
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it '応答メッセージにUnauthorizedを返す' do
+          put :update, :id => @scroll.id, :scroll => @attr, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+    context 'ユーザ権限はないが管理者権限があるとき' do
+      before do
+        sign_out @user
+        sign_in @admin
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          put :update, :id => @scroll.id, :scroll => @attr
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          put :update, :id => @scroll.id, :scroll => @attr
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+    end
+    context 'ユーザだが作家登録していないとき' do
+      before do
+        @author.destroy
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          put :update, :id => @scroll.id, :scroll => @attr
+          response.status.should eq 302
+        end
+        it '作家登録ページへ遷移する' do
+          put :update, :id => @scroll.id, :scroll => @attr
+          response.body.should redirect_to new_author_path
+        end
+      end
+    end
+    context '検証、保存に失敗したとき' do
+      before do
+        Scroll.any_instance.stub(:save).and_return(false)
+      end
+      context 'html形式' do
+        it 'ステータスコード200 Okを返す' do
+          put :update, :id => @scroll.id, :scroll => @attr
+          response.status.should eq 200
+        end
+        it '編集ページを描画する' do
+          put :update, :id => @scroll.id, :scroll => @attr
+          response.should render_template("edit")
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード422 unprocessable_entity を返す' do
+          Scroll.any_instance.stub(:save).and_return(false)
+          put :update, :id => @scroll.id, :scroll => @attr, :format => :json
+          response.status.should eq 422
+        end
+        it '応答メッセージUnprocessable Entityを返す' do
+          put :update, :id => @scroll.id, :scroll => @attr, :format => :json
+          response.message.should match(/Unprocessable/)
+        end
+      end
+    end
+  end
+
+  describe '削除に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author => @author
+      sign_in @user
+    end
+    context '事前チェックしておく' do
+      before do
+        Scroll.stub(:edit).with(any_args()).and_return @scroll
+        Scroll.any_instance.stub(:destroy_with_scroll_panel).with(any_args()).and_return(true)
+      end
+      it 'コミックモデルに編集取得を問い合わせている' do
+        Scroll.should_receive(:edit).exactly(1)
+        delete :destroy, :id => @scroll.id
+      end
+      it 'モデルに削除を依頼する' do
+        Scroll.any_instance.should_receive(:destroy_with_scroll_panel).exactly(1)
+        delete :destroy, :id => @scroll.id
+      end
+      it '@scrollにアレを取得している' do
+        delete :destroy, :id => @scroll.id
+        assigns(:scroll).id.should eq(@scroll.id)
+      end
+    end
+    context 'つつがなく終わるとき' do
+      it '削除される' do
+        lambda {
+          delete :destroy, :id => @scroll.id
+        }.should change Scroll, :count
+      end
+      context 'html形式' do
+        before do
+          Scroll.any_instance.stub(:destroy_with_scroll_panel).with(any_args()).and_return(true)
+        end
+        it 'ステータスコード302 Foundを返す' do
+          delete :destroy, :id => @scroll.id
+          response.status.should eq 302
+        end
+        it 'マイコミックの一覧ページへ遷移する' do
+          delete :destroy, :id => @scroll.id
+          response.should redirect_to('/home/scrolls')
+        end
+      end
+      context 'json形式' do
+        before do
+          Scroll.any_instance.stub(:destroy_with_scroll_panel).with(any_args()).and_return(true)
+        end
+        it 'ステータスコード200 OKを返す' do
+          delete :destroy, :id => @scroll.id, :format => :json
+          response.should be_success 
+        end
+        it 'ページ本体は特に返さない' do
+          delete :destroy, :id => @scroll.id, :format => :json
+          response.body.should match /./
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          delete :destroy, :id => @scroll.id
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          delete :destroy, :id => @scroll.id
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it '応答メッセージにUnauthorizedを返す' do
+          delete :destroy, :id => @scroll.id, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+    context 'ユーザ権限はないが管理者権限があるとき' do
+      before do
+        sign_out @user
+        sign_in @admin
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          delete :destroy, :id => @scroll.id
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          delete :destroy, :id => @scroll.id
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+    end
+    context 'ユーザだが作家登録していないとき' do
+      before do
+        @author.destroy
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          delete :destroy, :id => @scroll.id
+          response.status.should eq 302
+        end
+        it '作家登録ページへ遷移する' do
+          delete :destroy, :id => @scroll.id
+          response.body.should redirect_to new_author_path
+        end
+      end
+    end
+    context '削除に失敗したとき' do
+      before do
+        Scroll.any_instance.stub(:destroy_with_scroll_panel).and_return(false)
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          delete :destroy, :id => @scroll.id
+          response.status.should eq 302
+        end
+        it 'そのコミックの詳細ページへ遷移する' do
+          delete :destroy, :id => @scroll.id
+          response.should redirect_to(scroll_path(@scroll))
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード422 unprocessable_entity を返す' do
+          delete :destroy, :id => @scroll.id, :format => :json
+          response.status.should eq 422
+        end
+        it '応答メッセージUnprocessable Entityを返す' do
+          delete :destroy, :id => @scroll.id, :format => :json
+          response.message.should match(/Unprocessable/)
+        end
+      end
+    end
+  end
+
+else
+  describe '一覧表示に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @user.author.id
+      Scroll.stub(:list).and_return([@scroll, @scroll, @scroll])
+      sign_in @user
+    end
+    context 'つつがなく終わるとき' do
+      it 'ステータスコード200 OKを返す' do
+        get :index
+        response.should be_success 
+      end
+      context 'html形式' do
+        it 'indexテンプレートを描画する' do
+          get :index
+          response.should render_template("index")
+        end
+      end
+      context 'json形式' do
+        it 'jsonデータを返す' do
+          get :index, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      it 'ステータスコード200 OKを返す' do
+        get :index
+        response.should be_success 
+      end
+      context 'html形式' do
+        it 'indexテンプレートを描画する' do
+          get :index
+          response.should render_template("index")
+        end
+      end
+      context 'json形式' do
+        it 'jsonデータを返す' do
+          get :index, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+      end
+    end
+  end
+  
+  describe '単体表示に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @user.author.id, :title => 'normal'
+      Scroll.stub(:show).and_return(@scroll)
+      sign_in @user
+    end
+    context 'つつがなく終わるとき' do
+      it 'ステータスコード200 OKを返す' do
+        get :show, :id => @scroll.id
+        response.should be_success
+      end
+      context 'html形式' do
+        it 'showテンプレートを描画する' do
+          get :show, :id => @scroll.id
+          response.should render_template("show")
+        end
+      end
+      context 'json形式' do
+        it 'jsonデータを返す' do
+          get :show, :id => @scroll.id, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      it 'ステータスコード200 OKを返す' do
+        get :show, :id => @scroll.id
+        response.should be_success
+      end
+      context 'html形式' do
+        it 'showテンプレートを描画する' do
+          get :show, :id => @scroll.id
+          response.should render_template("show")
+        end
+      end
+      context 'json形式' do
+        it 'jsonデータを返す' do
+          get :show, :id => @scroll.id, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+      end
+    end
+  end
+  describe 'コミック数取得に於いて' do
+    before do
+      Scroll.should_receive(:visible_count).and_return(3)
+      sign_out @user
+    end
+    context 'つつがなく終わるとき' do
+      it 'ステータスコード200 OKを返す' do
+        get :count, :format => :json
+        response.should be_success 
+      end
+      context 'json形式' do
+        it 'jsonデータを返す' do
+          get :count, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+      end
+    end
+  end
+
+  describe '新規作成フォーム表示に於いて' do
+    before do
+      sign_in @user
+    end
+    context 'つつがなく終わるとき' do
+      it 'ステータスコード200 OKを返す' do
+        get :new
+        response.should be_success 
+      end
+      context 'html形式' do
+        it 'newテンプレートを描画する' do
+          get :new
+          response.should render_template("new")
+        end
+      end
+      context 'js形式' do
+        it 'new.jsテンプレートを描画する' do
+          get :new, :format => :js
+          response.should render_template("new")
+        end
+      end
+      context 'json形式' do
+        it 'jsonデータを返す' do
+          get :new, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :new
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          get :new
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'js形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          get :new, :format => :js
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          get :new, :format => :js
+          response.message.should match(/Unauthorized/)
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          get :new, :format => :json
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          get :new, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+  end
+
+  describe '新規作成に於いて' do
+    before do
+      sign_in @user
+      @attr = FactoryGirl.attributes_for(:scroll, :author_id => @author.id, :title => 'normal')
+    end
+    context 'つつがなく終わるとき' do
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          Scroll.any_instance.stub(:save).and_return(true)
+          post :create, :scroll => @attr
+          response.status.should eq 302
+        end
+        it '作成されたコミックの表示ページへ遷移する' do
+#          Scroll.any_instance.stub(:save).and_return(true)
+          post :create, :scroll => @attr
+          response.should redirect_to(Scroll.last)
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+#          Scroll.any_instance.stub(:save).and_return(true)
+          post :create, :scroll => @attr, :format => :json
+          response.should be_success 
+        end
+        it '作成されたコミックをjsonデータで返す' do
+          post :create, :scroll => @attr, :format => :json
+          lambda{JSON.parse(response.body)}.should_not raise_error(JSON::ParserError)
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          post :create, :scroll => @attr
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          post :create, :scroll => @attr
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          post :create, :scroll => @attr, :format => :json
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          post :create, :scroll => @attr, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+  end
+
+  describe '編集フォーム表示に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @user.author.id
+      sign_in @user
+      Scroll.stub(:edit).and_return(@scroll)
+    end
+    context 'つつがなく終わるとき' do
+      it 'ステータスコード200 OKを返す' do
+        get :edit, :id => @scroll.id
+        response.should be_success 
+      end
+      context 'html形式' do
+        it 'editテンプレートを描画する' do
+          get :edit, :id => @scroll.id
+          response.should render_template("edit")
+        end
+      end
+      context 'js形式' do
+        it 'edit.jsテンプレートを描画する' do
+          get :edit, :id => @scroll.id, :format => :js
+          response.should render_template("edit")
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          get :edit, :id => @scroll.id
+          response.status.should eq 302
+        end
+        it 'サインインページへ遷移する' do
+          get :edit, :id => @scroll.id
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'js形式' do
+        it 'ステータスコード401 Unauthorizedを返す' do
+          get :edit, :id => @scroll.id, :format => :js
+          response.status.should eq 401
+        end
+        it '応答メッセージにUnauthorizedを返す' do
+          get :edit, :id => @scroll.id, :format => :js
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+  end
+
+  describe '更新に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author => @author
+      @attr = FactoryGirl.attributes_for(:scroll, :author_id => @author.id, :title => 'updated title', :visible => 0)
+      sign_in @user
+    end
+    context 'つつがなく終わるとき' do
+      context 'html形式' do
+        it 'ステータスコード302 Foundを返す' do
+          Scroll.any_instance.stub(:save).with(any_args()).and_return(true)
+          put :update, :id => @scroll.id, :scroll => @attr
+          response.status.should eq 302
+        end
+        it '更新されたコミックの表示ページへ遷移する' do
+          put :update, :id => @scroll.id, :scroll => @attr
+          response.should redirect_to(@scroll)
+        end
+      end
+      context 'json形式' do
+        it 'ステータスコード200 OKを返す' do
+          Scroll.any_instance.stub(:save).with(any_args()).and_return(true)
+          put :update, :id => @scroll.id, :scroll => @attr, :format => :json
+          response.should be_success 
+        end
+        it 'ページ本体は特に返さない' do
+          Scroll.any_instance.stub(:save).with(any_args()).and_return(true)
+          put :update, :id => @scroll.id, :scroll => @attr, :format => :json
+          response.body.should match /./
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      it 'ステータスコード302 Foundを返す' do
+        put :update, :id => @scroll.id, :scroll => @attr
+        response.status.should eq 302
+      end
+      context 'html形式' do
+        it 'サインインページへ遷移する' do
+          put :update, :id => @scroll.id, :scroll => @attr
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+    end
+  end
+
+  describe '削除に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author => @author
+      sign_in @user
+    end
+    context 'つつがなく終わるとき' do
+      context 'html形式' do
+        before do
+          Scroll.any_instance.stub(:destroy_with_scroll_panel).with(any_args()).and_return(true)
+        end
+        it 'ステータスコード302 Foundを返す' do
+          delete :destroy, :id => @scroll.id
+          response.status.should eq 302
+        end
+        it 'マイコミックの一覧ページへ遷移する' do
+          delete :destroy, :id => @scroll.id
+          response.should redirect_to('/home/scroll')
+        end
+      end
+      context 'json形式' do
+        before do
+          Scroll.any_instance.stub(:destroy_with_scroll_panel).with(any_args()).and_return(true)
+        end
+        it 'ステータスコード200 OKを返す' do
+          delete :destroy, :id => @scroll.id, :format => :json
+          response.should be_success 
+        end
+        it 'ページ本体は特に返さない' do
+          delete :destroy, :id => @scroll.id, :format => :json
+          response.body.should match /./
+        end
+      end
+    end
+    context 'ユーザ権限がないとき' do
+      before do
+        sign_out @user
+      end
+      it 'ステータスコード302 Foundを返す' do
+        delete :destroy, :id => @scroll.id
+        response.status.should eq 302
+      end
+      context 'html形式' do
+        it 'サインインページへ遷移する' do
+          delete :destroy, :id => @scroll.id
+          response.body.should redirect_to '/users/sign_in'
+        end
+      end
+      context 'json形式' do
+        it '応答メッセージにUnauthorizedを返す' do
+          delete :destroy, :id => @scroll.id, :format => :json
+          response.message.should match(/Unauthorized/)
+        end
+      end
+    end
+  end
+
+end
+
+end
diff --git a/spec/helpers/scroll_panels_helper_spec.rb b/spec/helpers/scroll_panels_helper_spec.rb
new file mode 100644 (file)
index 0000000..90a74e8
--- /dev/null
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+# Specs in this file have access to a helper object that includes
+# the ScrollPanelsHelper. For example:
+#
+# describe ScrollPanelsHelper do
+#   describe "string concat" do
+#     it "concats two strings with spaces" do
+#       helper.concat_strings("this","that").should == "this that"
+#     end
+#   end
+# end
+describe ScrollPanelsHelper do
+  pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/helpers/scrolls_helper_spec.rb b/spec/helpers/scrolls_helper_spec.rb
new file mode 100644 (file)
index 0000000..b575131
--- /dev/null
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+# Specs in this file have access to a helper object that includes
+# the ScrollsHelper. For example:
+#
+# describe ScrollsHelper do
+#   describe "string concat" do
+#     it "concats two strings with spaces" do
+#       helper.concat_strings("this","that").should == "this that"
+#     end
+#   end
+# end
+describe ScrollsHelper do
+  pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/models/scroll_panel_spec.rb b/spec/models/scroll_panel_spec.rb
new file mode 100644 (file)
index 0000000..bc3fcb2
--- /dev/null
@@ -0,0 +1,1940 @@
+# -*- encoding: utf-8 -*-
+require 'spec_helper'
+#ストーリー
+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
+    @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
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @scroll_panel = FactoryGirl.build :scroll_panel, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+    end
+    
+    context 'オーソドックスなデータのとき' do
+      it '下限データが通る' do
+        @scroll_panel.t = 0
+        @scroll_panel.should be_valid
+      end
+      it '上限データが通る' do
+        @scroll_panel.t = 99999
+        @scroll_panel.should be_valid
+      end
+    end
+    
+    context 'scroll_idを検証するとき' do
+      it 'nullなら失敗する' do
+        @scroll_panel.scroll_id = nil
+        @scroll_panel.should_not be_valid
+      end
+      it '数値でなければ失敗する' do
+        @scroll_panel.scroll_id = 'a'
+        @scroll_panel.should_not be_valid
+      end
+      it '存在するコミックでなければ失敗する' do
+        @scroll_panel.scroll_id = 0
+        @scroll_panel.should_not be_valid
+      end
+    end
+    
+    context 'panel_idを検証するとき' do
+      it 'nullなら失敗する' do
+        @scroll_panel.panel_id = nil
+        @scroll_panel.should_not be_valid
+      end
+      it '数値でなければ失敗する' do
+        @scroll_panel.panel_id = 'a'
+        @scroll_panel.should_not be_valid
+      end
+      it '存在するコマでなければ失敗する' do
+        @scroll_panel.panel_id = 0
+        @scroll_panel.should_not be_valid
+      end
+    end
+    
+    context 'tを検証するとき' do
+      it 'nullなら失敗する' do
+        @scroll_panel.t = nil
+        @scroll_panel.should_not be_valid
+      end
+      it '数値でなければ失敗する' do
+        @scroll_panel.t = 'a'
+        @scroll_panel.should_not be_valid
+      end
+      it '負なら失敗する' do
+        @scroll_panel.t = -1
+        @scroll_panel.should_not be_valid
+      end
+    end
+    
+    context 'author_idを検証するとき' do
+      it 'nullなら失敗する' do
+        @scroll_panel.author_id = nil
+        @scroll_panel.should_not be_valid
+      end
+      it '数値でなければ失敗する' do
+        @scroll_panel.author_id = 'a'
+        @scroll_panel.should_not be_valid
+      end
+      it '存在する作家でなければ失敗する' do
+        @scroll_panel.author_id = 0
+        @scroll_panel.should_not be_valid
+      end
+    end
+    context '全体を検証するとき' do
+    end
+  end
+  
+  describe 'デフォルト値補充に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+    end
+    
+    #dbのデフォルト値が0だから明示的にnilにしないと追加ができない
+    it 'tをnilにする' do
+      @scroll_panel = FactoryGirl.build :scroll_panel, :scroll_id => @scroll.id, :panel_id => @panel.id
+      @scroll_panel.supply_default
+      @scroll_panel.t.should be_nil
+    end
+    
+  end
+  
+  describe '上書き補充に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+    end
+    
+    context 'author_idを補充' do
+      it '問答無用でauthor_idを補充する' do
+        @scroll_panel = FactoryGirl.build :scroll_panel, :scroll_id => @scroll.id, :panel_id => @panel.id
+        @scroll_panel.author_id = nil
+        @scroll_panel.overwrite @author
+        @scroll_panel.author_id.should eq @author.id
+      end
+    end
+    
+  end
+  
+  describe '所持判定に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id
+      @scrollo = FactoryGirl.create :scroll, :author_id => @other_author.id
+      @panelo = FactoryGirl.create :panel, :author_id => @other_author.id
+      @scroll_panelo = FactoryGirl.create :scroll_panel, :author_id => @other_author.id, :scroll_id => @scrollo.id, :panel_id => @panelo.id
+    end
+    context '事前チェックする' do
+      it '自身にロールリストからの作家取得を依頼している' do
+        ScrollPanel.should_receive(:get_author_from_roles).with(any_args).exactly(1)
+        r = @scroll_panel.own?([@author])
+      end
+    end
+    context 'ロール内作家が取得できるとき' do
+      before do
+      end
+      it 'ロール内作家のidが自身の作家idと一致するなら許可する' do
+        ScrollPanel.stub(:get_author_from_roles).with(any_args).and_return(@author)
+        r = @scroll_panel.own?([@author])
+        r.should be_true
+      end
+      it 'ロール内作家のidが自身の作家idと一致しないならno' do
+        ScrollPanel.stub(:get_author_from_roles).with(any_args).and_return(@other_author)
+        @scroll_panel.own?(@other_author).should be_false
+      end
+    end
+    context 'ロール内作家が取得できないとき' do
+      before do
+        ScrollPanel.stub(:get_author_from_roles).with(any_args).and_return(nil)
+      end
+      it 'Falseを返す' do
+        r = @scroll_panel.own?([@author])
+        r.should be_false
+      end
+    end
+  end
+  
+  describe '閲覧許可に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id
+    end
+    context 'オープンモードのとき' do
+      before do
+        MagicNumber['run_mode'] = 0
+      end
+      it '自身にゲスト用ロールチェックを問い合わせしている' do
+        ScrollPanel.any_instance.stub(:guest_role_check).and_return(true)
+        ScrollPanel.any_instance.should_receive(:guest_role_check).with(any_args).exactly(1)
+        r = @scroll_panel.visible?([@author])
+      end
+      it 'ゲスト用ロールチェックが失敗したとき、falseを返す' do
+        ScrollPanel.any_instance.stub(:guest_role_check).and_return(false)
+        r = @scroll_panel.visible?([@author])
+        r.should be_false
+      end
+    end
+    context 'クローズドモードのとき' do
+      before do
+        MagicNumber['run_mode'] = 1
+      end
+      it '自身に読者用ロールチェックを問い合わせしている' do
+        ScrollPanel.any_instance.stub(:reader_role_check).and_return(true)
+        ScrollPanel.any_instance.should_receive(:reader_role_check).with(any_args).exactly(1)
+        r = @scroll_panel.visible?([@author])
+      end
+      it '読者用ロールチェックが失敗したとき、falseを返す' do
+        ScrollPanel.any_instance.stub(:reader_role_check).and_return(false)
+        r = @scroll_panel.visible?([@author])
+        r.should be_false
+      end
+    end
+    context '事前チェックする' do
+      before do
+        MagicNumber['run_mode'] = 1
+        ScrollPanel.any_instance.stub(:reader_role_check).and_return(true)
+      end
+      it '自身のコミックに所持判定を問い合わせしている' 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 '自身のコミックに閲覧許可を問い合わせしている' 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)
+        r = @scroll_panel.visible?([@author])
+      end
+    end
+    context 'つつがなく終わるとき' do
+      before do
+        MagicNumber['run_mode'] = 1
+        ScrollPanel.any_instance.stub(:reader_role_check).and_return(true)
+      end
+      it '自分のコミックのストーリーなら許可する' 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 '他人の非公開コミックのストーリーなら許可しない' 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 '他人のコミックのストーリーでも公開なら許可する' do
+        Scroll.any_instance.stub(:own?).and_return(false)
+        Scroll.any_instance.stub(:visible).and_return(1)
+        r = @scroll_panel.visible?([@author])
+        r.should be_true
+      end
+    end
+  end
+  
+  describe 'プレイリスト取得に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id
+      @panel2 = FactoryGirl.create :panel, :author_id => @author.id, :publish => 0
+      @other_scroll = FactoryGirl.create :scroll, :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
+        ScrollPanel.offset(100, '8').should eq 8
+      end
+      it 'nilの場合は0になる' do
+        ScrollPanel.offset(100).should eq 0
+      end
+      #投稿されたコマ数以上の値が指定されたときは、最後のコマだけになる
+      #最後のコマとは、コマ数‐1.
+      it '1件のときオフセット1なら0になる' do
+        ScrollPanel.offset(1, '1').should eq 0
+      end
+      it '5件のときオフセット5なら4になる' do
+        ScrollPanel.offset(5, '5').should eq 4
+      end
+      # 負の値が指定されたときは、最後のコマから数えてコマを飛ばして表示する。
+      #-4のときは、最後から4つのコマを表示する。 
+      it '2件のときオフセット-1なら1になる' do
+        ScrollPanel.offset(2, '-1').should eq 1
+      end
+      it '5件のときオフセット-2なら3になる' do
+        ScrollPanel.offset(5, '-2').should eq 3
+      end
+      # 最終的なが負になるなど、不正な値が入ったときは0となる。 
+      it '2件のときオフセット-5なら0になる' do
+        ScrollPanel.offset(2, '-5').should eq 0
+      end
+    end
+    context 'panel_count補正について' do
+      it '文字列から数値に変換される' do
+        ScrollPanel.panel_count(100, '7').should eq 7
+      end
+      it 'nilの場合はScrollPanel.default_panel_sizeになる' do
+        ScrollPanel.panel_count(100).should eq ScrollPanel.default_panel_size
+      end
+      it '0以下の場合はScrollPanel.default_panel_sizeになる' do
+        ScrollPanel.panel_count(100, '0').should eq ScrollPanel.default_panel_size
+      end
+      it 'ScrollPanel.max_panel_sizeを超えた場合はScrollPanel.max_panel_sizeになる' do
+        ScrollPanel.panel_count(100, '1000').should eq ScrollPanel.max_panel_size
+      end
+    end
+    it 'リストを返す' do
+      c = ScrollPanel.play_list @scroll, @author
+      c.should eq [@scroll_panel]
+    end
+    it 't順で並んでいる' do
+      #公開コミックの公開コマは(他人のコミックであっても)含んでいる
+      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]
+    end
+    it '非公開のコマは含んでいる' do
+      h = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel2.id, :t => 1
+      c = ScrollPanel.play_list @scroll, @author
+      c.should eq [ @scroll_panel, h]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id, :t => 1
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id, :t => 2
+        @scroll_panel4 = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id, :t => 3
+        @scroll_panel5 = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id, :t => 4
+      end
+      it 'offset=0なら末尾2件を返す' do
+        #時系列で並んでいる
+        c = ScrollPanel.play_list( @scroll, @author, 0, 2)
+        c.should eq [@scroll_panel, @scroll_panel2]
+      end
+      it 'offset=2なら中間2件を返す' do
+        c = ScrollPanel.play_list(@scroll, @author, 2, 2)
+        c.should eq [@scroll_panel3, @scroll_panel4]
+      end
+      it 'offset=4なら先頭1件を返す' do
+        c = ScrollPanel.play_list(@scroll, @author, 4, 2)
+        c.should eq [@scroll_panel5]
+      end
+    end
+  end
+  
+  describe '一覧取得に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id, :visible => 1
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id
+      @other_scroll = FactoryGirl.create :scroll, :author_id => @other_author.id, :visible => 1
+      @other_panel = FactoryGirl.create :panel, :author_id => @other_author.id, :publish => 1
+      @hidden_scroll = FactoryGirl.create :scroll, :author_id => @author.id, :visible => 0
+    end
+    context 'page補正について' do
+      it '文字列から数値に変換される' do
+        ScrollPanel.page('8').should eq 8
+      end
+      it 'nilの場合は1になる' do
+        ScrollPanel.page().should eq 1
+      end
+      it '0以下の場合は1になる' do
+        ScrollPanel.page('0').should eq 1
+      end
+    end
+    context 'page_size補正について' do
+      it '文字列から数値に変換される' do
+        ScrollPanel.page_size('7').should eq 7
+      end
+      it 'nilの場合はScrollPanel.default_page_sizeになる' do
+        ScrollPanel.page_size().should eq ScrollPanel.default_page_size
+      end
+      it '0以下の場合はScrollPanel.default_page_sizeになる' do
+        ScrollPanel.page_size('0').should eq ScrollPanel.default_page_size
+      end
+      it 'ScrollPanel.max_page_sizeを超えた場合はScrollPanel.max_page_sizeになる' do
+        ScrollPanel.page_size('1000').should eq ScrollPanel.max_page_size
+      end
+    end
+    it 'リストを返す' do
+      c = ScrollPanel.list
+      c.should eq [@scroll_panel]
+    end
+    it '時系列で並んでいる' do
+      #公開コミックの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 '非公開のストーリーは(自分のストーリーであっても)含まない' 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]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id, :t => 1, :updated_at => Time.now + 100
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id, :t => 2, :updated_at => Time.now + 200
+        @scroll_panel4 = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id, :t => 3, :updated_at => Time.now + 300
+        @scroll_panel5 = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id, :t => 4, :updated_at => Time.now + 400
+      end
+      it '通常は2件を返す' do
+        l = ScrollPanel.list 1, 2
+        l.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        l = ScrollPanel.list 1, 2
+        l.should eq [@scroll_panel5, @scroll_panel4]
+      end
+      it 'page=2なら中間2件を返す' do
+        l = ScrollPanel.list 2, 2
+        l.should eq [@scroll_panel3, @scroll_panel2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        l = ScrollPanel.list 3, 2
+        l.should eq [@scroll_panel]
+      end
+    end
+  end
+  
+  describe '自分のストーリー一覧取得に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id, :visible => 1
+      @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
+      @other_scroll = FactoryGirl.create :scroll, :author_id => @other_author.id, :visible => 1
+      @other_panel = FactoryGirl.create :panel, :author_id => @other_author.id, :publish => 1
+      @hscroll = FactoryGirl.create :scroll, :author_id => @author.id, :visible => 0
+      @hpanel = FactoryGirl.create :panel, :author_id => @author.id, :publish => 0
+    end
+    context 'つつがなく終わるとき' do
+      it '一覧取得オプションを利用している' do
+        ScrollPanel.stub(:list_opt).with(any_args).and_return({})
+        ScrollPanel.should_receive(:list_opt).with(any_args).exactly(1)
+        r = ScrollPanel.mylist @author
+      end
+    end
+    it 'リストを返す' do
+      s = ScrollPanel.mylist @author
+      s.should eq [@scroll_panel]
+    end
+    it '時系列で並んでいる' do
+      ns = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id, :t => 1, :updated_at => Time.now + 100
+      sl = ScrollPanel.mylist @author
+      sl.should eq [ns, @scroll_panel]
+    end
+    it '他人のストーリーはコマコミックともに公開でも含まない' 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 '自分のストーリーはコマコミックともに非公開でも含んでいる' 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]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id, :t => 1, :updated_at => Time.now + 100
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id, :t => 2, :updated_at => Time.now + 200
+        @scroll_panel4 = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id, :t => 3, :updated_at => Time.now + 300
+        @scroll_panel5 = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id, :t => 4, :updated_at => Time.now + 400
+        ScrollPanel.stub(:default_page_size).and_return(2)
+      end
+      it '通常は2件を返す' do
+        l = ScrollPanel.mylist @author, 1, 2
+        l.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        l = ScrollPanel.mylist @author, 1, 2
+        l.should eq [@scroll_panel5, @scroll_panel4]
+      end
+      it 'page=2なら中間2件を返す' do
+        l = ScrollPanel.mylist @author, 2, 2
+        l.should eq [@scroll_panel3, @scroll_panel2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        l = ScrollPanel.mylist @author, 3, 2
+        l.should eq [@scroll_panel]
+      end
+    end
+  end
+  
+  describe '他作家のストーリー一覧取得に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id, :visible => 1
+      @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
+      @other_scroll = FactoryGirl.create :scroll, :author_id => @other_author.id, :visible => 1
+      @other_panel = FactoryGirl.create :panel, :author_id => @other_author.id, :publish => 1
+      @other_scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @other_author.id, :scroll_id => @other_scroll.id, :panel_id => @other_panel.id
+    end
+    it 'リストを返す' do
+      r = ScrollPanel.himlist @other_author
+      r.should eq [@other_scroll_panel]
+    end
+    it '時系列で並んでいる' do
+      ns = FactoryGirl.create :scroll_panel, :author_id => @other_author.id, :scroll_id => @other_scroll.id, :panel_id => @other_panel.id, :t => 1, :updated_at => Time.now + 100
+      r = ScrollPanel.himlist @other_author
+      r.should eq [ns, @other_scroll_panel]
+    end
+    it '公開コミックのストーリー' 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
+      r.should eq [@other_scroll_panel]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @other_scroll_panel2 = FactoryGirl.create :scroll_panel, :author_id => @other_author.id, :scroll_id => @other_scroll.id, :panel_id => @other_panel.id, :t => 1, :updated_at => Time.now + 100
+        @other_scroll_panel3 = FactoryGirl.create :scroll_panel, :author_id => @other_author.id, :scroll_id => @other_scroll.id, :panel_id => @other_panel.id, :t => 2, :updated_at => Time.now + 200
+        @other_scroll_panel4 = FactoryGirl.create :scroll_panel, :author_id => @other_author.id, :scroll_id => @other_scroll.id, :panel_id => @other_panel.id, :t => 3, :updated_at => Time.now + 300
+        @other_scroll_panel5 = FactoryGirl.create :scroll_panel, :author_id => @other_author.id, :scroll_id => @other_scroll.id, :panel_id => @other_panel.id, :t => 4, :updated_at => Time.now + 400
+        ScrollPanel.stub(:default_page_size).and_return(2)
+      end
+      it '通常は2件を返す' do
+        l = ScrollPanel.himlist @other_author, 1, 2
+        l.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        l = ScrollPanel.himlist @other_author, 1, 2
+        l.should eq [@other_scroll_panel5, @other_scroll_panel4]
+      end
+      it 'page=2なら中間2件を返す' do
+        l = ScrollPanel.himlist @other_author, 2, 2
+        l.should eq [@other_scroll_panel3, @other_scroll_panel2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        l = ScrollPanel.himlist @other_author, 3, 2
+        l.should eq [@other_scroll_panel]
+      end
+    end
+  end
+  
+  describe 'ストーリー一覧ページ制御に於いて' do
+    before do
+      ScrollPanel.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = ScrollPanel.list_paginate 
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it 'ストーリー一覧の取得条件を利用している' do
+      ScrollPanel.stub(:list_where).with(any_args).and_return('')
+      ScrollPanel.should_receive(:list_where).with(any_args).exactly(1)
+      r = ScrollPanel.list_paginate 
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = ScrollPanel.list_paginate 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe '自分のストーリー一覧ページ制御に於いて' do
+    before do
+      ScrollPanel.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = ScrollPanel.mylist_paginate @author
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it '自分のストーリー一覧の取得条件を利用している' 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
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = ScrollPanel.mylist_paginate @author, 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe '他作家のストーリー一覧ページ制御に於いて' do
+    before do
+      ScrollPanel.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = ScrollPanel.himlist_paginate @other_author
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it '他作家のストーリー一覧の取得条件を利用している' 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
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = ScrollPanel.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 = ScrollPanel.list_opt
+      r.should have(3).items
+    end
+    it 'コミックを含んでいる' do
+      r = ScrollPanel.list_opt
+      r.has_key?(:scroll).should be_true
+    end
+      it 'コミックは作家を含んでいる' do
+        r = ScrollPanel.list_opt
+        r[:scroll].has_key?(:author).should be_true
+      end
+    it '作家を含んでいる' do
+      r = ScrollPanel.list_opt
+      r.has_key?(:author).should be_true
+    end
+    it 'コマを含んでいる' do
+      r = ScrollPanel.list_opt
+      r.has_key?(:panel).should be_true
+    end
+      it 'コマは作家を含んでいる' do
+        r = ScrollPanel.list_opt
+        r[:panel].has_key?(:author).should be_true
+      end
+      it 'コマはコマ絵を含んでいる' do
+        r = ScrollPanel.list_opt
+        r[:panel].has_key?(:panel_pictures).should be_true
+      end
+        it 'コマ絵は実素材を含んでいる' do
+          r = ScrollPanel.list_opt
+          r[:panel][:panel_pictures].has_key?(:picture).should be_true
+        end
+          it '実素材は絵師を含んでいる' do
+            r = ScrollPanel.list_opt
+            r[:panel][:panel_pictures][:picture].has_key?(:artist).should be_true
+          end
+          it '実素材はライセンスを含んでいる' do
+            r = ScrollPanel.list_opt
+            r[:panel][:panel_pictures][:picture].has_key?(:license).should be_true
+          end
+      it 'コマはフキダシを含んでいる' do
+        r = ScrollPanel.list_opt
+        r[:panel].has_key?(:speech_balloons).should be_true
+      end
+        it 'フキダシはフキダシ枠を含んでいる' do
+          r = ScrollPanel.list_opt
+          r[:panel][:speech_balloons].has_key?(:balloon).should be_true
+        end
+        it 'フキダシはセリフを含んでいる' do
+          r = ScrollPanel.list_opt
+          r[:panel][:speech_balloons].has_key?(:speech).should be_true
+        end
+  end
+  describe 'json一覧出力オプションに於いて' do
+  end
+  
+  describe '単体取得に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id
+    end
+    context 'つつがなく終わるとき' do
+      it '単体取得オプションを利用している' do
+        ScrollPanel.stub(:show_opt).with(any_args).and_return({})
+        ScrollPanel.should_receive(:show_opt).with(any_args).exactly(1)
+        r = ScrollPanel.show @scroll_panel.id, @author
+      end
+      it '閲覧許可を問い合わせている' do
+        ScrollPanel.any_instance.stub(:visible?).with(@author).and_return(true)
+        ScrollPanel.any_instance.should_receive(:visible?).with(@author).exactly(1)
+        r = ScrollPanel.show @scroll_panel.id, @author
+      end
+    end
+    it '指定のストーリーを返す' do
+      l = ScrollPanel.show @scroll_panel.id, @author
+      l.should eq @scroll_panel
+    end
+    context '他人のストーリーを開こうとしたとき' do
+      it '403Forbidden例外を返す' do
+        ScrollPanel.any_instance.stub(:visible?).with(@other_author).and_return(false)
+        lambda{
+          ScrollPanel.show @scroll_panel.id, @other_author
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+    context '存在しないストーリーを開こうとしたとき' do
+      it '404RecordNotFound例外を返す' do
+        lambda{
+          ScrollPanel.show 110, @author
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+    end
+  end
+  
+  describe '編集取得に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id
+    end
+    context 'つつがなく終わるとき' do
+      it '単体取得オプションを利用している' do
+        ScrollPanel.stub(:show_opt).with(any_args).and_return({})
+        ScrollPanel.should_receive(:show_opt).with(any_args).exactly(1)
+        r = ScrollPanel.edit @scroll_panel.id, @author
+      end
+      it '所持判定を問い合わせている' do
+        ScrollPanel.any_instance.stub(:own?).with(any_args).and_return(true)
+        ScrollPanel.any_instance.should_receive(:own?).with(any_args).exactly(1)
+        r = ScrollPanel.edit @scroll_panel.id, @author
+      end
+    end
+    it '指定のストーリーを返す' do
+      l = ScrollPanel.edit @scroll_panel.id, @author
+      l.should eq @scroll_panel
+    end
+    context '他人のストーリーを開こうとしたとき' do
+      it '403Forbidden例外を返す' do
+        ScrollPanel.any_instance.stub(:own?).and_return(false)
+        lambda{
+          ScrollPanel.edit @scroll_panel.id, @author
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+    context '存在しないストーリーを開こうとしたとき' do
+      it '404RecordNotFound例外を返す' do
+        lambda{
+          ScrollPanel.edit 110, @author
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+    end
+  end
+  
+  describe '単体取得オプションに於いて' do
+    it 'includeキーを含んでいる' do
+      r = ScrollPanel.show_opt
+      r.has_key?(:include).should be_true
+    end
+    it '3つの項目を含んでいる' do
+      r = ScrollPanel.show_opt[:include]
+      r.should have(3).items
+    end
+    it 'コミックを含んでいる' do
+      r = ScrollPanel.show_opt[:include]
+      r.has_key?(:scroll).should be_true
+    end
+      it 'コミックは作家を含んでいる' do
+        r = ScrollPanel.show_opt[:include]
+        r[:scroll].has_key?(:author).should be_true
+      end
+    it '作家を含んでいる' do
+      r = ScrollPanel.show_opt[:include]
+      r.has_key?(:author).should be_true
+    end
+    it 'コマを含んでいる' do
+      r = ScrollPanel.show_opt[:include]
+      r.has_key?(:panel).should be_true
+    end
+      it 'コマは作家を含んでいる' do
+        r = ScrollPanel.show_opt[:include]
+        r[:panel].has_key?(:author).should be_true
+      end
+      it 'コマはコマ絵を含んでいる' do
+        r = ScrollPanel.show_opt[:include]
+        r[:panel].has_key?(:panel_pictures).should be_true
+      end
+        it 'コマ絵は実素材を含んでいる' do
+          r = ScrollPanel.show_opt[:include]
+          r[:panel][:panel_pictures].has_key?(:picture).should be_true
+        end
+          it '実素材は絵師を含んでいる' do
+            r = ScrollPanel.show_opt[:include]
+            r[:panel][:panel_pictures][:picture].has_key?(:artist).should be_true
+          end
+          it '実素材はライセンスを含んでいる' do
+            r = ScrollPanel.show_opt[:include]
+            r[:panel][:panel_pictures][:picture].has_key?(:license).should be_true
+          end
+      it 'コマはフキダシを含んでいる' do
+        r = ScrollPanel.show_opt[:include]
+        r[:panel].has_key?(:speech_balloons).should be_true
+      end
+        it 'フキダシはフキダシ枠を含んでいる' do
+          r = ScrollPanel.show_opt[:include]
+          r[:panel][:speech_balloons].has_key?(:balloon).should be_true
+        end
+        it 'フキダシはセリフを含んでいる' do
+          r = ScrollPanel.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
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.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 = @scroll_panel.scroll_panel_as_json @author
+      end
+    end
+    it 'json textを返している' do
+      r = JSON.parse @scroll_panel.scroll_panel_as_json(@author)
+      r.is_a?(Hash).should be_true
+    end
+    it 'scroll,author,panel,コマ要素を含んでいる' do
+      r = JSON.parse @scroll_panel.scroll_panel_as_json(@author)
+      r.has_key?('scroll').should be_true
+      r['scroll'].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 @scroll_panel.scroll_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 @scroll_panel.scroll_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
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id
+      ScrollPanel.any_instance.stub(:scroll_panel_as_json).with(@author).and_return('{"s": 5}')
+    end
+    context 'つつがなく終わるとき' do
+      it 'ストーリーの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
+    end
+    it 'json textを返している' do
+      r = ScrollPanel.list_as_json_text [@scroll_panel], @author
+      j = JSON.parse r
+      j.is_a?(Array).should be_true
+    end
+    it 'ストーリーを含んでいる' do
+      r = ScrollPanel.list_as_json_text [@scroll_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
+      
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id
+      @scroll_panel2 = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel2.id
+    end
+    context '事前チェック' do
+    end
+    context 'つつがなく終わるとき' do
+    end
+    it '連想配列(値は実素材、キーは実素材id)を返している' do
+      r = ScrollPanel.licensed_pictures [@scroll_panel, @scroll_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 = ScrollPanel.licensed_pictures [@scroll_panel, @scroll_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
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+    end
+    
+    context 'コミック初のコマなら' 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
+        r = ScrollPanel.new_t @scroll_panel.scroll_id
+        r.should eq 0
+      end
+    end
+    context 'コミックに一個コマがあるとき' 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
+        @scroll_panel.t = nil
+        r = ScrollPanel.new_t @scroll_panel.scroll_id
+        r.should eq 1
+      end
+    end
+    context 'コミックに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
+        @scroll_panel = FactoryGirl.build :scroll_panel, :author_id => @author.id, :scroll_id => @scroll.id, :panel_id => @panel.id
+        @scroll_panel.t = nil
+        r = ScrollPanel.new_t @scroll_panel.scroll_id
+        r.should eq 2
+      end
+    end
+  end
+  describe 'シリアライズチェックに於いて' do
+    context 'つつがなく終わるとき' do
+      it '0からシリアライズされているならTrueを返す' do
+        r = ScrollPanel.serial? [0, 1, 2]
+        r.should be_true
+      end
+      it '見た目はシリアライズされてなくてもソート結果が無事ならtrueを返す' do
+        r = ScrollPanel.serial? [0, 2, 1]
+        r.should be_true
+      end
+      it '見た目はシリアライズされてなくてもソート結果が無事ならtrueを返す' do
+        r = ScrollPanel.serial? [ 2, 1, 4, 3, 0]
+        r.should be_true
+      end
+    end
+    context '異常なとき' do
+      it '0から始まらないならFalseを返す' do
+        r = ScrollPanel.serial? [1, 2, 3]
+        r.should be_false
+      end
+      it '連続していないならFalseを返す' do
+        r = ScrollPanel.serial? [0, 1, 2, 4]
+        r.should be_false
+      end
+      it '連続していないならFalseを返す' do
+        r = ScrollPanel.serial? [0, 1, 2, 4, 5]
+        r.should be_false
+      end
+    end
+  end
+  describe 't収集に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+      @scroll2 = FactoryGirl.create :scroll, :author_id => @author.id
+      @c2scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll2.id, :panel_id => @panel.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it 'ストーリーから同一コミックのtだけを収集している' do
+        r = ScrollPanel.collect_t @scroll_panel
+        r.should eq [0]
+      end
+    end
+    context '複数コマのとき' do
+      it 'ストーリーから同一コミックの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 '複数コマでヨソのコミックも混じっているとき' do
+      it 'ストーリーから同一コミックの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
+  end
+  describe 'tチェックに於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @scroll_panel = FactoryGirl.build :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it 't収集を依頼している' do
+        ScrollPanel.should_receive(:collect_t).with(any_args).exactly(1)
+        ScrollPanel.stub(:collect_t).with(any_args).and_return([])
+        ScrollPanel.stub(:serial?).with(any_args).and_return(true)
+        r = ScrollPanel.validate_t @scroll_panel
+      end
+      it '収集したtをシリアライズチェック依頼している' do
+        ScrollPanel.stub(:collect_t).with(any_args).and_return([])
+        ScrollPanel.should_receive(:serial?).with(any_args).exactly(1)
+        ScrollPanel.stub(:serial?).with(any_args).and_return(true)
+        r = ScrollPanel.validate_t @scroll_panel
+      end
+    end
+    #実データでチェック
+    #依頼チェックだけでは不安なので最低限のチェックを
+    context '新規のとき' do
+      it '一件だけで正常通過している' do
+        @scroll_panel = FactoryGirl.build :scroll_panel, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id, :t => 0
+        r = ScrollPanel.validate_t @scroll_panel
+        r.should be_true 
+      end
+    end
+    context '既存のとき' do
+      it '2件目を作っても正常通過している' do
+        @scroll_panel = FactoryGirl.create :scroll_panel, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id, :t => 0
+        @scroll_panel2 = FactoryGirl.build :scroll_panel, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id, :t => 1
+        r = ScrollPanel.validate_t @scroll_panel2
+        r.should be_true 
+      end
+    end
+  end
+  describe '挿入シフトに於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+    end
+    context '依頼チェック' do
+      #テーブルが空で0に挿入
+      it 'Updateを依頼している' do
+        ScrollPanel.stub(:update_all).with(any_args)
+        ScrollPanel.should_receive(:update_all).with(any_args).exactly(1)
+        @scroll_panel = FactoryGirl.build :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel.insert_shift
+      end
+    end
+    context 'テーブルに1件(t:0)で0に挿入したとき' do
+      before do
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.build :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '既存の行を1にシフトしている' do
+        @scroll_panel2.insert_shift
+        l = ScrollPanel.find :all
+        l.first.t.should eq 1
+      end
+      it 'これから挿入するt(0)が欠番になっている' do
+        @scroll_panel2.insert_shift
+        l = ScrollPanel.find(:all).map {|s| s.t }
+        l.include?(0).should_not be_true
+      end
+    end
+    context 'テーブルに2件(t:0,1)で1に挿入したとき' do
+      before do
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel3 = FactoryGirl.build :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '既存のt1を2にシフトしてこれから挿入するt(1)が欠番になっている' do
+        @scroll_panel3.insert_shift
+        l = ScrollPanel.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
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel4 = FactoryGirl.create :scroll_panel, :t => 3, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel5 = FactoryGirl.create :scroll_panel, :t => 4, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel6 = FactoryGirl.build :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '既存のt1を2にシフトしてこれから挿入するt(1)が欠番になっている' do
+        @scroll_panel6.insert_shift
+        l = ScrollPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 1, 3, 4, 5]
+      end
+    end
+    context '先ほどのケース+他のコミック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
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel4 = FactoryGirl.create :scroll_panel, :t => 3, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel5 = FactoryGirl.create :scroll_panel, :t => 4, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel6 = FactoryGirl.build :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '既存のt1を2にシフトしてこれから挿入するt(1)が欠番になっている' do
+        @scroll_panel6.insert_shift
+        l = ScrollPanel.find(:all, :conditions => ['scroll_id = ?', @scroll.id]).map {|s| s.t }
+        l.sort.should eq [0, 1, 3, 4, 5]
+      end
+      it '他のコミックに影響がない' do
+        ot = @scroll_panelc2.t
+        @scroll_panel6.insert_shift
+        @scroll_panelc2.reload
+        @scroll_panelc2.t.should eq ot
+      end
+    end
+  end
+  describe '少ない方に移動に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+    end
+    context '依頼チェック' do
+      it 'Updateを依頼している' do
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        ScrollPanel.stub(:update_all).with(any_args)
+        ScrollPanel.should_receive(:update_all).with(any_args).exactly(1)
+        ot = @scroll_panel2.t
+        @scroll_panel2.t = 0
+        @scroll_panel2.lesser_shift ot
+      end
+    end
+    context 'テーブルに2件(t:0,1)で1を0に移動したとき' do
+      before do
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @scroll_panel2.t
+        @scroll_panel2.t = 0
+      end
+      it '既存のt0を1にシフトしてこれから挿入するt(0)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @scroll_panel2.lesser_shift @ot
+        l = ScrollPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [1, 1]
+      end
+      it '既存のt0を1にシフトしている' do
+        @scroll_panel2.lesser_shift @ot
+        @scroll_panel.reload
+        @scroll_panel.t.should eq 1
+      end
+    end
+    context 'テーブルに3件(t:0,1,2)で2を1に移動したとき' do
+      before do
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @scroll_panel3.t
+        @scroll_panel3.t = 1
+      end
+      it '既存のt1を2にシフトしてこれから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @scroll_panel3.lesser_shift @ot
+        l = ScrollPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 2, 2]
+      end
+      it '既存のt1を2にシフトしている' do
+        @scroll_panel3.lesser_shift @ot
+        @scroll_panel2.reload
+        @scroll_panel2.t.should eq 2
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)で3を1に移動したとき' do
+      before do
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel4 = FactoryGirl.create :scroll_panel, :t => 3, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel5 = FactoryGirl.create :scroll_panel, :t => 4, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @scroll_panel4.t
+        @scroll_panel4.t = 1
+      end
+      it 'これから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @scroll_panel4.lesser_shift @ot
+        l = ScrollPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 2, 3, 3, 4]
+      end
+      it '既存のt0には変化がない' do
+        @scroll_panel4.lesser_shift @ot
+        @scroll_panel.reload
+        @scroll_panel.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @scroll_panel4.lesser_shift @ot
+        @scroll_panel5.reload
+        @scroll_panel5.t.should eq 4
+      end
+      it '既存のt1を2にシフトしている' do
+        @scroll_panel4.lesser_shift @ot
+        @scroll_panel2.reload
+        @scroll_panel2.t.should eq 2
+      end
+      it '既存のt2を3にシフトしている' do
+        @scroll_panel4.lesser_shift @ot
+        @scroll_panel3.reload
+        @scroll_panel3.t.should eq 3
+      end
+    end
+    context '先ほどのケース+他のコミック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
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel4 = FactoryGirl.create :scroll_panel, :t => 3, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel5 = FactoryGirl.create :scroll_panel, :t => 4, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @scroll_panel4.t
+        @scroll_panel4.t = 1
+      end
+      it 'これから挿入するt(1)が欠番になっている' do
+        @scroll_panel4.lesser_shift @ot
+        l = ScrollPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0, 2, 3, 3, 4]
+      end
+      it '既存のt0には変化がない' do
+        @scroll_panel4.lesser_shift @ot
+        @scroll_panel.reload
+        @scroll_panel.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @scroll_panel4.lesser_shift @ot
+        @scroll_panel5.reload
+        @scroll_panel5.t.should eq 4
+      end
+      it '既存のt1を2にシフトしている' do
+        @scroll_panel4.lesser_shift @ot
+        @scroll_panel2.reload
+        @scroll_panel2.t.should eq 2
+      end
+      it '既存のt2を3にシフトしている' do
+        @scroll_panel4.lesser_shift @ot
+        @scroll_panel3.reload
+        @scroll_panel3.t.should eq 3
+      end
+      it '他のコミックに影響がない' do
+        @scroll_panel4.lesser_shift @ot
+        @scroll_panelc2.reload
+        @scroll_panelc2.t.should eq 0
+      end
+    end
+    #例外ケース。
+    #負のときは0として正常扱い
+    context 'テーブルに2件(t:0,1)で1を-1に移動したとき' do
+      before do
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @scroll_panel2.t
+        @scroll_panel2.t = -1
+      end
+      it '既存のt0を1にシフトしてこれから挿入するt(0)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @scroll_panel2.lesser_shift @ot
+        l = ScrollPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [1, 1]
+      end
+      it '既存のt0を1にシフトしている' do
+        @scroll_panel2.lesser_shift @ot
+        @scroll_panel.reload
+        @scroll_panel.t.should eq 1
+      end
+      it '既存のt1は0に補正されている' do
+        @scroll_panel2.lesser_shift @ot
+        @scroll_panel2.t.should eq 0
+      end
+    end
+  end
+  describe '大きい方に移動に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+    end
+    context '依頼チェック' do
+      it 'Updateを依頼している' do
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        ScrollPanel.stub(:update_all).with(any_args)
+        ScrollPanel.should_receive(:update_all).with(any_args).exactly(1)
+        ot = @scroll_panel.t
+        @scroll_panel.t = 1
+        @scroll_panel.higher_shift ot
+      end
+    end
+    context 'テーブルに2件(t:0,1)で0を1に移動したとき' do
+      before do
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @scroll_panel.t
+        @scroll_panel.t = 1
+      end
+      it '既存のt1を0にシフトしてこれから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @scroll_panel.higher_shift @ot
+        l = ScrollPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0]
+      end
+      it '既存のt1を0にシフトしている' do
+        @scroll_panel.higher_shift @ot
+        @scroll_panel2.reload
+        @scroll_panel2.t.should eq 0
+      end
+    end
+    context 'テーブルに3件(t:0,1,2)で0を1に移動したとき' do
+      before do
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @scroll_panel.t
+        @scroll_panel.t = 1
+      end
+      it '既存のt1を0にシフトしてこれから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @scroll_panel.higher_shift @ot
+        l = ScrollPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0, 2]
+      end
+      it '既存のt1を0にシフトしている' do
+        @scroll_panel.higher_shift @ot
+        @scroll_panel2.reload
+        @scroll_panel2.t.should eq 0
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)で1を3に移動したとき' do
+      before do
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel4 = FactoryGirl.create :scroll_panel, :t => 3, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel5 = FactoryGirl.create :scroll_panel, :t => 4, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @scroll_panel2.t
+        @scroll_panel2.t = 3
+      end
+      it 'これから挿入するt(3)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @scroll_panel2.higher_shift @ot
+        l = ScrollPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 1, 1, 2, 4]
+      end
+      it '既存のt0には変化がない' do
+        @scroll_panel2.higher_shift @ot
+        @scroll_panel.reload
+        @scroll_panel.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @scroll_panel2.higher_shift @ot
+        @scroll_panel5.reload
+        @scroll_panel5.t.should eq 4
+      end
+      it '既存のt2を1にシフトしている' do
+        @scroll_panel2.higher_shift @ot
+        @scroll_panel3.reload
+        @scroll_panel3.t.should eq 1
+      end
+      it '既存のt3を2にシフトしている' do
+        @scroll_panel2.higher_shift @ot
+        @scroll_panel4.reload
+        @scroll_panel4.t.should eq 2
+      end
+    end
+    context '先ほどのケース+他のコミック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
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel4 = FactoryGirl.create :scroll_panel, :t => 3, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel5 = FactoryGirl.create :scroll_panel, :t => 4, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @scroll_panel2.t
+        @scroll_panel2.t = 3
+      end
+      it 'これから挿入するt(3)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @scroll_panel2.higher_shift @ot
+        l = ScrollPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0, 1, 1, 2, 4]
+      end
+      it '既存のt0には変化がない' do
+        @scroll_panel2.higher_shift @ot
+        @scroll_panel.reload
+        @scroll_panel.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @scroll_panel2.higher_shift @ot
+        @scroll_panel5.reload
+        @scroll_panel5.t.should eq 4
+      end
+      it '既存のt2を1にシフトしている' do
+        @scroll_panel2.higher_shift @ot
+        @scroll_panel3.reload
+        @scroll_panel3.t.should eq 1
+      end
+      it '既存のt3を2にシフトしている' do
+        @scroll_panel2.higher_shift @ot
+        @scroll_panel4.reload
+        @scroll_panel4.t.should eq 2
+      end
+      it '他のコミックに影響がない' do
+        @scroll_panel2.higher_shift @ot
+        @scroll_panelc2.reload
+        @scroll_panelc2.t.should eq 0
+      end
+    end
+    #例外ケース。
+    #max超えたときはmaxとして正常扱い
+    context 'テーブルに2件(t:0,1)で0を2に移動したとき' do
+      before do
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @scroll_panel.t
+        @scroll_panel.t = 2
+      end
+      it '既存のt1を0にシフトしてこれから挿入するt(1)が欠番になっている' do
+        #移動させたい行はそのまま残る
+        @scroll_panel.higher_shift @ot
+        l = ScrollPanel.find(:all).map {|s| s.t }
+        l.sort.should eq [0, 0]
+      end
+      it '既存のt1を0にシフトしている' do
+        @scroll_panel.higher_shift @ot
+        @scroll_panel2.reload
+        @scroll_panel2.t.should eq 0
+      end
+      it '既存のt0は1に補正されている' do
+        @scroll_panel.higher_shift @ot
+        @scroll_panel.t.should eq 1
+      end
+    end
+  end
+  describe '入れ替えに於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+      @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+    end
+    context '新tが旧tより小さいとき' do
+      it '少ない方に移動を依頼している' do
+        ScrollPanel.any_instance.stub(:lesser_shift).with(any_args)
+        ScrollPanel.any_instance.should_receive(:lesser_shift).with(any_args).exactly(1)
+        ot = @scroll_panel2.t
+        @scroll_panel2.t = 0
+        @scroll_panel2.update_shift ot
+      end
+    end
+    context '新tが旧tより大きいとき' do
+      it '大きい方に移動を依頼している' do
+        ScrollPanel.any_instance.stub(:higher_shift).with(any_args)
+        ScrollPanel.any_instance.should_receive(:higher_shift).with(any_args).exactly(1)
+        ot = @scroll_panel.t
+        @scroll_panel.t = 1
+        @scroll_panel.update_shift ot
+      end
+    end
+  end
+  describe '順序入れ替えに於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+    end
+    context 'オブジェクトが新規でtが空のとき' do
+      it '末尾追加としてtを補充依頼している' do
+        @scroll_panel = FactoryGirl.build :scroll_panel, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        ScrollPanel.stub(:new_t).with(any_args).and_return(0)
+        ScrollPanel.should_receive(:new_t).with(any_args).exactly(1)
+        @scroll_panel.t = nil
+        r = @scroll_panel.rotate
+      end
+    end
+    context 'オブジェクトが新規でtが設定されているとき' do
+      it '挿入追加として挿入シフトを依頼している' do
+        @scroll_panel = FactoryGirl.build :scroll_panel, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        ScrollPanel.any_instance.stub(:insert_shift).with(any_args)
+        ScrollPanel.any_instance.should_receive(:insert_shift).with(any_args).exactly(1)
+        @scroll_panel.t = 0
+        r = @scroll_panel.rotate
+      end
+    end
+    context 'オブジェクトが新規でなくtが設定されているとき' do
+      it 'コマ移動として入れ替えを依頼している' do
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        ScrollPanel.any_instance.stub(:update_shift).with(any_args)
+        ScrollPanel.any_instance.should_receive(:update_shift).with(1).exactly(1)
+        @scroll_panel2.t = 0
+        r = @scroll_panel.rotate 1
+      end
+    end
+    context 'オブジェクトが新規でなくtが空のとき' do
+      it '入れ替えもシフトもせず、tを空のままにしている' do
+        #結果、tに欠番が生じてシリアライズチェックでひっかかる
+      end
+    end
+  end
+  describe '編集許可に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @scroll_panel = FactoryGirl.build :scroll_panel, :t => nil, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it 'trueを返す' do
+        r = @scroll_panel.allow?
+        r.should be_true
+      end
+    end
+    context 'コミックで引っかかるとき' 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)
+        r = @scroll_panel.allow?
+        r.should be_false
+      end
+    end
+    context 'コマで引っかかるとき' do
+      it 'falseを返す' do
+        Scroll.any_instance.stub(:own?).with(any_args).and_return(true)
+        Panel.any_instance.stub(:usable?).with(any_args).and_return(false)
+        r = @scroll_panel.allow?
+        r.should be_false
+      end
+    end
+    context 'コミックまたはコマが指定されていなかったとき' do
+      it 'nilを返す' do
+        Scroll.any_instance.stub(:own?).with(any_args).and_return(true)
+        @scroll_panel.panel_id = nil
+        r = @scroll_panel.allow?
+        r.should eq nil
+      end
+      it 'nilを返す' do
+        Panel.any_instance.stub(:usable?).with(any_args).and_return(true)
+        @scroll_panel.scroll_id = nil
+        r = @scroll_panel.allow?
+        r.should eq nil
+      end
+    end
+  end
+  describe '保存に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @scroll_panel = FactoryGirl.build :scroll_panel, :t => nil, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '編集許可チェックを依頼している' do
+        ScrollPanel.any_instance.stub(:allow?).with(any_args).and_return(true)
+        ScrollPanel.any_instance.should_receive(:allow?).with(any_args).exactly(1)
+        r = @scroll_panel.store
+      end
+      it '順序入れ替えを依頼している' do
+        ScrollPanel.any_instance.stub(:rotate).with(any_args).and_return(0)
+        ScrollPanel.any_instance.should_receive(:rotate).with(any_args).exactly(1)
+        ScrollPanel.any_instance.stub(:save).with(any_args).and_return(true)
+        ScrollPanel.stub(:validate_t).with(any_args).and_return(true)
+        r = @scroll_panel.store 
+      end
+      it '保存を依頼している' do
+        ScrollPanel.stub(:new_t).with(any_args).and_return(0)
+        ScrollPanel.any_instance.stub(:save).with(any_args).and_return(true)
+        ScrollPanel.any_instance.should_receive(:save).with(any_args).exactly(1)
+        ScrollPanel.stub(:validate_t).with(any_args).and_return(true)
+        r = @scroll_panel.store
+      end
+      it 'tのシリアライズチェックを依頼している' do
+        ScrollPanel.stub(:new_t).with(any_args).and_return(0)
+        ScrollPanel.any_instance.stub(:save).with(any_args).and_return(true)
+        ScrollPanel.stub(:validate_t).with(any_args).and_return(true)
+        ScrollPanel.should_receive(:validate_t).with(any_args).exactly(1)
+        r = @scroll_panel.store
+      end
+    end
+    #入れ替えテストと同じテストを実施。こちらはシフトだけでなく本尊も更新されている
+    context 'テーブルに5件(t:0,1,2,3,4)+他のコミック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
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel4 = FactoryGirl.create :scroll_panel, :t => 3, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel5 = FactoryGirl.create :scroll_panel, :t => 4, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel6 = FactoryGirl.build :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '既存のt0には変化がない' do
+        @scroll_panel6.store
+        @scroll_panel.reload
+        @scroll_panel.t.should eq 0
+      end
+      it '既存のt1には変化がない' do
+        @scroll_panel6.store
+        @scroll_panel2.reload
+        @scroll_panel2.t.should eq 1
+      end
+      it '既存のt2を3にシフトしている' do
+        @scroll_panel6.store
+        @scroll_panel3.reload
+        @scroll_panel3.t.should eq 3
+      end
+      it '既存のt3を4にシフトしている' do
+        @scroll_panel6.store
+        @scroll_panel4.reload
+        @scroll_panel4.t.should eq 4
+      end
+      it '既存のt5を5にシフトしている' do
+        @scroll_panel6.store
+        @scroll_panel5.reload
+        @scroll_panel5.t.should eq 5
+      end
+      it '新規のt2が作成されている' do
+        @scroll_panel6.store
+        @scroll_panel6.reload
+        @scroll_panel6.t.should eq 2
+      end
+      it '他のコミックに影響がない' do
+        @ot = @scroll_panelc2.t
+        @scroll_panel6.store
+        @scroll_panelc2.reload
+        @scroll_panelc2.t.should eq @ot
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)+他のコミック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
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel4 = FactoryGirl.create :scroll_panel, :t => 3, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel5 = FactoryGirl.create :scroll_panel, :t => 4, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @scroll_panel4.t
+        @scroll_panel4.t = 1
+      end
+      it '既存のt0には変化がない' do
+        @scroll_panel4.store @ot
+        @scroll_panel.reload
+        @scroll_panel.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @scroll_panel4.store @ot
+        @scroll_panel5.reload
+        @scroll_panel5.t.should eq 4
+      end
+      it '既存のt1を2にシフトしている' do
+        @scroll_panel4.store @ot
+        @scroll_panel2.reload
+        @scroll_panel2.t.should eq 2
+      end
+      it '既存のt2を3にシフトしている' do
+        @scroll_panel4.store @ot
+        @scroll_panel3.reload
+        @scroll_panel3.t.should eq 3
+      end
+      it '既存のt3を1にシフトしている' do
+        @scroll_panel4.store @ot
+        @scroll_panel4.reload
+        @scroll_panel4.t.should eq 1
+      end
+      it '他のコミックに影響がない' do
+        @scroll_panel4.store @ot
+        @scroll_panelc2.reload
+        @scroll_panelc2.t.should eq 0
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)+他のコミック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
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel4 = FactoryGirl.create :scroll_panel, :t => 3, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel5 = FactoryGirl.create :scroll_panel, :t => 4, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @scroll_panel2.t
+        @scroll_panel2.t = 3
+      end
+      it '既存のt0には変化がない' do
+        @scroll_panel2.store @ot
+        @scroll_panel.reload
+        @scroll_panel.t.should eq 0
+      end
+      it '既存のt4には変化がない' do
+        @scroll_panel2.store @ot
+        @scroll_panel5.reload
+        @scroll_panel5.t.should eq 4
+      end
+      it '既存のt1を3にシフトしている' do
+        @scroll_panel2.store @ot
+        @scroll_panel2.reload
+        @scroll_panel2.t.should eq 3
+      end
+      it '既存のt2を1にシフトしている' do
+        @scroll_panel2.store @ot
+        @scroll_panel3.reload
+        @scroll_panel3.t.should eq 1
+      end
+      it '既存のt3を2にシフトしている' do
+        @scroll_panel2.store @ot
+        @scroll_panel4.reload
+        @scroll_panel4.t.should eq 2
+      end
+      it '他のコミックに影響がない' do
+        @scroll_panel2.store @ot
+        @scroll_panelc2.reload
+        @scroll_panelc2.t.should eq 0
+      end
+    end
+    #ロールバックテスト。入れ替えが直接DBをいじるので、すべてのケースで確実にロールバックを確認する
+    context 'テーブルに5件(t:0,1,2,3,4)+他のコミック1件で2に挿入したが保存に失敗したとき' do
+      before do
+        ScrollPanel.any_instance.stub(:save).with(any_args).and_return(false)
+        @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
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel4 = FactoryGirl.create :scroll_panel, :t => 3, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel5 = FactoryGirl.create :scroll_panel, :t => 4, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel6 = FactoryGirl.build :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '既存のtに変化がない' do
+        @scroll_panel6.store
+        @scroll_panel.reload
+        @scroll_panel.t.should eq 0
+        @scroll_panel2.reload
+        @scroll_panel2.t.should eq 1
+        @scroll_panel3.reload
+        @scroll_panel3.t.should eq 2
+        @scroll_panel4.reload
+        @scroll_panel4.t.should eq 3
+        @scroll_panel5.reload
+        @scroll_panel5.t.should eq 4
+        @scroll_panelc2.reload
+        @scroll_panelc2.t.should eq 0
+      end
+      it 'falseを返す' do
+        r = @scroll_panel6.store
+        r.should be_false
+      end
+    end
+    context 'テーブルに5件(t:0,1,2,3,4)+他のコミック1件で3を1に移動したがシリアルチェックに失敗したとき' do
+      before do
+        ScrollPanel.stub(:validate_t).with(any_args).and_return(false)
+        @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
+        @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel4 = FactoryGirl.create :scroll_panel, :t => 3, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel5 = FactoryGirl.create :scroll_panel, :t => 4, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @ot = @scroll_panel4.t
+        @scroll_panel4.t = 1
+      end
+      it '既存のtに変化がない' do
+        @scroll_panel4.store @ot
+        @scroll_panel.reload
+        @scroll_panel.t.should eq 0
+        @scroll_panel2.reload
+        @scroll_panel2.t.should eq 1
+        @scroll_panel3.reload
+        @scroll_panel3.t.should eq 2
+        @scroll_panel4.reload
+        @scroll_panel4.t.should eq 3
+        @scroll_panel5.reload
+        @scroll_panel5.t.should eq 4
+        @scroll_panelc2.reload
+        @scroll_panelc2.t.should eq 0
+      end
+      it 'falseを返す' do
+        r = @scroll_panel4.store @ot
+        r.should be_false
+      end
+      it 'tにエラーメッセージが入っている' do
+        @scroll_panel4.store @ot
+        @scroll_panel4.errors[:t].should_not be_empty
+        @scroll_panel4.valid?.should be_true
+      end
+    end
+    context '編集不可だったとき' do
+      before do
+        @scroll_panel = FactoryGirl.build :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        ScrollPanel.any_instance.stub(:allow?).and_return(false)
+      end
+      it '403Forbidden例外を返す' do
+        lambda{
+          @scroll_panel.store
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+  end
+  describe '切り詰め処理つき削除に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @panel = FactoryGirl.create :panel, :author_id => @author.id
+      @scroll_panel = FactoryGirl.create :scroll_panel, :t => 0, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '削除される' do
+        lambda{
+          @scroll_panel.destroy_and_shorten
+        }.should change(ScrollPanel, :count ).by(-1)
+      end
+      it 'Trueを返す' do
+        r = @scroll_panel.destroy_and_shorten
+        r.should be_true 
+      end
+    end
+    context '削除に失敗したとき' do
+      before do
+        ScrollPanel.any_instance.stub(:destroy).and_return(false)
+      end
+      it 'ロールバックされる' do
+        lambda{
+          @scroll_panel.destroy_and_shorten
+        }.should_not change(ScrollPanel, :count )
+      end
+      it 'Falseを返す' do
+        r = @scroll_panel.destroy_and_shorten
+        r.should be_false
+      end
+    end
+    #連携テスト。切り詰めが直接DBをいじる
+    context '2件で先頭を削除したとき' do
+      before do
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '行が削除される' do
+        lambda{
+          @scroll_panel.destroy_and_shorten
+        }.should change(ScrollPanel, :count ).by(-1)
+      end
+      it '先頭は削除される' do
+        @scroll_panel.destroy_and_shorten
+        lambda{
+          ScrollPanel.find @scroll_panel.id
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+      it '2件目は前に詰められる' do
+        @scroll_panel.destroy_and_shorten
+        @scroll_panel2.reload
+        @scroll_panel2.t.should eq 0
+      end
+    end
+    context '3件で先頭を削除したとき' do
+      before do
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '行が削除される' do
+        lambda{
+          @scroll_panel.destroy_and_shorten
+        }.should change(ScrollPanel, :count ).by(-1)
+      end
+      it '先頭は削除される' do
+        @scroll_panel.destroy_and_shorten
+        lambda{
+          ScrollPanel.find @scroll_panel.id
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+      it '2件目は前に詰められる' do
+        @scroll_panel.destroy_and_shorten
+        @scroll_panel2.reload
+        @scroll_panel2.t.should eq 0
+      end
+      it '3件目は前に詰められる' do
+        @scroll_panel.destroy_and_shorten
+        @scroll_panel3.reload
+        @scroll_panel3.t.should eq 1
+      end
+    end
+    context '5件で3件目を削除したとき' do
+      before do
+        @scroll_panel2 = FactoryGirl.create :scroll_panel, :t => 1, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel3 = FactoryGirl.create :scroll_panel, :t => 2, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel4 = FactoryGirl.create :scroll_panel, :t => 3, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+        @scroll_panel5 = FactoryGirl.create :scroll_panel, :t => 4, :scroll_id => @scroll.id, :panel_id => @panel.id, :author_id => @author.id
+      end
+      it '行が削除される' do
+        lambda{
+          @scroll_panel3.destroy_and_shorten
+        }.should change(ScrollPanel, :count ).by(-1)
+      end
+      it '1件目は変化がない' do
+        @scroll_panel3.destroy_and_shorten
+        @scroll_panel.reload
+        @scroll_panel.t.should eq 0
+      end
+      it '2件目は変化がない' do
+        @scroll_panel3.destroy_and_shorten
+        @scroll_panel2.reload
+        @scroll_panel2.t.should eq 1
+      end
+      it '3件目は削除される' do
+        @scroll_panel3.destroy_and_shorten
+        lambda{
+          ScrollPanel.find @scroll_panel3.id
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+      it '4件目は前に詰められる' do
+        @scroll_panel3.destroy_and_shorten
+        @scroll_panel4.reload
+        @scroll_panel4.t.should eq 2
+      end
+      it '5件目は前に詰められる' do
+        @scroll_panel3.destroy_and_shorten
+        @scroll_panel5.reload
+        @scroll_panel5.t.should eq 3
+      end
+    end
+    #ロールバックテスト。切り詰めが直接DBをいじるので、すべてのケースで確実にロールバックを確認する
+  end
+end
diff --git a/spec/models/scroll_spec.rb b/spec/models/scroll_spec.rb
new file mode 100644 (file)
index 0000000..3b76b14
--- /dev/null
@@ -0,0 +1,840 @@
+# -*- encoding: utf-8 -*-
+require 'spec_helper'
+#コミック
+
+describe Scroll 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
+      @scroll = FactoryGirl.build :scroll, :author_id => @author.id
+    end
+    
+    context 'オーソドックスなデータのとき' do
+      it '下限データが通る' do
+        @scroll.title = 'a'
+        @scroll.visible = 0
+        @scroll.should be_valid
+      end
+      it '上限データが通る' do
+        @scroll.title = 'a'*100
+        @scroll.visible = 1
+        @scroll.should be_valid
+      end
+    end
+    
+    context 'titleを検証するとき' do
+      it 'nullなら失敗する' do
+        @scroll.title = nil
+        @scroll.should_not be_valid
+      end
+      it '100文字以上なら失敗する' do
+        @scroll.title = 'a'*101
+        @scroll.should_not be_valid
+      end
+    end
+    context 'visibleを検証するとき' do
+      it 'nullなら失敗する' do
+        @scroll.visible = nil
+        @scroll.should_not be_valid
+      end
+      it '負なら失敗する' do
+        @scroll.visible = -1
+        @scroll.should_not be_valid
+      end
+      it '2以上なら失敗する' do
+        @scroll.visible = 2
+        @scroll.should_not be_valid
+      end
+    end
+  end
+  
+  describe '文字コード検証に於いて' do
+    before do
+      @scroll = FactoryGirl.build :scroll, :author_id => @author.id
+    end
+    
+    context 'titleを検証するとき' do
+      it 'Shift JISなら失敗する' do
+        @scroll.title = "\x83G\x83r\x83]\x83D"
+        lambda{
+          @scroll.valid_encode
+        }.should raise_error(Pettanr::BadRequest)
+      end
+    end
+    
+    context 'descriptionを検証するとき' do
+      it 'Shift JISなら失敗する' do
+        @scroll.description = "\x83G\x83r\x83]\x83D"
+        lambda{
+          @scroll.valid_encode
+        }.should raise_error(Pettanr::BadRequest)
+      end
+    end
+  end
+  
+  describe 'デフォルト値補充に於いて' do
+    it 'visibleが0になっている' do
+      @scroll = FactoryGirl.build :scroll, :visible => nil
+      @scroll.supply_default
+      @scroll.visible.should eq 0
+    end
+  end
+  
+  describe '上書き補充に於いて' do
+    it '作家idが設定されている' do
+      @scroll = FactoryGirl.build :scroll, :author_id => nil
+      @scroll.overwrite @author
+      @scroll.author_id.should eq @author.id
+    end
+  end
+  
+  describe 'ロールリストからの作家取得に於いて' do
+    context 'ロールがユーザのとき' do
+      it 'ユーザから作家を取得して、それを返す' do
+        r = Scroll.get_author_from_roles([@user])
+        r.should eq @author
+      end
+    end
+    context 'ロールが作家のとき' do
+      it '作家を返す' do
+        r = Scroll.get_author_from_roles([@author])
+        r.should eq @author
+      end
+    end
+    context 'ロールが絵師のとき' do
+      it '絵師から作家を取得できれば、それをを返す' do
+        r = Scroll.get_author_from_roles([@artist])
+        r.should eq @author
+      end
+      it '絵師から作家を取得できないときnilを返す' do
+        @artist.author_id = nil
+        @artist.save!
+        r = Scroll.get_author_from_roles([@artist])
+        r.should be_nil
+      end
+    end
+    context 'ロールが管理者のとき' do
+      it 'nilを返す' do
+        r = Scroll.get_author_from_roles([@admin])
+        r.should be_nil
+      end
+    end
+  end
+  
+  describe 'ロールリストからの絵師取得に於いて' do
+    context 'ロールがユーザのとき' do
+      it 'ユーザから作家を取得して、そこから絵師を取得できれば、それを返す' do
+        r = Scroll.get_artist_from_roles([@user])
+        r.should eq @artist
+      end
+      it '作家から絵師を取得できないときnilを返す' do
+        @artist.author_id = nil
+        @artist.save!
+        r = Scroll.get_artist_from_roles([@user])
+        r.should be_nil
+      end
+    end
+    context 'ロールが作家のとき' do
+      it 'そこから絵師を取得できれば、それを返す' do
+        r = Scroll.get_artist_from_roles([@author])
+        r.should eq @artist
+      end
+      it '作家から絵師を取得できないときnilを返す' do
+        @artist.author_id = nil
+        @artist.save!
+        r = Scroll.get_artist_from_roles([@author])
+        r.should be_nil
+      end
+    end
+    context 'ロールが絵師のとき' do
+      it 'それを返す' do
+        r = Scroll.get_artist_from_roles([@artist])
+        r.should eq @artist
+      end
+    end
+    context 'ロールが管理者のとき' do
+      it 'nilを返す' do
+        r = Scroll.get_artist_from_roles([@admin])
+        r.should be_nil
+      end
+    end
+  end
+  
+  describe '所持判定に於いて' do
+    before do
+      @scroll = FactoryGirl.build :scroll, :author_id => @author.id
+    end
+    context '事前チェックする' do
+      it '自身にロールリストからの作家取得を依頼している' do
+        Scroll.should_receive(:get_author_from_roles).with(any_args).exactly(1)
+        r = @scroll.own?([@author])
+      end
+    end
+    context 'ロール内作家が取得できるとき' do
+      before do
+      end
+      it 'ロール内作家のidが自身の作家idと一致するなら許可する' do
+        Scroll.stub(:get_author_from_roles).with(any_args).and_return(@author)
+        r = @scroll.own?([@author])
+        r.should be_true
+      end
+      it 'ロール内作家のidが自身の作家idと一致しないならno' do
+        Scroll.stub(:get_author_from_roles).with(any_args).and_return(@other_author)
+        @scroll.own?(@other_author).should be_false
+      end
+    end
+    context 'ロール内作家が取得できないとき' do
+      before do
+        Scroll.stub(:get_author_from_roles).with(any_args).and_return(nil)
+      end
+      it 'Falseを返す' do
+        r = @scroll.own?([@author])
+        r.should be_false
+      end
+    end
+  end
+  
+  describe '読者用ロールチェックに於いて' do
+    before do
+      @scroll = FactoryGirl.build :scroll, :author_id => @author.id
+    end
+    context 'ロールリストに作家が含まれるとき' do
+      it 'ロールリストがリストではないとき、リストにする trueを返す' do
+        r = @scroll.reader_role_check(@author)
+        r.should be_true
+      end
+      it 'trueを返す' do
+        r = @scroll.reader_role_check([@author])
+        r.should be_true
+      end
+    end
+    context 'ロールリストに絵師が含まれるとき' do
+      it 'trueを返す' do
+        r = @scroll.reader_role_check([@artist])
+        r.should be_true
+      end
+    end
+    context 'ロールリストにユーザが含まれるとき' do
+      it 'trueを返す' do
+        r = @scroll.reader_role_check([@user])
+        r.should be_true
+      end
+    end
+    context 'ロールリストに管理者が含まれるとき' do
+      it 'trueを返す' do
+        r = @scroll.reader_role_check([@admin])
+        r.should be_true
+      end
+    end
+    context 'ロールリストにユーザ、管理者、作家、絵師が含まれないとき' do
+      it 'falseを返す' do
+        r = @scroll.reader_role_check([nil])
+        r.should be_false
+      end
+    end
+  end
+  
+  describe '素材読者用ロールチェックに於いて' do
+    before do
+      @scroll = FactoryGirl.build :scroll, :author_id => @author.id
+    end
+    context 'ロールリストに作家が含まれるとき' do
+      it 'ロールリストがリストではないとき、リストにする trueを返す' do
+        r = @scroll.resource_reader_role_check(@author)
+        r.should be_true
+      end
+      it 'trueを返す' do
+        r = @scroll.resource_reader_role_check([@author])
+        r.should be_true
+      end
+    end
+    context 'ロールリストに絵師が含まれるとき' do
+      it 'trueを返す' do
+        r = @scroll.resource_reader_role_check([@artist])
+        r.should be_true
+      end
+    end
+    context 'ロールリストにユーザが含まれるとき' do
+      it 'trueを返す' do
+        r = @scroll.resource_reader_role_check([@user])
+        r.should be_true
+      end
+    end
+    context 'ロールリストに管理者が含まれるとき' do
+      it 'trueを返す' do
+        r = @scroll.resource_reader_role_check([@admin])
+        r.should be_true
+      end
+    end
+    context 'ロールリストに借手が含まれるとき' do
+      it 'trueを返す' do
+        r = @scroll.resource_reader_role_check([@demand_user])
+        r.should be_true
+      end
+    end
+    context 'ロールリストにユーザ、管理者、作家、絵師、借手が含まれないとき' do
+      it 'falseを返す' do
+        r = @scroll.resource_reader_role_check([nil])
+        r.should be_false
+      end
+    end
+  end
+  
+  describe '閲覧許可に於いて' do
+    before do
+      @scroll = FactoryGirl.build :scroll, :author_id => @author.id
+    end
+    context 'オープンモードのとき' do
+      before do
+        MagicNumber['run_mode'] = 0
+      end
+      it '自身にゲスト用ロールチェックを問い合わせしている' do
+        Scroll.any_instance.stub(:guest_role_check).and_return(true)
+        Scroll.any_instance.should_receive(:guest_role_check).with(any_args).exactly(1)
+        r = @scroll.visible?([@author])
+      end
+      it 'ゲスト用ロールチェックが失敗したとき、falseを返す' do
+        Scroll.any_instance.stub(:guest_role_check).and_return(false)
+        r = @scroll.visible?([@author])
+        r.should be_false
+      end
+    end
+    context 'クローズドモードのとき' do
+      before do
+        MagicNumber['run_mode'] = 1
+      end
+      it '自身に読者用ロールチェックを問い合わせしている' do
+        Scroll.any_instance.stub(:reader_role_check).and_return(true)
+        Scroll.any_instance.should_receive(:reader_role_check).with(any_args).exactly(1)
+        r = @scroll.visible?([@author])
+      end
+      it '読者用ロールチェックが失敗したとき、falseを返す' do
+        Scroll.any_instance.stub(:reader_role_check).and_return(false)
+        r = @scroll.visible?([@author])
+        r.should be_false
+      end
+    end
+    context '事前チェックする' do
+      before do
+        MagicNumber['run_mode'] = 1
+        Scroll.any_instance.stub(:reader_role_check).and_return(true)
+        Scroll.any_instance.stub(:own?).and_return(true)
+      end
+      it '自身に所持判定を問い合わせしている' do
+        Scroll.any_instance.should_receive(:own?).with(any_args).exactly(1)
+        r = @scroll.visible?([@author])
+      end
+    end
+    context 'つつがなく終わるとき' do
+      before do
+        MagicNumber['run_mode'] = 1
+        Scroll.any_instance.stub(:reader_role_check).and_return(true)
+      end
+      it '自分のコミックなら許可する' 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 '他人の非公開コミックなら許可しない' 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 '他人のコミックでも公開なら許可する' do
+        Scroll.any_instance.stub(:own?).and_return(false)
+        Scroll.any_instance.stub(:visible).and_return(1)
+        r = @scroll.visible?([@author])
+        r.should be_true
+      end
+    end
+  end
+  
+  describe '一覧取得に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+    end
+    context 'page補正について' do
+      it '文字列から数値に変換される' do
+        Scroll.page('8').should eq 8
+      end
+      it 'nilの場合は1になる' do
+        Scroll.page().should eq 1
+      end
+      it '0以下の場合は1になる' do
+        Scroll.page('0').should eq 1
+      end
+    end
+    context 'page_size補正について' do
+      it '文字列から数値に変換される' do
+        Scroll.page_size('7').should eq 7
+      end
+      it 'nilの場合はScroll.default_page_sizeになる' do
+        Scroll.page_size().should eq Scroll.default_page_size
+      end
+      it '0以下の場合はScroll.default_page_sizeになる' do
+        Scroll.page_size('0').should eq Scroll.default_page_size
+      end
+      it 'Scroll.max_page_sizeを超えた場合はScroll.max_page_sizeになる' do
+        Scroll.page_size('1000').should eq Scroll.max_page_size
+      end
+    end
+    context 'つつがなく終わるとき' do
+      it '一覧取得オプションを利用している' do
+        Scroll.stub(:list_opt).with(any_args).and_return({})
+        Scroll.should_receive(:list_opt).with(any_args).exactly(1)
+        r = Scroll.list
+      end
+    end
+    it 'リストを返す' do
+      c = Scroll.list
+      c.should eq [@scroll]
+    end
+    it '非公開コミックは(自分のコミックであっても)含んでいない' do
+      FactoryGirl.create :scroll, :author_id => @author.id, :visible => 0
+      c = Scroll.list
+      c.should eq [@scroll]
+    end
+    it '時系列で並んでいる' do
+      #公開コミックは(他人のコミックであっても)含んでいる
+      v = FactoryGirl.create :scroll, :author_id => @other_author.id, :updated_at => Time.now + 100
+      c = Scroll.list
+      c.should eq [v, @scroll]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @scroll2 = FactoryGirl.create :scroll, :author_id => @author.id, :updated_at => Time.now + 100
+        @scroll3 = FactoryGirl.create :scroll, :author_id => @author.id, :updated_at => Time.now + 200
+        @scroll4 = FactoryGirl.create :scroll, :author_id => @author.id, :updated_at => Time.now + 300
+        @scroll5 = FactoryGirl.create :scroll, :author_id => @author.id, :updated_at => Time.now + 400
+        Scroll.stub(:default_page_size).and_return(2)
+      end
+      it '通常は2件を返す' do
+        c = Scroll.list
+        c.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        c = Scroll.list(1)
+        c.should eq [@scroll5, @scroll4]
+      end
+      it 'page=2なら中間2件を返す' do
+        c = Scroll.list(2)
+        c.should eq [@scroll3, @scroll2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        c = Scroll.list(3)
+        c.should eq [@scroll]
+      end
+    end
+  end
+  
+  describe '自分のコミック一覧取得に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '一覧取得オプションを利用している' do
+        Scroll.stub(:list_opt).with(any_args).and_return({})
+        Scroll.should_receive(:list_opt).with(any_args).exactly(1)
+        r = Scroll.mylist @author
+      end
+    end
+    it 'リストを返す' do
+      c = Scroll.mylist @author
+      c.should eq [@scroll]
+    end
+    it '時系列で並んでいる' do
+      nc = FactoryGirl.create :scroll, :author_id => @author.id, :updated_at => Time.now + 100
+      cl = Scroll.mylist @author
+      cl.should eq [nc, @scroll]
+    end
+    it '他人のコミックは公開でも含まない' do
+      nc = FactoryGirl.create :scroll, :author_id => @other_author.id, :visible => 1
+      cl = Scroll.mylist @author
+      cl.should eq [@scroll]
+    end
+    it '自分のコミックは非公開でも含んでいる' 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]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @scroll2 = FactoryGirl.create :scroll, :author_id => @author.id, :updated_at => Time.now + 100
+        @scroll3 = FactoryGirl.create :scroll, :author_id => @author.id, :updated_at => Time.now + 200
+        @scroll4 = FactoryGirl.create :scroll, :author_id => @author.id, :updated_at => Time.now + 300
+        @scroll5 = FactoryGirl.create :scroll, :author_id => @author.id, :updated_at => Time.now + 400
+      end
+      it '通常は2件を返す' do
+        c = Scroll.mylist @author, 1, 2
+        c.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        c = Scroll.mylist(@author, 1, 2)
+        c.should eq [@scroll5, @scroll4]
+      end
+      it 'page=2なら中間2件を返す' do
+        c = Scroll.mylist(@author, 2, 2)
+        c.should eq [@scroll3, @scroll2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        c = Scroll.mylist(@author, 3, 2)
+        c.should eq [@scroll]
+      end
+    end
+  end
+  
+  describe '他作家のコミック一覧取得に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @other_scroll = FactoryGirl.create :scroll, :author_id => @other_author.id, :visible => 1
+    end
+    context 'つつがなく終わるとき' do
+      it '一覧取得オプションを利用している' do
+        Scroll.stub(:list_opt).with(any_args).and_return({})
+        Scroll.should_receive(:list_opt).with(any_args).exactly(1)
+        r = Scroll.himlist @other_author
+      end
+    end
+    it '指定した作家のリストを返す' do
+      r = Scroll.himlist @other_author
+      r.should eq [@other_scroll]
+    end
+    it '時系列で並んでいる' do
+      nc = FactoryGirl.create :scroll, :author_id => @other_author.id, :updated_at => Time.now + 100
+      r = Scroll.himlist @other_author
+      r.should eq [nc, @other_scroll]
+    end
+    it '公開コミックに限る ' do
+      hidden = FactoryGirl.create :scroll, :author_id => @other_author.id, :visible => 0
+      r = Scroll.himlist @other_author
+      r.should eq [@other_scroll]
+    end
+    context 'DBに5件あって1ページの件数を2件に変えたとして' do
+      before do
+        @other_scroll2 = FactoryGirl.create :scroll, :author_id => @other_author.id, :updated_at => Time.now + 100
+        @other_scroll3 = FactoryGirl.create :scroll, :author_id => @other_author.id, :updated_at => Time.now + 200
+        @other_scroll4 = FactoryGirl.create :scroll, :author_id => @other_author.id, :updated_at => Time.now + 300
+        @other_scroll5 = FactoryGirl.create :scroll, :author_id => @other_author.id, :updated_at => Time.now + 400
+      end
+      it '通常は2件を返す' do
+        c = Scroll.himlist @other_author, 1, 2
+        c.should have(2).items 
+      end
+      it 'page=1なら末尾2件を返す' do
+        #時系列で並んでいる
+        c = Scroll.himlist(@other_author, 1, 2)
+        c.should eq [@other_scroll5, @other_scroll4]
+      end
+      it 'page=2なら中間2件を返す' do
+        c = Scroll.himlist(@other_author, 2, 2)
+        c.should eq [@other_scroll3, @other_scroll2]
+      end
+      it 'page=3なら先頭1件を返す' do
+        c = Scroll.himlist(@other_author, 3, 2)
+        c.should eq [@other_scroll]
+      end
+    end
+  end
+  
+  describe 'コミック一覧ページ制御に於いて' do
+    before do
+      Scroll.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = Scroll.list_paginate 
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it 'コミック一覧の取得条件を利用している' do
+      Scroll.stub(:list_where).with(any_args).and_return('')
+      Scroll.should_receive(:list_where).with(any_args).exactly(1)
+      r = Scroll.list_paginate 
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = Scroll.list_paginate 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe '自分のコミック一覧ページ制御に於いて' do
+    before do
+      Scroll.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = Scroll.mylist_paginate @author
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it '自分のコミック一覧の取得条件を利用している' 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
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = Scroll.mylist_paginate @author, 3, 10
+      r.limit_value.should eq 10
+      r.offset_value.should eq 20
+    end
+  end
+  
+  describe '他作家のコミック一覧ページ制御に於いて' do
+    before do
+      Scroll.stub(:count).with(any_args).and_return(100)
+    end
+    it 'ページ制御を返す' do
+      r = Scroll.himlist_paginate @other_author
+      r.is_a?(Kaminari::PaginatableArray).should be_true
+    end
+    it '他作家のコミック一覧の取得条件を利用している' 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
+    end
+    it 'ページ件数10のとき、3ページ目のオフセットは20から始まる' do
+      r = Scroll.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 = Scroll.list_opt
+      r.should have(2).items
+    end
+    it 'ストーリーを含んでいる' do
+      r = Scroll.list_opt
+      r.has_key?(:scroll_panels).should be_true
+    end
+      it 'ストーリーはコマを含んでいる' do
+        r = Scroll.list_opt
+        r[:scroll_panels].has_key?(:panel).should be_true
+      end
+    it '作家を含んでいる' do
+      r = Scroll.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
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id, :visible => 1
+      @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 'ストーリーを含んでいる' 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 'ストーリーはコマを含んでいる' do
+        r = Scroll.list.to_json Scroll.list_json_opt
+        j = JSON.parse r
+        i = j.first
+        s = i['scroll_panels'].first
+        s.has_key?('panel').should be_true
+      end
+    it '作家を含んでいる' do
+      r = Scroll.list.to_json Scroll.list_json_opt
+      j = JSON.parse r
+      i = j.first
+      i.has_key?('author').should be_true
+    end
+  end
+  
+  describe '単体取得に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '単体取得オプションを利用している' do
+        Scroll.stub(:show_opt).with(any_args).and_return({})
+        Scroll.should_receive(:show_opt).with(any_args).exactly(1)
+        r = Scroll.show @scroll.id, @author
+      end
+      it '閲覧許可を問い合わせている' do
+        Scroll.any_instance.stub(:visible?).with(any_args).and_return(true)
+        Scroll.any_instance.should_receive(:visible?).with(any_args).exactly(1)
+        r = Scroll.show @scroll.id, @author
+      end
+    end
+    it '指定のコミックを返す' do
+      c = Scroll.show @scroll.id, @author
+      c.should eq @scroll
+    end
+    context '閲覧許可が出なかったとき' do
+      it '403Forbidden例外を返す' do
+        Scroll.any_instance.stub(:visible?).and_return(false)
+        lambda{
+          Scroll.show @scroll.id, @author
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+    context '存在しないコミックを開こうとしたとき' do
+      it '404RecordNotFound例外を返す' do
+        lambda{
+          Scroll.show 110, @author
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+    end
+  end
+  
+  describe '編集取得に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+    end
+    context 'つつがなく終わるとき' do
+      it '単体取得オプションを利用している' do
+        Scroll.stub(:show_opt).with(any_args).and_return({})
+        Scroll.should_receive(:show_opt).with(any_args).exactly(1)
+        r = Scroll.edit @scroll.id, @author
+      end
+      it '所持判定を問い合わせている' do
+        Scroll.any_instance.stub(:own?).with(any_args).and_return(true)
+        Scroll.any_instance.should_receive(:own?).with(any_args).exactly(1)
+        r = Scroll.edit @scroll.id, @author
+      end
+    end
+    it '指定のコミックを返す' do
+      Scroll.any_instance.stub(:own?).and_return(true)
+      c = Scroll.edit @scroll.id, @author.id
+      c.should eq @scroll
+    end
+    context '他人のコミックを開こうとしたとき' do
+      it '403Forbidden例外を返す' do
+        Scroll.any_instance.stub(:own?).and_return(false)
+        lambda{
+          Scroll.edit @scroll.id, @author
+        }.should raise_error(ActiveRecord::Forbidden)
+      end
+    end
+    context '存在しないコミックを開こうとしたとき' do
+      it '404RecordNotFound例外を返す' do
+        lambda{
+          Scroll.edit 110, @author
+        }.should raise_error(ActiveRecord::RecordNotFound)
+      end
+    end
+  end
+  describe '単体取得オプションに於いて' do
+    it 'includeキーを含んでいる' do
+      r = Scroll.show_opt
+      r.has_key?(:include).should be_true
+    end
+    it '2つの項目を含んでいる' do
+      r = Scroll.show_opt[:include]
+      r.should have(2).items
+    end
+    it '作家を含んでいる' do
+      r = Scroll.show_opt[:include]
+      r.has_key?(:author).should be_true
+    end
+    it 'ストーリーを含んでいる' do
+      r = Scroll.show_opt[:include]
+      r.has_key?(:scroll_panels).should be_true
+    end
+      it 'ストーリーはコマを含んでいる' do
+        r = Scroll.show_opt[:include]
+        r[:scroll_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
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id, :visible => 1
+      @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 'ストーリーを含んでいる' 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 'ストーリーはコマを含んでいる' do
+        r = Scroll.show(@scroll.id, @author).to_json Scroll.show_json_opt
+        j = JSON.parse r
+        i = j
+        s = i['scroll_panels'].first
+        s.has_key?('panel').should be_true
+      end
+    it '作家を含んでいる' do
+      r = Scroll.show(@scroll.id, @author).to_json Scroll.show_json_opt
+      j = JSON.parse r
+      i = j
+      i.has_key?('author').should be_true
+    end
+  end
+  
+  describe '削除に於いて' do
+    before do
+      @scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @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
+      @other_scroll = FactoryGirl.create :scroll, :author_id => @author.id
+      @other_scroll_panel = FactoryGirl.create :scroll_panel, :author_id => @author.id, :scroll_id => @other_scroll.id, :panel_id => @panel.id
+    end
+    context 'つつがなく終わるとき' do
+      it '自身を削除する' do
+        lambda {
+          r = @scroll.destroy_with_scroll_panel
+        }.should change(Scroll, :count).by(-1)
+        lambda {
+          r = Scroll.find @scroll.id
+        }.should raise_error
+      end
+      it '自身にリンクしているストーリーをすべて削除する' do
+        lambda {
+          r = @scroll.destroy_with_scroll_panel
+        }.should change(ScrollPanel, :count).by(-1)
+        lambda {
+          r = ScrollPanel.find @scroll_panel.id
+        }.should raise_error
+      end
+      it 'Trueを返す' do
+        r = @scroll.destroy_with_scroll_panel
+        r.should be_true
+      end
+    end
+    context '削除に失敗したとき' do
+      before do
+        ScrollPanel.any_instance.stub(:destroy).with(any_args).and_return(false)
+      end
+      it 'Falseを返す' do
+        r = @scroll.destroy_with_scroll_panel
+        r.should be_false
+      end
+      it 'ロールバックしている' do
+        lambda {
+          r = @scroll.destroy_with_scroll_panel
+        }.should_not change(Scroll, :count)
+        lambda {
+          r = @scroll.destroy_with_scroll_panel
+        }.should_not change(ScrollPanel, :count)
+      end
+    end
+  end
+end