Extract paginated collection gem

refs: CNVS-11648

Test Plan:

Test the sub_accounts api endpoint and make sure the pagination
is not broken.

Change-Id: Ie59674a7a834698ac432307940305d6958cb73f1
Reviewed-on: https://gerrit.instructure.com/31413
QA-Review: Jeremy Putnam <jeremyp@instructure.com>
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
Product-Review: Simon Williams <simon@instructure.com>
This commit is contained in:
Joseph Rodriguez 2014-03-05 16:31:18 -07:00 committed by Simon Williams
parent ec31030b0f
commit 86c8d2a7ed
14 changed files with 264 additions and 105 deletions

View File

@ -130,5 +130,6 @@ gem 'html_text_helper', :path => 'gems/html_text_helper'
gem 'json_token', :path => 'gems/json_token'
gem 'lti_outbound', :path => 'gems/lti_outbound'
gem 'multipart', :path => 'gems/multipart'
gem 'paginated_collection', :path => 'gems/paginated_collection'
gem 'utf8_cleaner', :path => 'gems/utf8_cleaner'
gem 'workflow', :path => 'gems/workflow'

View File

@ -2,6 +2,10 @@
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
unless defined?(CANVAS_RAILS3)
CANVAS_RAILS3 = !!ENV["CANVAS_RAILS3"] || File.exist?(File.expand_path("../../RAILS3", __FILE__))
end
Gem::Specification.new do |spec|
spec.name = "activesupport-suspend_callbacks"
spec.version = '0.0.1'
@ -14,7 +18,11 @@ Gem::Specification.new do |spec|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]
spec.add_dependency "activesupport"
if CANVAS_RAILS3
spec.add_dependency "activesupport", "3.2.17"
else
spec.add_dependency "activesupport", "~>2.3.17"
end
spec.add_development_dependency "bundler", "~> 1.5"
spec.add_development_dependency "rake"

View File

@ -1,15 +1,23 @@
#!/bin/bash
result=0
echo "################ Running tests against Rails 2 ################"
unset CANVAS_RAILS3
bundle install
bundle exec rspec spec
let result=$result+$?
echo "################ Running tests against Rails 3 ################"
rm -f Gemfile.lock
export CANVAS_RAILS3=true
bundle install
bundle exec rspec spec
let result=$result+$?
if [ $result -eq 0 ]; then
echo "SUCCESS"
echo "SUCCESS"
else
echo "FAILURE"
echo "FAILURE"
fi
exit $result

View File

@ -0,0 +1,3 @@
source 'https://rubygems.org'
gemspec

View File

@ -0,0 +1 @@
require "bundler/gem_tasks"

View File

@ -1,5 +1,5 @@
#
# Copyright (C) 2012 Instructure, Inc.
# Copyright (C) 2012-2014 Instructure, Inc.
#
# This file is part of Canvas.
#
@ -48,47 +48,15 @@
#
# Note that the block needs to edit the AR collection in-place, because it's a
# subclass of Array with paging information, rather than just a raw array.
require 'folio'
module PaginatedCollection
require 'paginated_collection/proxy'
require 'paginated_collection/collection'
def self.build(&block)
raise(ArgumentError, "block required") unless block
Proxy.new(block)
end
class Collection < Array
include Folio::Page
end
class Proxy
attr_accessor :block
def initialize(block)
@block = block
end
def paginate(options = {})
execute_pager(configure_pager(new_pager, options))
end
def new_pager
PaginatedCollection::Collection.new
end
def configure_pager(pager, options)
raise(ArgumentError, "per_page required") unless options[:per_page] && options[:per_page] > 0
current_page = options.fetch(:page) { nil }
current_page = pager.first_page if current_page.nil?
pager.current_page = current_page
pager.per_page = options[:per_page]
pager.total_entries = options[:total_entries]
pager
end
def execute_pager(pager)
pager = @block.call(pager)
if !pager.respond_to?(:current_page)
raise(ArgumentError, "The PaginatedCollection block needs to return a WillPaginate-style object")
end
return pager
end
PaginatedCollection::Proxy.new(block)
end
end

View File

@ -0,0 +1,23 @@
#
# Copyright (C) 2012-2014 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
module PaginatedCollection
class Collection < Array
include Folio::Page
end
end

View File

@ -0,0 +1,53 @@
#
# Copyright (C) 2012-2014 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
module PaginatedCollection
class Proxy
attr_accessor :block
def initialize(block)
@block = block
end
def paginate(options = {})
execute_pager(configure_pager(new_pager, options))
end
def new_pager
PaginatedCollection::Collection.new
end
def configure_pager(pager, options)
raise(ArgumentError, "per_page required") unless options[:per_page] && options[:per_page] > 0
current_page = options.fetch(:page) { nil }
current_page = pager.first_page if current_page.nil?
pager.current_page = current_page
pager.per_page = options[:per_page]
pager.total_entries = options[:total_entries]
pager
end
def execute_pager(pager)
pager = @block.call(pager)
if !pager.respond_to?(:current_page)
raise(ArgumentError, "The PaginatedCollection block needs to return a WillPaginate-style object")
end
return pager
end
end
end

View File

@ -0,0 +1,35 @@
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
unless defined?(CANVAS_RAILS3)
require File.expand_path("../../../config/canvas_rails3", __FILE__)
end
Gem::Specification.new do |spec|
spec.name = "paginated_collection"
spec.version = "1.0.0"
spec.authors = ["Brian Palmer"]
spec.email = ["brianp@instructure.com"]
spec.summary = %q{Paginated Collection gem}
spec.files = Dir.glob("{lib}/**/*") + %w(Rakefile)
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]
if CANVAS_RAILS3
spec.add_dependency "folio-pagination", "0.0.7"
spec.add_dependency "will_paginate", "3.0.4"
spec.add_dependency "rails", "3.2.17"
else
spec.add_dependency "folio-pagination-legacy", "0.0.3"
spec.add_dependency "will_paginate", "2.3.15"
spec.add_dependency "rails", "~> 2.3"
end
spec.add_development_dependency "bundler", "~> 1.5"
spec.add_development_dependency "rake"
spec.add_development_dependency "rspec"
spec.add_development_dependency "sqlite3"
end

View File

@ -0,0 +1,73 @@
#
# Copyright (C) 2012-2014 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require 'spec_helper'
describe PaginatedCollection do
describe '.build' do
it 'returns a #paginate proxy' do
expect { PaginatedCollection.build }.to raise_error(ArgumentError)
proxy = PaginatedCollection.build { |pager| pager }
expect(proxy).to be_a_kind_of(PaginatedCollection::Proxy)
expect(proxy.paginate(:per_page => 5).size).to eq 0
end
end
describe '#paginate' do
it 'uses the provided collection' do
expect { PaginatedCollection.build { |pager| [] }.paginate(:per_page => 5) }.to raise_error(ArgumentError)
items = PaginatedCollection.build { |pager| pager.replace([1, 2]) }.paginate(:page => 1, :per_page => 5)
expect(items).to eq [1, 2]
expect(items.size).to eq 2
expect(items.current_page).to eq 1
expect(items.per_page).to eq 5
expect(items.last_page).to eq 1
%w(first_page next_page previous_page total_entries).each do |a|
expect(items.send(a)).to be_nil
end
end
it 'uses the pager returned' do
example_klass = Class.new(ActiveRecord::Base) do
self.table_name = 'examples'
end
3.times { example_klass.create! }
proxy = PaginatedCollection.build do |pager|
result = example_klass.paginate(page: pager.current_page, per_page: pager.per_page)
result.map! { |example| example.id }
result
end
p1 = proxy.paginate(:page => 1, :per_page => 2)
expect(p1.current_page).to eq 1
expect(p1.next_page).to eq 2
expect(p1.previous_page).to be_nil
p2 = proxy.paginate(:page => 2, :per_page => 2)
expect(p2.current_page).to eq 2
expect(p2.next_page).to be_nil
expect(p2.previous_page).to eq 1
expect(p1).to eq example_klass.all.map(&:id)[0, 2]
expect(p2).to eq example_klass.all.map(&:id)[2, 1]
end
end
end

View File

@ -0,0 +1,15 @@
require 'folio/rails'
require 'folio/will_paginate/active_record' if defined?(CANVAS_RAILS3) && CANVAS_RAILS3
require 'paginated_collection'
require 'support/active_record'
RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true
config.run_all_when_everything_filtered = true
config.filter_run :focus
config.color_enabled = true
config.order = 'random'
end

View File

@ -0,0 +1,8 @@
require 'active_record'
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
ActiveRecord::Migration.verbose = false
ActiveRecord::Schema.define(version: 1) do
create_table :examples, force: true
end

View File

@ -0,0 +1,25 @@
#!/bin/bash
result=0
echo "################ Running tests against Rails 2 ################"
export CANVAS_RAILS3=0
bundle install
bundle exec rspec spec
let result=$result+$?
echo "################ Running tests against Rails 3 ################"
mv Gemfile.lock Gemfile.lock.rails2
export CANVAS_RAILS3=true
bundle install
bundle exec rspec spec
let result=$result+$?
mv Gemfile.lock.rails2 Gemfile.lock
if [ $result -eq 0 ]; then
echo "SUCCESS"
else
echo "FAILURE"
fi
exit $result

View File

@ -1,62 +0,0 @@
#
# Copyright (C) 2012 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
describe "PaginatedCollection" do
describe ".build" do
it "should return a #paginate proxy" do
expect { PaginatedCollection.build() }.to raise_error(ArgumentError)
proxy = PaginatedCollection.build { |pager| pager }
proxy.should be_is_a PaginatedCollection::Proxy
proxy.paginate(:per_page => 5).size.should == 0
end
end
describe "#paginate" do
it "should use the provided collection" do
expect { PaginatedCollection.build { |pager| [] }.paginate(:per_page => 5) }.to raise_error(ArgumentError)
items = PaginatedCollection.build { |pager| pager.replace([1,2]) }.paginate(:page => 1, :per_page => 5)
items.should == [1,2]
items.size.should == 2
items.current_page.should == 1
items.per_page.should == 5
items.last_page.should == 1
%w(first_page next_page previous_page total_entries).each { |a| items.send(a).should be_nil }
end
it "should use the pager returned" do
3.times { user_model }
proxy = PaginatedCollection.build do |pager|
result = User.active.order(:id).paginate(:page => pager.current_page, :per_page => pager.per_page)
result.map! { |u| u.id }
result
end
p1 = proxy.paginate(:page => 1, :per_page => 2)
p1.current_page.should == 1
p1.next_page.should == 2
p1.previous_page.should be_nil
p2 = proxy.paginate(:page => 2, :per_page => 2)
p2.current_page.should == 2
p2.next_page.should be_nil
p2.previous_page.should == 1
p1.should == User.active.order(:id).pluck(:id)[0,2]
p2.should == User.active.order(:id).pluck(:id)[2,1]
end
end
end