acts-as-list-0.7.2/0000755000175000017500000000000012634606423012430 5ustar aleealeeacts-as-list-0.7.2/init.rb0000644000175000017500000000010212634606423013711 0ustar aleealee$:.unshift "#{File.dirname(__FILE__)}/lib" require "acts_as_list" acts-as-list-0.7.2/Rakefile0000644000175000017500000000154112634606423014076 0ustar aleealeerequire "rubygems" require "bundler/setup" Bundler::GemHelper.install_tasks require "rake/testtask" # Run the test with "rake" or "rake test" desc "Default: run acts_as_list unit tests." task default: :test desc "Test the acts_as_list plugin." Rake::TestTask.new(:test) do |t| t.libs << "lib" << "test" t.pattern = "test/**/test_*.rb" t.verbose = false end begin # Run the rdoc task to generate rdocs for this gem require "rdoc/task" RDoc::Task.new do |rdoc| require "acts_as_list/version" version = ActiveRecord::Acts::List::VERSION rdoc.rdoc_dir = "rdoc" rdoc.title = "acts_as_list #{version}" rdoc.rdoc_files.include("README*") rdoc.rdoc_files.include("lib/**/*.rb") end rescue LoadError puts "RDocTask is not supported on this platform." rescue StandardError puts "RDocTask is not supported on this platform." end acts-as-list-0.7.2/gemfiles/0000755000175000017500000000000012634606423014223 5ustar aleealeeacts-as-list-0.7.2/gemfiles/rails_4_1.gemfile0000644000175000017500000000061112634606423017330 0ustar aleealee# This file was generated by Appraisal source "http://rubygems.org" gem "sqlite3", :platforms => [:ruby] gem "activerecord-jdbcsqlite3-adapter", :platforms => [:jruby] gem "rake" gem "appraisal" gem "activerecord", "4.1.10" group :test do gem "minitest" end platforms :rbx do gem "rubysl", "~> 2.0" gem "rubinius-developer_tools" gem "rubysl-test-unit" end gemspec :path => "../" acts-as-list-0.7.2/gemfiles/rails_4_2.gemfile0000644000175000017500000000061012634606423017330 0ustar aleealee# This file was generated by Appraisal source "http://rubygems.org" gem "sqlite3", :platforms => [:ruby] gem "activerecord-jdbcsqlite3-adapter", :platforms => [:jruby] gem "rake" gem "appraisal" gem "activerecord", "4.2.1" group :test do gem "minitest" end platforms :rbx do gem "rubysl", "~> 2.0" gem "rubinius-developer_tools" gem "rubysl-test-unit" end gemspec :path => "../" acts-as-list-0.7.2/gemfiles/rails_4_1.gemfile.lock0000644000175000017500000001541112634606423020263 0ustar aleealeePATH remote: ../ specs: acts_as_list (0.7.2) activerecord (>= 3.0) GEM remote: http://rubygems.org/ specs: activemodel (4.1.10) activesupport (= 4.1.10) builder (~> 3.1) activerecord (4.1.10) activemodel (= 4.1.10) activesupport (= 4.1.10) arel (~> 5.0.0) activesupport (4.1.10) i18n (~> 0.6, >= 0.6.9) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) thread_safe (~> 0.1) tzinfo (~> 1.1) appraisal (1.0.3) bundler rake thor (>= 0.14.0) arel (5.0.1.20140414130214) builder (3.2.2) ffi2-generators (0.1.1) i18n (0.7.0) json (1.8.2) minitest (5.5.1) rake (10.4.2) rubinius-coverage (2.0.3) rubinius-debugger (2.2.1) rubinius-developer_tools (2.0.0) rubinius-coverage (~> 2.0) rubinius-debugger (~> 2.0) rubinius-profiler (~> 2.0) rubinius-profiler (2.0.1) rubysl (2.1.0) rubysl-abbrev (~> 2.0) rubysl-base64 (~> 2.0) rubysl-benchmark (~> 2.0) rubysl-bigdecimal (~> 2.0) rubysl-cgi (~> 2.0) rubysl-cgi-session (~> 2.0) rubysl-cmath (~> 2.0) rubysl-complex (~> 2.0) rubysl-continuation (~> 2.0) rubysl-coverage (~> 2.0) rubysl-csv (~> 2.0) rubysl-curses (~> 2.0) rubysl-date (~> 2.0) rubysl-delegate (~> 2.0) rubysl-digest (~> 2.0) rubysl-drb (~> 2.0) rubysl-e2mmap (~> 2.0) rubysl-english (~> 2.0) rubysl-enumerator (~> 2.0) rubysl-erb (~> 2.0) rubysl-etc (~> 2.0) rubysl-expect (~> 2.0) rubysl-fcntl (~> 2.0) rubysl-fiber (~> 2.0) rubysl-fileutils (~> 2.0) rubysl-find (~> 2.0) rubysl-forwardable (~> 2.0) rubysl-getoptlong (~> 2.0) rubysl-gserver (~> 2.0) rubysl-io-console (~> 2.0) rubysl-io-nonblock (~> 2.0) rubysl-io-wait (~> 2.0) rubysl-ipaddr (~> 2.0) rubysl-irb (~> 2.1) rubysl-logger (~> 2.0) rubysl-mathn (~> 2.0) rubysl-matrix (~> 2.0) rubysl-mkmf (~> 2.0) rubysl-monitor (~> 2.0) rubysl-mutex_m (~> 2.0) rubysl-net-ftp (~> 2.0) rubysl-net-http (~> 2.0) rubysl-net-imap (~> 2.0) rubysl-net-pop (~> 2.0) rubysl-net-protocol (~> 2.0) rubysl-net-smtp (~> 2.0) rubysl-net-telnet (~> 2.0) rubysl-nkf (~> 2.0) rubysl-observer (~> 2.0) rubysl-open-uri (~> 2.0) rubysl-open3 (~> 2.0) rubysl-openssl (~> 2.0) rubysl-optparse (~> 2.0) rubysl-ostruct (~> 2.0) rubysl-pathname (~> 2.0) rubysl-prettyprint (~> 2.0) rubysl-prime (~> 2.0) rubysl-profile (~> 2.0) rubysl-profiler (~> 2.0) rubysl-pstore (~> 2.0) rubysl-pty (~> 2.0) rubysl-rational (~> 2.0) rubysl-resolv (~> 2.0) rubysl-rexml (~> 2.0) rubysl-rinda (~> 2.0) rubysl-rss (~> 2.0) rubysl-scanf (~> 2.0) rubysl-securerandom (~> 2.0) rubysl-set (~> 2.0) rubysl-shellwords (~> 2.0) rubysl-singleton (~> 2.0) rubysl-socket (~> 2.0) rubysl-stringio (~> 2.0) rubysl-strscan (~> 2.0) rubysl-sync (~> 2.0) rubysl-syslog (~> 2.0) rubysl-tempfile (~> 2.0) rubysl-thread (~> 2.0) rubysl-thwait (~> 2.0) rubysl-time (~> 2.0) rubysl-timeout (~> 2.0) rubysl-tmpdir (~> 2.0) rubysl-tsort (~> 2.0) rubysl-un (~> 2.0) rubysl-uri (~> 2.0) rubysl-weakref (~> 2.0) rubysl-webrick (~> 2.0) rubysl-xmlrpc (~> 2.0) rubysl-yaml (~> 2.0) rubysl-zlib (~> 2.0) rubysl-abbrev (2.0.4) rubysl-base64 (2.0.0) rubysl-benchmark (2.0.1) rubysl-bigdecimal (2.0.2) rubysl-cgi (2.0.1) rubysl-cgi-session (2.0.1) rubysl-cmath (2.0.0) rubysl-complex (2.0.0) rubysl-continuation (2.0.0) rubysl-coverage (2.0.3) rubysl-csv (2.0.2) rubysl-english (~> 2.0) rubysl-curses (2.0.1) rubysl-date (2.0.9) rubysl-delegate (2.0.1) rubysl-digest (2.0.3) rubysl-drb (2.0.1) rubysl-e2mmap (2.0.0) rubysl-english (2.0.0) rubysl-enumerator (2.0.0) rubysl-erb (2.0.2) rubysl-etc (2.0.3) ffi2-generators (~> 0.1) rubysl-expect (2.0.0) rubysl-fcntl (2.0.4) ffi2-generators (~> 0.1) rubysl-fiber (2.0.0) rubysl-fileutils (2.0.3) rubysl-find (2.0.1) rubysl-forwardable (2.0.1) rubysl-getoptlong (2.0.0) rubysl-gserver (2.0.0) rubysl-socket (~> 2.0) rubysl-thread (~> 2.0) rubysl-io-console (2.0.0) rubysl-io-nonblock (2.0.0) rubysl-io-wait (2.0.0) rubysl-ipaddr (2.0.0) rubysl-irb (2.1.1) rubysl-e2mmap (~> 2.0) rubysl-mathn (~> 2.0) rubysl-thread (~> 2.0) rubysl-logger (2.1.0) rubysl-mathn (2.0.0) rubysl-matrix (2.1.0) rubysl-e2mmap (~> 2.0) rubysl-mkmf (2.0.1) rubysl-fileutils (~> 2.0) rubysl-shellwords (~> 2.0) rubysl-monitor (2.0.0) rubysl-mutex_m (2.0.0) rubysl-net-ftp (2.0.1) rubysl-net-http (2.0.4) rubysl-cgi (~> 2.0) rubysl-erb (~> 2.0) rubysl-singleton (~> 2.0) rubysl-net-imap (2.0.1) rubysl-net-pop (2.0.1) rubysl-net-protocol (2.0.1) rubysl-net-smtp (2.0.1) rubysl-net-telnet (2.0.0) rubysl-nkf (2.0.1) rubysl-observer (2.0.0) rubysl-open-uri (2.0.0) rubysl-open3 (2.0.0) rubysl-openssl (2.2.1) rubysl-optparse (2.0.1) rubysl-shellwords (~> 2.0) rubysl-ostruct (2.0.4) rubysl-pathname (2.1.0) rubysl-prettyprint (2.0.3) rubysl-prime (2.0.1) rubysl-profile (2.0.0) rubysl-profiler (2.0.1) rubysl-pstore (2.0.0) rubysl-pty (2.0.3) rubysl-rational (2.0.1) rubysl-resolv (2.1.2) rubysl-rexml (2.0.4) rubysl-rinda (2.0.1) rubysl-rss (2.0.0) rubysl-scanf (2.0.0) rubysl-securerandom (2.0.0) rubysl-set (2.0.1) rubysl-shellwords (2.0.0) rubysl-singleton (2.0.0) rubysl-socket (2.0.1) rubysl-stringio (2.0.0) rubysl-strscan (2.0.0) rubysl-sync (2.0.0) rubysl-syslog (2.1.0) ffi2-generators (~> 0.1) rubysl-tempfile (2.0.1) rubysl-test-unit (2.0.2) minitest rubysl-thread (2.0.3) rubysl-thwait (2.0.0) rubysl-time (2.0.3) rubysl-timeout (2.0.0) rubysl-tmpdir (2.0.1) rubysl-tsort (2.0.1) rubysl-un (2.0.0) rubysl-fileutils (~> 2.0) rubysl-optparse (~> 2.0) rubysl-uri (2.0.0) rubysl-weakref (2.0.0) rubysl-webrick (2.0.0) rubysl-xmlrpc (2.0.0) rubysl-yaml (2.1.0) rubysl-zlib (2.0.1) sqlite3 (1.3.10) thor (0.19.1) thread_safe (0.3.5) tzinfo (1.2.2) thread_safe (~> 0.1) PLATFORMS ruby DEPENDENCIES activerecord (= 4.1.10) activerecord-jdbcsqlite3-adapter acts_as_list! appraisal bundler (>= 1.0.0) minitest rake rubinius-developer_tools rubysl (~> 2.0) rubysl-test-unit sqlite3 acts-as-list-0.7.2/gemfiles/rails_4_2.gemfile.lock0000644000175000017500000000173312634606423020266 0ustar aleealeePATH remote: ../ specs: acts_as_list (0.7.2) activerecord (>= 3.0) GEM remote: http://rubygems.org/ specs: activemodel (4.2.1) activesupport (= 4.2.1) builder (~> 3.1) activerecord (4.2.1) activemodel (= 4.2.1) activesupport (= 4.2.1) arel (~> 6.0) activesupport (4.2.1) i18n (~> 0.7) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) appraisal (1.0.3) bundler rake thor (>= 0.14.0) arel (6.0.0) builder (3.2.2) i18n (0.7.0) json (1.8.2) minitest (5.5.1) rake (10.4.2) sqlite3 (1.3.10) thor (0.19.1) thread_safe (0.3.5) tzinfo (1.2.2) thread_safe (~> 0.1) PLATFORMS ruby DEPENDENCIES activerecord (= 4.2.1) activerecord-jdbcsqlite3-adapter acts_as_list! appraisal bundler (>= 1.0.0) minitest rake rubinius-developer_tools rubysl (~> 2.0) rubysl-test-unit sqlite3 acts-as-list-0.7.2/gemfiles/rails_3_2.gemfile.lock0000644000175000017500000001523512634606423020267 0ustar aleealeePATH remote: ../ specs: acts_as_list (0.7.2) activerecord (>= 3.0) GEM remote: http://rubygems.org/ specs: activemodel (3.2.21) activesupport (= 3.2.21) builder (~> 3.0.0) activerecord (3.2.21) activemodel (= 3.2.21) activesupport (= 3.2.21) arel (~> 3.0.2) tzinfo (~> 0.3.29) activesupport (3.2.21) i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) appraisal (1.0.2) bundler rake thor (>= 0.14.0) arel (3.0.3) builder (3.0.4) ffi2-generators (0.1.1) i18n (0.7.0) minitest (5.5.1) multi_json (1.10.1) rake (10.4.2) rubinius-coverage (2.0.3) rubinius-debugger (2.2.1) rubinius-developer_tools (2.0.0) rubinius-coverage (~> 2.0) rubinius-debugger (~> 2.0) rubinius-profiler (~> 2.0) rubinius-profiler (2.0.1) rubysl (2.1.0) rubysl-abbrev (~> 2.0) rubysl-base64 (~> 2.0) rubysl-benchmark (~> 2.0) rubysl-bigdecimal (~> 2.0) rubysl-cgi (~> 2.0) rubysl-cgi-session (~> 2.0) rubysl-cmath (~> 2.0) rubysl-complex (~> 2.0) rubysl-continuation (~> 2.0) rubysl-coverage (~> 2.0) rubysl-csv (~> 2.0) rubysl-curses (~> 2.0) rubysl-date (~> 2.0) rubysl-delegate (~> 2.0) rubysl-digest (~> 2.0) rubysl-drb (~> 2.0) rubysl-e2mmap (~> 2.0) rubysl-english (~> 2.0) rubysl-enumerator (~> 2.0) rubysl-erb (~> 2.0) rubysl-etc (~> 2.0) rubysl-expect (~> 2.0) rubysl-fcntl (~> 2.0) rubysl-fiber (~> 2.0) rubysl-fileutils (~> 2.0) rubysl-find (~> 2.0) rubysl-forwardable (~> 2.0) rubysl-getoptlong (~> 2.0) rubysl-gserver (~> 2.0) rubysl-io-console (~> 2.0) rubysl-io-nonblock (~> 2.0) rubysl-io-wait (~> 2.0) rubysl-ipaddr (~> 2.0) rubysl-irb (~> 2.1) rubysl-logger (~> 2.0) rubysl-mathn (~> 2.0) rubysl-matrix (~> 2.0) rubysl-mkmf (~> 2.0) rubysl-monitor (~> 2.0) rubysl-mutex_m (~> 2.0) rubysl-net-ftp (~> 2.0) rubysl-net-http (~> 2.0) rubysl-net-imap (~> 2.0) rubysl-net-pop (~> 2.0) rubysl-net-protocol (~> 2.0) rubysl-net-smtp (~> 2.0) rubysl-net-telnet (~> 2.0) rubysl-nkf (~> 2.0) rubysl-observer (~> 2.0) rubysl-open-uri (~> 2.0) rubysl-open3 (~> 2.0) rubysl-openssl (~> 2.0) rubysl-optparse (~> 2.0) rubysl-ostruct (~> 2.0) rubysl-pathname (~> 2.0) rubysl-prettyprint (~> 2.0) rubysl-prime (~> 2.0) rubysl-profile (~> 2.0) rubysl-profiler (~> 2.0) rubysl-pstore (~> 2.0) rubysl-pty (~> 2.0) rubysl-rational (~> 2.0) rubysl-resolv (~> 2.0) rubysl-rexml (~> 2.0) rubysl-rinda (~> 2.0) rubysl-rss (~> 2.0) rubysl-scanf (~> 2.0) rubysl-securerandom (~> 2.0) rubysl-set (~> 2.0) rubysl-shellwords (~> 2.0) rubysl-singleton (~> 2.0) rubysl-socket (~> 2.0) rubysl-stringio (~> 2.0) rubysl-strscan (~> 2.0) rubysl-sync (~> 2.0) rubysl-syslog (~> 2.0) rubysl-tempfile (~> 2.0) rubysl-thread (~> 2.0) rubysl-thwait (~> 2.0) rubysl-time (~> 2.0) rubysl-timeout (~> 2.0) rubysl-tmpdir (~> 2.0) rubysl-tsort (~> 2.0) rubysl-un (~> 2.0) rubysl-uri (~> 2.0) rubysl-weakref (~> 2.0) rubysl-webrick (~> 2.0) rubysl-xmlrpc (~> 2.0) rubysl-yaml (~> 2.0) rubysl-zlib (~> 2.0) rubysl-abbrev (2.0.4) rubysl-base64 (2.0.0) rubysl-benchmark (2.0.1) rubysl-bigdecimal (2.0.2) rubysl-cgi (2.0.1) rubysl-cgi-session (2.0.1) rubysl-cmath (2.0.0) rubysl-complex (2.0.0) rubysl-continuation (2.0.0) rubysl-coverage (2.0.3) rubysl-csv (2.0.2) rubysl-english (~> 2.0) rubysl-curses (2.0.1) rubysl-date (2.0.9) rubysl-delegate (2.0.1) rubysl-digest (2.0.3) rubysl-drb (2.0.1) rubysl-e2mmap (2.0.0) rubysl-english (2.0.0) rubysl-enumerator (2.0.0) rubysl-erb (2.0.2) rubysl-etc (2.0.3) ffi2-generators (~> 0.1) rubysl-expect (2.0.0) rubysl-fcntl (2.0.4) ffi2-generators (~> 0.1) rubysl-fiber (2.0.0) rubysl-fileutils (2.0.3) rubysl-find (2.0.1) rubysl-forwardable (2.0.1) rubysl-getoptlong (2.0.0) rubysl-gserver (2.0.0) rubysl-socket (~> 2.0) rubysl-thread (~> 2.0) rubysl-io-console (2.0.0) rubysl-io-nonblock (2.0.0) rubysl-io-wait (2.0.0) rubysl-ipaddr (2.0.0) rubysl-irb (2.1.1) rubysl-e2mmap (~> 2.0) rubysl-mathn (~> 2.0) rubysl-thread (~> 2.0) rubysl-logger (2.1.0) rubysl-mathn (2.0.0) rubysl-matrix (2.1.0) rubysl-e2mmap (~> 2.0) rubysl-mkmf (2.0.1) rubysl-fileutils (~> 2.0) rubysl-shellwords (~> 2.0) rubysl-monitor (2.0.0) rubysl-mutex_m (2.0.0) rubysl-net-ftp (2.0.1) rubysl-net-http (2.0.4) rubysl-cgi (~> 2.0) rubysl-erb (~> 2.0) rubysl-singleton (~> 2.0) rubysl-net-imap (2.0.1) rubysl-net-pop (2.0.1) rubysl-net-protocol (2.0.1) rubysl-net-smtp (2.0.1) rubysl-net-telnet (2.0.0) rubysl-nkf (2.0.1) rubysl-observer (2.0.0) rubysl-open-uri (2.0.0) rubysl-open3 (2.0.0) rubysl-openssl (2.2.1) rubysl-optparse (2.0.1) rubysl-shellwords (~> 2.0) rubysl-ostruct (2.0.4) rubysl-pathname (2.1.0) rubysl-prettyprint (2.0.3) rubysl-prime (2.0.1) rubysl-profile (2.0.0) rubysl-profiler (2.0.1) rubysl-pstore (2.0.0) rubysl-pty (2.0.3) rubysl-rational (2.0.1) rubysl-resolv (2.1.2) rubysl-rexml (2.0.4) rubysl-rinda (2.0.1) rubysl-rss (2.0.0) rubysl-scanf (2.0.0) rubysl-securerandom (2.0.0) rubysl-set (2.0.1) rubysl-shellwords (2.0.0) rubysl-singleton (2.0.0) rubysl-socket (2.0.1) rubysl-stringio (2.0.0) rubysl-strscan (2.0.0) rubysl-sync (2.0.0) rubysl-syslog (2.1.0) ffi2-generators (~> 0.1) rubysl-tempfile (2.0.1) rubysl-test-unit (2.0.2) minitest rubysl-thread (2.0.3) rubysl-thwait (2.0.0) rubysl-time (2.0.3) rubysl-timeout (2.0.0) rubysl-tmpdir (2.0.1) rubysl-tsort (2.0.1) rubysl-un (2.0.0) rubysl-fileutils (~> 2.0) rubysl-optparse (~> 2.0) rubysl-uri (2.0.0) rubysl-weakref (2.0.0) rubysl-webrick (2.0.0) rubysl-xmlrpc (2.0.0) rubysl-yaml (2.1.0) rubysl-zlib (2.0.1) sqlite3 (1.3.10) thor (0.19.1) tzinfo (0.3.42) PLATFORMS ruby DEPENDENCIES activerecord (= 3.2.21) activerecord-jdbcsqlite3-adapter acts_as_list! appraisal bundler (>= 1.0.0) minitest rake rubinius-developer_tools rubysl (~> 2.0) rubysl-test-unit sqlite3 acts-as-list-0.7.2/gemfiles/rails_3_2.gemfile0000644000175000017500000000061112634606423017330 0ustar aleealee# This file was generated by Appraisal source "http://rubygems.org" gem "sqlite3", :platforms => [:ruby] gem "activerecord-jdbcsqlite3-adapter", :platforms => [:jruby] gem "rake" gem "appraisal" gem "activerecord", "3.2.21" group :test do gem "minitest" end platforms :rbx do gem "rubysl", "~> 2.0" gem "rubinius-developer_tools" gem "rubysl-test-unit" end gemspec :path => "../" acts-as-list-0.7.2/acts_as_list.gemspec0000644000175000017500000000270712634606423016453 0ustar aleealee# -*- encoding: utf-8 -*- $:.push File.expand_path("../lib", __FILE__) require "acts_as_list/version" Gem::Specification.new do |s| # Description Meta... s.name = "acts_as_list" s.version = ActiveRecord::Acts::List::VERSION s.platform = Gem::Platform::RUBY s.authors = ["David Heinemeier Hansson", "Swanand Pagnis", "Quinn Chaffee"] s.email = ["swanand.pagnis@gmail.com"] s.homepage = "http://github.com/swanandp/acts_as_list" s.summary = "A gem adding sorting, reordering capabilities to an active_record model, allowing it to act as a list" s.description = 'This "acts_as" extension provides the capabilities for sorting and reordering a number of objects in a list. The class that has this specified needs to have a "position" column defined as an integer on the mapped database table.' s.license = "MIT" s.rubyforge_project = "acts_as_list" s.required_ruby_version = ">= 1.9.2" # Load Paths... s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) } s.require_paths = ["lib"] # Dependencies (installed via "bundle install") s.add_dependency("activerecord", [">= 3.0"]) s.add_development_dependency("bundler", [">= 1.0.0"]) end acts-as-list-0.7.2/lib/0000755000175000017500000000000012634606423013176 5ustar aleealeeacts-as-list-0.7.2/lib/acts_as_list/0000755000175000017500000000000012634606423015646 5ustar aleealeeacts-as-list-0.7.2/lib/acts_as_list/version.rb0000644000175000017500000000013412634606423017656 0ustar aleealeemodule ActiveRecord module Acts module List VERSION = '0.7.2' end end end acts-as-list-0.7.2/lib/acts_as_list/active_record/0000755000175000017500000000000012634606423020457 5ustar aleealeeacts-as-list-0.7.2/lib/acts_as_list/active_record/acts/0000755000175000017500000000000012634606423021411 5ustar aleealeeacts-as-list-0.7.2/lib/acts_as_list/active_record/acts/list.rb0000644000175000017500000004377112634606423022725 0ustar aleealeemodule ActiveRecord module Acts #:nodoc: module List #:nodoc: def self.included(base) base.extend(ClassMethods) end # This +acts_as+ extension provides the capabilities for sorting and reordering a number of objects in a list. # The class that has this specified needs to have a +position+ column defined as an integer on # the mapped database table. # # Todo list example: # # class TodoList < ActiveRecord::Base # has_many :todo_items, order: "position" # end # # class TodoItem < ActiveRecord::Base # belongs_to :todo_list # acts_as_list scope: :todo_list # end # # todo_list.first.move_to_bottom # todo_list.last.move_higher module ClassMethods # Configuration options are: # # * +column+ - specifies the column name to use for keeping the position integer (default: +position+) # * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach _id # (if it hasn't already been added) and use that as the foreign key restriction. It's also possible # to give it an entire string that is interpolated if you need a tighter scope than just a foreign key. # Example: acts_as_list scope: 'todo_list_id = #{todo_list_id} AND completed = 0' # * +top_of_list+ - defines the integer used for the top of the list. Defaults to 1. Use 0 to make the collection # act more like an array in its indexing. # * +add_new_at+ - specifies whether objects get added to the :top or :bottom of the list. (default: +bottom+) # `nil` will result in new items not being added to the list on create def acts_as_list(options = {}) configuration = { column: "position", scope: "1 = 1", top_of_list: 1, add_new_at: :bottom} configuration.update(options) if options.is_a?(Hash) configuration[:scope] = "#{configuration[:scope]}_id".intern if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/ if configuration[:scope].is_a?(Symbol) scope_methods = %( def scope_condition { :#{configuration[:scope].to_s} => send(:#{configuration[:scope].to_s}) } end def scope_changed? changes.include?(scope_name.to_s) end ) elsif configuration[:scope].is_a?(Array) scope_methods = %( def attrs %w(#{configuration[:scope].join(" ")}).inject({}) do |memo,column| memo[column.intern] = read_attribute(column.intern); memo end end def scope_changed? (attrs.keys & changes.keys.map(&:to_sym)).any? end def scope_condition attrs end ) else scope_methods = %( def scope_condition "#{configuration[:scope]}" end def scope_changed?() false end ) end class_eval <<-EOV include ::ActiveRecord::Acts::List::InstanceMethods def acts_as_list_top #{configuration[:top_of_list]}.to_i end def acts_as_list_class ::#{self.name} end def position_column '#{configuration[:column]}' end def scope_name '#{configuration[:scope]}' end def add_new_at '#{configuration[:add_new_at]}' end def #{configuration[:column]}=(position) write_attribute(:#{configuration[:column]}, position) @position_changed = true end #{scope_methods} # only add to attr_accessible # if the class has some mass_assignment_protection if defined?(accessible_attributes) and !accessible_attributes.blank? attr_accessible :#{configuration[:column]} end before_destroy :reload_position after_destroy :decrement_positions_on_lower_items before_update :check_scope after_update :update_positions before_validation :check_top_position scope :in_list, lambda { where("#{table_name}.#{configuration[:column]} IS NOT NULL") } EOV if configuration[:add_new_at].present? self.send(:before_create, "add_to_list_#{configuration[:add_new_at]}") end end end # All the methods available to a record that has had acts_as_list specified. Each method works # by assuming the object to be the item in the list, so chapter.move_lower would move that chapter # lower in the list of all chapters. Likewise, chapter.first? would return +true+ if that chapter is # the first in the list of all chapters. module InstanceMethods # Insert the item at the given position (defaults to the top position of 1). def insert_at(position = acts_as_list_top) insert_at_position(position) end # Swap positions with the next lower item, if one exists. def move_lower return unless lower_item acts_as_list_class.transaction do lower_item.decrement_position increment_position end end # Swap positions with the next higher item, if one exists. def move_higher return unless higher_item acts_as_list_class.transaction do higher_item.increment_position decrement_position end end # Move to the bottom of the list. If the item is already in the list, the items below it have their # position adjusted accordingly. def move_to_bottom return unless in_list? acts_as_list_class.transaction do decrement_positions_on_lower_items assume_bottom_position end end # Move to the top of the list. If the item is already in the list, the items above it have their # position adjusted accordingly. def move_to_top return unless in_list? acts_as_list_class.transaction do increment_positions_on_higher_items assume_top_position end end # Removes the item from the list. def remove_from_list if in_list? decrement_positions_on_lower_items set_list_position(nil) end end # Move the item within scope. If a position within the new scope isn't supplied, the item will # be appended to the end of the list. def move_within_scope(scope_id) send("#{scope_name}=", scope_id) save! end # Increase the position of this item without adjusting the rest of the list. def increment_position return unless in_list? set_list_position(self.send(position_column).to_i + 1) end # Decrease the position of this item without adjusting the rest of the list. def decrement_position return unless in_list? set_list_position(self.send(position_column).to_i - 1) end # Return +true+ if this object is the first in the list. def first? return false unless in_list? self.send(position_column) == acts_as_list_top end # Return +true+ if this object is the last in the list. def last? return false unless in_list? self.send(position_column) == bottom_position_in_list end # Return the next higher item in the list. def higher_item return nil unless in_list? acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where("#{position_column} < #{(send(position_column).to_i).to_s}"). order("#{acts_as_list_class.table_name}.#{position_column} DESC").first end end # Return the next n higher items in the list # selects all higher items by default def higher_items(limit=nil) limit ||= acts_as_list_list.count position_value = send(position_column) acts_as_list_list. where("#{position_column} < ?", position_value). where("#{position_column} >= ?", position_value - limit). limit(limit). order("#{acts_as_list_class.table_name}.#{position_column} ASC") end # Return the next lower item in the list. def lower_item return nil unless in_list? acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where("#{position_column} > #{(send(position_column).to_i).to_s}"). order("#{acts_as_list_class.table_name}.#{position_column} ASC").first end end # Return the next n lower items in the list # selects all lower items by default def lower_items(limit=nil) limit ||= acts_as_list_list.count position_value = send(position_column) acts_as_list_list. where("#{position_column} > ?", position_value). where("#{position_column} <= ?", position_value + limit). limit(limit). order("#{acts_as_list_class.table_name}.#{position_column} ASC") end # Test if this record is in a list def in_list? !not_in_list? end def not_in_list? send(position_column).nil? end def default_position acts_as_list_class.columns_hash[position_column.to_s].default end def default_position? default_position && default_position.to_i == send(position_column) end # Sets the new position and saves it def set_list_position(new_position) write_attribute position_column, new_position save(validate: false) end private def acts_as_list_list acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition) end end def add_to_list_top increment_positions_on_all_items self[position_column] = acts_as_list_top end def add_to_list_bottom if not_in_list? || scope_changed? && !@position_changed || default_position? self[position_column] = bottom_position_in_list.to_i + 1 else increment_positions_on_lower_items(self[position_column], id) end end # Overwrite this method to define the scope of the list changes def scope_condition() {} end # Returns the bottom position number in the list. # bottom_position_in_list # => 2 def bottom_position_in_list(except = nil) item = bottom_item(except) item ? item.send(position_column) : acts_as_list_top - 1 end # Returns the bottom item def bottom_item(except = nil) conditions = scope_condition conditions = except ? "#{self.class.primary_key} != #{self.class.connection.quote(except.id)}" : {} acts_as_list_class.unscoped do acts_as_list_class.in_list.where(scope_condition).where(conditions).order("#{acts_as_list_class.table_name}.#{position_column} DESC").first end end # Forces item to assume the bottom position in the list. def assume_bottom_position set_list_position(bottom_position_in_list(self).to_i + 1) end # Forces item to assume the top position in the list. def assume_top_position set_list_position(acts_as_list_top) end # This has the effect of moving all the higher items up one. def decrement_positions_on_higher_items(position) acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where( "#{position_column} <= #{position}" ).update_all( "#{position_column} = (#{position_column} - 1)" ) end end # This has the effect of moving all the lower items up one. def decrement_positions_on_lower_items(position=nil) return unless in_list? position ||= send(position_column).to_i acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where( "#{position_column} > #{position}" ).update_all( "#{position_column} = (#{position_column} - 1)" ) end end # This has the effect of moving all the higher items down one. def increment_positions_on_higher_items return unless in_list? acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where( "#{position_column} < #{send(position_column).to_i}" ).update_all( "#{position_column} = (#{position_column} + 1)" ) end end # This has the effect of moving all the lower items down one. def increment_positions_on_lower_items(position, avoid_id = nil) avoid_id_condition = avoid_id ? " AND #{self.class.primary_key} != #{self.class.connection.quote(avoid_id)}" : '' acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where( "#{position_column} >= #{position}#{avoid_id_condition}" ).update_all( "#{position_column} = (#{position_column} + 1)" ) end end # Increments position (position_column) of all items in the list. def increment_positions_on_all_items acts_as_list_class.unscoped do acts_as_list_class.where( scope_condition ).update_all( "#{position_column} = (#{position_column} + 1)" ) end end # Reorders intermediate items to support moving an item from old_position to new_position. def shuffle_positions_on_intermediate_items(old_position, new_position, avoid_id = nil) return if old_position == new_position avoid_id_condition = avoid_id ? " AND #{self.class.primary_key} != #{self.class.connection.quote(avoid_id)}" : '' if old_position < new_position # Decrement position of intermediate items # # e.g., if moving an item from 2 to 5, # move [3, 4, 5] to [2, 3, 4] acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where( "#{position_column} > #{old_position}" ).where( "#{position_column} <= #{new_position}#{avoid_id_condition}" ).update_all( "#{position_column} = (#{position_column} - 1)" ) end else # Increment position of intermediate items # # e.g., if moving an item from 5 to 2, # move [2, 3, 4] to [3, 4, 5] acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where( "#{position_column} >= #{new_position}" ).where( "#{position_column} < #{old_position}#{avoid_id_condition}" ).update_all( "#{position_column} = (#{position_column} + 1)" ) end end end def insert_at_position(position) return set_list_position(position) if new_record? if in_list? old_position = send(position_column).to_i return if position == old_position shuffle_positions_on_intermediate_items(old_position, position) else increment_positions_on_lower_items(position) end set_list_position(position) end # used by insert_at_position instead of remove_from_list, as postgresql raises error if position_column has non-null constraint def store_at_0 if in_list? old_position = send(position_column).to_i set_list_position(0) decrement_positions_on_lower_items(old_position) end end def update_positions old_position = send("#{position_column}_was").to_i new_position = send(position_column).to_i return unless acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where("#{position_column} = #{new_position}").count > 1 end shuffle_positions_on_intermediate_items old_position, new_position, id end # Temporarily swap changes attributes with current attributes def swap_changed_attributes @changed_attributes.each { |k, _| @changed_attributes[k], self[k] = self[k], @changed_attributes[k] } end def check_scope if scope_changed? swap_changed_attributes send('decrement_positions_on_lower_items') if lower_item swap_changed_attributes send("add_to_list_#{add_new_at}") end end def reload_position self.reload end # This check is skipped if the position is currently the default position from the table # as modifying the default position on creation is handled elsewhere def check_top_position if send(position_column) && !default_position? && send(position_column) < acts_as_list_top self[position_column] = acts_as_list_top end end end end end end acts-as-list-0.7.2/lib/acts_as_list.rb0000644000175000017500000000070012634606423016170 0ustar aleealeerequire 'acts_as_list/active_record/acts/list' module ActsAsList if defined?(Rails::Railtie) class Railtie < Rails::Railtie initializer 'acts_as_list.insert_into_active_record' do ActiveSupport.on_load :active_record do ActiveRecord::Base.send(:include, ActiveRecord::Acts::List) end end end else ActiveRecord::Base.send(:include, ActiveRecord::Acts::List) if defined?(ActiveRecord) end end acts-as-list-0.7.2/.gitignore0000644000175000017500000000013312634606423014415 0ustar aleealee*.gem .bundle Gemfile.lock pkg/* .rvmrc *.tmproj .rbenv-version .ruby-gemset .ruby-version acts-as-list-0.7.2/.gemtest0000644000175000017500000000000012634606423014067 0ustar aleealeeacts-as-list-0.7.2/test/0000755000175000017500000000000012634606423013407 5ustar aleealeeacts-as-list-0.7.2/test/shared_no_addition.rb0000644000175000017500000000111212634606423017544 0ustar aleealeemodule Shared module NoAddition def setup (1..4).each { |counter| NoAdditionMixin.create! pos: counter, parent_id: 5 } end def test_insert new = NoAdditionMixin.create(parent_id: 20) assert_equal nil, new.pos assert !new.in_list? new = NoAdditionMixin.create(parent_id: 20) assert_equal nil, new.pos end def test_update_does_not_add_to_list new = NoAdditionMixin.create(parent_id: 20) new.update_attribute(:updated_at, Time.now) # force some change new.reload assert !new.in_list? end end end acts-as-list-0.7.2/test/shared_top_addition.rb0000644000175000017500000000533112634606423017741 0ustar aleealeemodule Shared module TopAddition def setup (1..4).each { |counter| TopAdditionMixin.create! pos: counter, parent_id: 5 } end def test_reordering assert_equal [4, 3, 2, 1], TopAdditionMixin.where(parent_id: 5).order('pos').map(&:id) TopAdditionMixin.where(id: 2).first.move_lower assert_equal [4, 3, 1, 2], TopAdditionMixin.where(parent_id: 5).order('pos').map(&:id) TopAdditionMixin.where(id: 2).first.move_higher assert_equal [4, 3, 2, 1], TopAdditionMixin.where(parent_id: 5).order('pos').map(&:id) TopAdditionMixin.where(id: 1).first.move_to_bottom assert_equal [4, 3, 2, 1], TopAdditionMixin.where(parent_id: 5).order('pos').map(&:id) TopAdditionMixin.where(id: 1).first.move_to_top assert_equal [1, 4, 3, 2], TopAdditionMixin.where(parent_id: 5).order('pos').map(&:id) TopAdditionMixin.where(id: 2).first.move_to_bottom assert_equal [1, 4, 3, 2], TopAdditionMixin.where(parent_id: 5).order('pos').map(&:id) TopAdditionMixin.where(id: 4).first.move_to_top assert_equal [4, 1, 3, 2], TopAdditionMixin.where(parent_id: 5).order('pos').map(&:id) end def test_injection item = TopAdditionMixin.new(parent_id: 1) assert_equal({ parent_id: 1 }, item.scope_condition) assert_equal "pos", item.position_column end def test_insert new = TopAdditionMixin.create(parent_id: 20) assert_equal 1, new.pos assert new.first? assert new.last? new = TopAdditionMixin.create(parent_id: 20) assert_equal 1, new.pos assert new.first? assert !new.last? new = TopAdditionMixin.create(parent_id: 20) assert_equal 1, new.pos assert new.first? assert !new.last? new = TopAdditionMixin.create(parent_id: 0) assert_equal 1, new.pos assert new.first? assert new.last? end def test_insert_at new = TopAdditionMixin.create(parent_id: 20) assert_equal 1, new.pos new = TopAdditionMixin.create(parent_id: 20) assert_equal 1, new.pos new = TopAdditionMixin.create(parent_id: 20) assert_equal 1, new.pos new4 = TopAdditionMixin.create(parent_id: 20) assert_equal 1, new4.pos new4.insert_at(3) assert_equal 3, new4.pos end def test_delete_middle assert_equal [4, 3, 2, 1], TopAdditionMixin.where(parent_id: 5).order('pos').map(&:id) TopAdditionMixin.where(id: 2).first.destroy assert_equal [4, 3, 1], TopAdditionMixin.where(parent_id: 5).order('pos').map(&:id) assert_equal 3, TopAdditionMixin.where(id: 1).first.pos assert_equal 2, TopAdditionMixin.where(id: 3).first.pos assert_equal 1, TopAdditionMixin.where(id: 4).first.pos end end end acts-as-list-0.7.2/test/helper.rb0000644000175000017500000000053412634606423015215 0ustar aleealeerequire "rubygems" require "bundler/setup" begin Bundler.setup(:default, :development) rescue Bundler::BundlerError => e $stderr.puts e.message $stderr.puts "Run `bundle install` to install missing gems" exit e.status_code end require "active_record" require "minitest/autorun" require "#{File.dirname(__FILE__)}/../init" require "shared" acts-as-list-0.7.2/test/test_list.rb0000644000175000017500000004310212634606423015746 0ustar aleealee# NOTE: following now done in helper.rb (better Readability) require 'helper' ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Schema.verbose = false def setup_db(position_options = {}) # AR caches columns options like defaults etc. Clear them! ActiveRecord::Base.connection.create_table :mixins do |t| t.column :pos, :integer, position_options t.column :active, :boolean, default: true t.column :parent_id, :integer t.column :parent_type, :string t.column :created_at, :datetime t.column :updated_at, :datetime t.column :state, :integer end mixins = [ Mixin, ListMixin, ListMixinSub1, ListMixinSub2, ListWithStringScopeMixin, ArrayScopeListMixin, ZeroBasedMixin, DefaultScopedMixin, DefaultScopedWhereMixin, TopAdditionMixin, NoAdditionMixin ] mixins << EnumArrayScopeListMixin if rails_4 ActiveRecord::Base.connection.schema_cache.clear! mixins.each do |klass| klass.reset_column_information end end def setup_db_with_default setup_db default: 0 end # Returns true if ActiveRecord is rails3,4 version def rails_3 defined?(ActiveRecord::VERSION) && ActiveRecord::VERSION::MAJOR >= 3 end def rails_4 defined?(ActiveRecord::VERSION) && ActiveRecord::VERSION::MAJOR >= 4 end def teardown_db ActiveRecord::Base.connection.tables.each do |table| ActiveRecord::Base.connection.drop_table(table) end end class Mixin < ActiveRecord::Base self.table_name = 'mixins' end class ListMixin < Mixin acts_as_list column: "pos", scope: :parent end class ListMixinSub1 < ListMixin end class ListMixinSub2 < ListMixin if rails_3 validates :pos, presence: true else validates_presence_of :pos end end class ListWithStringScopeMixin < Mixin acts_as_list column: "pos", scope: 'parent_id = #{parent_id}' end class ArrayScopeListMixin < Mixin acts_as_list column: "pos", scope: [:parent_id, :parent_type] end if rails_4 class EnumArrayScopeListMixin < Mixin STATE_VALUES = %w(active archived) enum state: STATE_VALUES acts_as_list column: "pos", scope: [:parent_id, :state] end end class ZeroBasedMixin < Mixin acts_as_list column: "pos", top_of_list: 0, scope: [:parent_id] end class DefaultScopedMixin < Mixin acts_as_list column: "pos" default_scope { order('pos ASC') } end class DefaultScopedWhereMixin < Mixin acts_as_list column: "pos" default_scope { order('pos ASC').where(active: true) } def self.for_active_false_tests unscoped.order('pos ASC').where(active: false) end end class TopAdditionMixin < Mixin acts_as_list column: "pos", add_new_at: :top, scope: :parent_id end class NoAdditionMixin < Mixin acts_as_list column: "pos", add_new_at: nil, scope: :parent_id end class TheAbstractClass < ActiveRecord::Base self.abstract_class = true self.table_name = 'mixins' end class TheAbstractSubclass < TheAbstractClass acts_as_list column: "pos", scope: :parent end class TheBaseClass < ActiveRecord::Base self.table_name = 'mixins' acts_as_list column: "pos", scope: :parent end class TheBaseSubclass < TheBaseClass end class ActsAsListTestCase < Minitest::Test # No default test required as this class is abstract. # Need for test/unit. undef_method :default_test if method_defined?(:default_test) def teardown teardown_db end end class ZeroBasedTest < ActsAsListTestCase include Shared::ZeroBased def setup setup_db super end end class ZeroBasedTestWithDefault < ActsAsListTestCase include Shared::ZeroBased def setup setup_db_with_default super end end class ListTest < ActsAsListTestCase include Shared::List def setup setup_db super end end class ListTestWithDefault < ActsAsListTestCase include Shared::List def setup setup_db_with_default super end end class ListSubTest < ActsAsListTestCase include Shared::ListSub def setup setup_db super end end class ListSubTestWithDefault < ActsAsListTestCase include Shared::ListSub def setup setup_db_with_default super end end class ArrayScopeListTest < ActsAsListTestCase include Shared::ArrayScopeList def setup setup_db super end end class ArrayScopeListTestWithDefault < ActsAsListTestCase include Shared::ArrayScopeList def setup setup_db_with_default super end end class DefaultScopedTest < ActsAsListTestCase def setup setup_db (1..4).each { |counter| DefaultScopedMixin.create!({pos: counter}) } end def test_insert new = DefaultScopedMixin.create assert_equal 5, new.pos assert !new.first? assert new.last? new = DefaultScopedMixin.create assert_equal 6, new.pos assert !new.first? assert new.last? new = DefaultScopedMixin.create assert_equal 7, new.pos assert !new.first? assert new.last? end def test_reordering assert_equal [1, 2, 3, 4], DefaultScopedMixin.all.map(&:id) DefaultScopedMixin.where(id: 2).first.move_lower assert_equal [1, 3, 2, 4], DefaultScopedMixin.all.map(&:id) DefaultScopedMixin.where(id: 2).first.move_higher assert_equal [1, 2, 3, 4], DefaultScopedMixin.all.map(&:id) DefaultScopedMixin.where(id: 1).first.move_to_bottom assert_equal [2, 3, 4, 1], DefaultScopedMixin.all.map(&:id) DefaultScopedMixin.where(id: 1).first.move_to_top assert_equal [1, 2, 3, 4], DefaultScopedMixin.all.map(&:id) DefaultScopedMixin.where(id: 2).first.move_to_bottom assert_equal [1, 3, 4, 2], DefaultScopedMixin.all.map(&:id) DefaultScopedMixin.where(id: 4).first.move_to_top assert_equal [4, 1, 3, 2], DefaultScopedMixin.all.map(&:id) end def test_insert_at new = DefaultScopedMixin.create assert_equal 5, new.pos new = DefaultScopedMixin.create assert_equal 6, new.pos new = DefaultScopedMixin.create assert_equal 7, new.pos new4 = DefaultScopedMixin.create assert_equal 8, new4.pos new4.insert_at(2) assert_equal 2, new4.pos new.reload assert_equal 8, new.pos new.insert_at(2) assert_equal 2, new.pos new4.reload assert_equal 3, new4.pos new5 = DefaultScopedMixin.create assert_equal 9, new5.pos new5.insert_at(1) assert_equal 1, new5.pos new4.reload assert_equal 4, new4.pos end def test_update_position assert_equal [1, 2, 3, 4], DefaultScopedMixin.all.map(&:id) DefaultScopedMixin.where(id: 2).first.set_list_position(4) assert_equal [1, 3, 4, 2], DefaultScopedMixin.all.map(&:id) DefaultScopedMixin.where(id: 2).first.set_list_position(2) assert_equal [1, 2, 3, 4], DefaultScopedMixin.all.map(&:id) DefaultScopedMixin.where(id: 1).first.set_list_position(4) assert_equal [2, 3, 4, 1], DefaultScopedMixin.all.map(&:id) DefaultScopedMixin.where(id: 1).first.set_list_position(1) assert_equal [1, 2, 3, 4], DefaultScopedMixin.all.map(&:id) end end class DefaultScopedWhereTest < ActsAsListTestCase def setup setup_db (1..4).each { |counter| DefaultScopedWhereMixin.create! pos: counter, active: false } end def test_insert new = DefaultScopedWhereMixin.create assert_equal 5, new.pos assert !new.first? assert new.last? new = DefaultScopedWhereMixin.create assert_equal 6, new.pos assert !new.first? assert new.last? new = DefaultScopedWhereMixin.create assert_equal 7, new.pos assert !new.first? assert new.last? end def test_reordering assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.for_active_false_tests.map(&:id) DefaultScopedWhereMixin.for_active_false_tests.where(id: 2).first.move_lower assert_equal [1, 3, 2, 4], DefaultScopedWhereMixin.for_active_false_tests.map(&:id) DefaultScopedWhereMixin.for_active_false_tests.where(id: 2).first.move_higher assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.for_active_false_tests.map(&:id) DefaultScopedWhereMixin.for_active_false_tests.where(id: 1).first.move_to_bottom assert_equal [2, 3, 4, 1], DefaultScopedWhereMixin.for_active_false_tests.map(&:id) DefaultScopedWhereMixin.for_active_false_tests.where(id: 1).first.move_to_top assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.for_active_false_tests.map(&:id) DefaultScopedWhereMixin.for_active_false_tests.where(id: 2).first.move_to_bottom assert_equal [1, 3, 4, 2], DefaultScopedWhereMixin.for_active_false_tests.map(&:id) DefaultScopedWhereMixin.for_active_false_tests.where(id: 4).first.move_to_top assert_equal [4, 1, 3, 2], DefaultScopedWhereMixin.for_active_false_tests.map(&:id) end def test_insert_at new = DefaultScopedWhereMixin.create assert_equal 5, new.pos new = DefaultScopedWhereMixin.create assert_equal 6, new.pos new = DefaultScopedWhereMixin.create assert_equal 7, new.pos new4 = DefaultScopedWhereMixin.create assert_equal 8, new4.pos new4.insert_at(2) assert_equal 2, new4.pos new.reload assert_equal 8, new.pos new.insert_at(2) assert_equal 2, new.pos new4.reload assert_equal 3, new4.pos new5 = DefaultScopedWhereMixin.create assert_equal 9, new5.pos new5.insert_at(1) assert_equal 1, new5.pos new4.reload assert_equal 4, new4.pos end def test_update_position assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.for_active_false_tests.map(&:id) DefaultScopedWhereMixin.for_active_false_tests.where(id: 2).first.set_list_position(4) assert_equal [1, 3, 4, 2], DefaultScopedWhereMixin.for_active_false_tests.map(&:id) DefaultScopedWhereMixin.for_active_false_tests.where(id: 2).first.set_list_position(2) assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.for_active_false_tests.map(&:id) DefaultScopedWhereMixin.for_active_false_tests.where(id: 1).first.set_list_position(4) assert_equal [2, 3, 4, 1], DefaultScopedWhereMixin.for_active_false_tests.map(&:id) DefaultScopedWhereMixin.for_active_false_tests.where(id: 1).first.set_list_position(1) assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.for_active_false_tests.map(&:id) end end class MultiDestroyTest < ActsAsListTestCase def setup setup_db end # example: # # class TodoList < ActiveRecord::Base # has_many :todo_items, order: "position" # accepts_nested_attributes_for :todo_items, allow_destroy: true # end # # class TodoItem < ActiveRecord::Base # belongs_to :todo_list # acts_as_list scope: :todo_list # end # # Assume that there are three items. # The user mark two items as deleted, click save button, form will be post: # # todo_list.todo_items_attributes = [ # {id: 1, _destroy: true}, # {id: 2, _destroy: true} # ] # # Save toto_list, the position of item #3 should eql 1. # def test_destroy new1 = DefaultScopedMixin.create assert_equal 1, new1.pos new2 = DefaultScopedMixin.create assert_equal 2, new2.pos new3 = DefaultScopedMixin.create assert_equal 3, new3.pos new1.destroy new2.destroy new3.reload assert_equal 1, new3.pos end end #class TopAdditionMixin < Mixin class TopAdditionTest < ActsAsListTestCase include Shared::TopAddition def setup setup_db super end end class TopAdditionTestWithDefault < ActsAsListTestCase include Shared::TopAddition def setup setup_db_with_default super end end class NoAdditionTest < ActsAsListTestCase include Shared::NoAddition def setup setup_db super end end class MultipleListsTest < ActsAsListTestCase def setup setup_db (1..4).each { |counter| ListMixin.create! :pos => counter, :parent_id => 1} (1..4).each { |counter| ListMixin.create! :pos => counter, :parent_id => 2} end def test_check_scope_order assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 1).order(:pos).map(&:id) assert_equal [5, 6, 7, 8], ListMixin.where(:parent_id => 2).order(:pos).map(&:id) ListMixin.find(4).update_attributes(:parent_id => 2, :pos => 2) assert_equal [1, 2, 3], ListMixin.where(:parent_id => 1).order(:pos).map(&:id) assert_equal [5, 4, 6, 7, 8], ListMixin.where(:parent_id => 2).order(:pos).map(&:id) end def test_check_scope_position assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 1).map(&:pos) assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 2).map(&:pos) ListMixin.find(4).update_attributes(:parent_id => 2, :pos => 2) assert_equal [1, 2, 3], ListMixin.where(:parent_id => 1).order(:pos).map(&:pos) assert_equal [1, 2, 3, 4, 5], ListMixin.where(:parent_id => 2).order(:pos).map(&:pos) end end if rails_4 class EnumArrayScopeListMixinTest < ActsAsListTestCase def setup setup_db EnumArrayScopeListMixin.create! :parent_id => 1, :state => EnumArrayScopeListMixin.states['active'] EnumArrayScopeListMixin.create! :parent_id => 1, :state => EnumArrayScopeListMixin.states['archived'] EnumArrayScopeListMixin.create! :parent_id => 2, :state => EnumArrayScopeListMixin.states["active"] EnumArrayScopeListMixin.create! :parent_id => 2, :state => EnumArrayScopeListMixin.states["archived"] end def test_positions assert_equal [1], EnumArrayScopeListMixin.where(:parent_id => 1, :state => EnumArrayScopeListMixin.states['active']).map(&:pos) assert_equal [1], EnumArrayScopeListMixin.where(:parent_id => 1, :state => EnumArrayScopeListMixin.states['archived']).map(&:pos) assert_equal [1], EnumArrayScopeListMixin.where(:parent_id => 2, :state => EnumArrayScopeListMixin.states['active']).map(&:pos) assert_equal [1], EnumArrayScopeListMixin.where(:parent_id => 2, :state => EnumArrayScopeListMixin.states['archived']).map(&:pos) end end end class MultipleListsArrayScopeTest < ActsAsListTestCase def setup setup_db (1..4).each { |counter| ArrayScopeListMixin.create! :pos => counter,:parent_id => 1, :parent_type => 'anything'} (1..4).each { |counter| ArrayScopeListMixin.create! :pos => counter,:parent_id => 2, :parent_type => 'something'} (1..4).each { |counter| ArrayScopeListMixin.create! :pos => counter,:parent_id => 3, :parent_type => 'anything'} end def test_order_after_all_scope_properties_are_changed assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order(:pos).map(&:id) assert_equal [5, 6, 7, 8], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').order(:pos).map(&:id) ArrayScopeListMixin.find(2).update_attributes(:parent_id => 2, :pos => 2,:parent_type => 'something') assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order(:pos).map(&:id) assert_equal [5, 2, 6, 7, 8], ArrayScopeListMixin.where(:parent_id => 2,:parent_type => 'something').order(:pos).map(&:id) end def test_position_after_all_scope_properties_are_changed assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').map(&:pos) assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').map(&:pos) ArrayScopeListMixin.find(4).update_attributes(:parent_id => 2, :pos => 2, :parent_type => 'something') assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order(:pos).map(&:pos) assert_equal [1, 2, 3, 4, 5], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').order(:pos).map(&:pos) end def test_order_after_one_scope_property_is_changed assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order(:pos).map(&:id) assert_equal [9, 10, 11, 12], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').order(:pos).map(&:id) ArrayScopeListMixin.find(2).update_attributes(:parent_id => 3, :pos => 2) assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order(:pos).map(&:id) assert_equal [9, 2, 10, 11, 12], ArrayScopeListMixin.where(:parent_id => 3,:parent_type => 'anything').order(:pos).map(&:id) end def test_position_after_one_scope_property_is_changed assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').map(&:pos) assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').map(&:pos) ArrayScopeListMixin.find(4).update_attributes(:parent_id => 3, :pos => 2) assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order(:pos).map(&:pos) assert_equal [1, 2, 3, 4, 5], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').order(:pos).map(&:pos) end def test_order_after_moving_to_empty_list assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order(:pos).map(&:id) assert_equal [], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').order(:pos).map(&:id) ArrayScopeListMixin.find(2).update_attributes(:parent_id => 4, :pos => 1) assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order(:pos).map(&:id) assert_equal [2], ArrayScopeListMixin.where(:parent_id => 4,:parent_type => 'anything').order(:pos).map(&:id) end def test_position_after_moving_to_empty_list assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').map(&:pos) assert_equal [], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').map(&:pos) ArrayScopeListMixin.find(2).update_attributes(:parent_id => 4, :pos => 1) assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order(:pos).map(&:pos) assert_equal [1], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').order(:pos).map(&:pos) end end acts-as-list-0.7.2/test/shared_list_sub.rb0000644000175000017500000001006212634606423017105 0ustar aleealeemodule Shared module ListSub def setup (1..4).each do |i| node = ((i % 2 == 1) ? ListMixinSub1 : ListMixinSub2).new parent_id: 5000 node.pos = i node.save! end end def test_reordering assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5000).order('pos').map(&:id) ListMixin.where(id: 2).first.move_lower assert_equal [1, 3, 2, 4], ListMixin.where(parent_id: 5000).order('pos').map(&:id) ListMixin.where(id: 2).first.move_higher assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5000).order('pos').map(&:id) ListMixin.where(id: 1).first.move_to_bottom assert_equal [2, 3, 4, 1], ListMixin.where(parent_id: 5000).order('pos').map(&:id) ListMixin.where(id: 1).first.move_to_top assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5000).order('pos').map(&:id) ListMixin.where(id: 2).first.move_to_bottom assert_equal [1, 3, 4, 2], ListMixin.where(parent_id: 5000).order('pos').map(&:id) ListMixin.where(id: 4).first.move_to_top assert_equal [4, 1, 3, 2], ListMixin.where(parent_id: 5000).order('pos').map(&:id) end def test_move_to_bottom_with_next_to_last_item assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5000).order('pos').map(&:id) ListMixin.where(id: 3).first.move_to_bottom assert_equal [1, 2, 4, 3], ListMixin.where(parent_id: 5000).order('pos').map(&:id) end def test_next_prev assert_equal ListMixin.where(id: 2).first, ListMixin.where(id: 1).first.lower_item assert_nil ListMixin.where(id: 1).first.higher_item assert_equal ListMixin.where(id: 3).first, ListMixin.where(id: 4).first.higher_item assert_nil ListMixin.where(id: 4).first.lower_item end def test_next_prev_groups li1 = ListMixin.where(id: 1).first li2 = ListMixin.where(id: 2).first li3 = ListMixin.where(id: 3).first li4 = ListMixin.where(id: 4).first assert_equal [li2, li3, li4], li1.lower_items assert_equal [li4], li3.lower_items assert_equal [li2, li3], li1.lower_items(2) assert_equal [], li4.lower_items assert_equal [li1, li2], li3.higher_items assert_equal [li1], li2.higher_items assert_equal [li2, li3], li4.higher_items(2) assert_equal [], li1.higher_items end def test_injection item = ListMixin.new("parent_id"=>1) assert_equal({ parent_id: 1 }, item.scope_condition) assert_equal "pos", item.position_column end def test_insert_at new = ListMixin.create("parent_id" => 20) assert_equal 1, new.pos new = ListMixinSub1.create("parent_id" => 20) assert_equal 2, new.pos new = ListMixinSub1.create("parent_id" => 20) assert_equal 3, new.pos new4 = ListMixin.create("parent_id" => 20) assert_equal 4, new4.pos new4.insert_at(3) assert_equal 3, new4.pos new.reload assert_equal 4, new.pos new.insert_at(2) assert_equal 2, new.pos new4.reload assert_equal 4, new4.pos new5 = ListMixinSub1.create("parent_id" => 20) assert_equal 5, new5.pos new5.insert_at(1) assert_equal 1, new5.pos new4.reload assert_equal 5, new4.pos end def test_delete_middle assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5000).order('pos').map(&:id) ListMixin.where(id: 2).first.destroy assert_equal [1, 3, 4], ListMixin.where(parent_id: 5000).order('pos').map(&:id) assert_equal 1, ListMixin.where(id: 1).first.pos assert_equal 2, ListMixin.where(id: 3).first.pos assert_equal 3, ListMixin.where(id: 4).first.pos ListMixin.where(id: 1).first.destroy assert_equal [3, 4], ListMixin.where(parent_id: 5000).order('pos').map(&:id) assert_equal 1, ListMixin.where(id: 3).first.pos assert_equal 2, ListMixin.where(id: 4).first.pos end def test_acts_as_list_class assert_equal TheBaseClass, TheBaseSubclass.new.acts_as_list_class assert_equal TheAbstractSubclass, TheAbstractSubclass.new.acts_as_list_class end end end acts-as-list-0.7.2/test/shared_zero_based.rb0000644000175000017500000000474112634606423017405 0ustar aleealeemodule Shared module ZeroBased def setup (1..4).each { |counter| ZeroBasedMixin.create! pos: counter, parent_id: 5 } end def test_insert new = ZeroBasedMixin.create(parent_id: 20) assert_equal 0, new.pos assert new.first? assert new.last? new = ZeroBasedMixin.create(parent_id: 20) assert_equal 1, new.pos assert !new.first? assert new.last? new = ZeroBasedMixin.create(parent_id: 20) assert_equal 2, new.pos assert !new.first? assert new.last? new = ZeroBasedMixin.create(parent_id: 0) assert_equal 0, new.pos assert new.first? assert new.last? new = ZeroBasedMixin.create(parent_id: 1, pos: -500) assert_equal 0, new.pos assert new.first? assert new.last? end def test_reordering assert_equal [1, 2, 3, 4], ZeroBasedMixin.where(parent_id: 5).order('pos').map(&:id) ListMixin.where(id: 2).first.move_lower assert_equal [1, 3, 2, 4], ZeroBasedMixin.where(parent_id: 5).order('pos').map(&:id) ListMixin.where(id: 2).first.move_higher assert_equal [1, 2, 3, 4], ZeroBasedMixin.where(parent_id: 5).order('pos').map(&:id) ListMixin.where(id: 1).first.move_to_bottom assert_equal [2, 3, 4, 1], ZeroBasedMixin.where(parent_id: 5).order('pos').map(&:id) ListMixin.where(id: 1).first.move_to_top assert_equal [1, 2, 3, 4], ZeroBasedMixin.where(parent_id: 5).order('pos').map(&:id) ListMixin.where(id: 2).first.move_to_bottom assert_equal [1, 3, 4, 2], ZeroBasedMixin.where(parent_id: 5).order('pos').map(&:id) ListMixin.where(id: 4).first.move_to_top assert_equal [4, 1, 3, 2], ZeroBasedMixin.where(parent_id: 5).order('pos').map(&:id) end def test_insert_at new = ZeroBasedMixin.create(parent_id: 20) assert_equal 0, new.pos new = ZeroBasedMixin.create(parent_id: 20) assert_equal 1, new.pos new = ZeroBasedMixin.create(parent_id: 20) assert_equal 2, new.pos new4 = ZeroBasedMixin.create(parent_id: 20) assert_equal 3, new4.pos new4.insert_at(2) assert_equal 2, new4.pos new.reload assert_equal 3, new.pos new.insert_at(2) assert_equal 2, new.pos new4.reload assert_equal 3, new4.pos new5 = ListMixin.create(parent_id: 20) assert_equal 4, new5.pos new5.insert_at(1) assert_equal 1, new5.pos new4.reload assert_equal 4, new4.pos end end end acts-as-list-0.7.2/test/shared.rb0000644000175000017500000000051612634606423015204 0ustar aleealee# Common shared behaviour. module Shared autoload :List, 'shared_list' autoload :ListSub, 'shared_list_sub' autoload :ZeroBased, 'shared_zero_based' autoload :ArrayScopeList, 'shared_array_scope_list' autoload :TopAddition, 'shared_top_addition' autoload :NoAddition, 'shared_no_addition' end acts-as-list-0.7.2/test/shared_list.rb0000644000175000017500000002015712634606423016242 0ustar aleealeemodule Shared module List def setup (1..4).each do |counter| node = ListMixin.new parent_id: 5 node.pos = counter node.save! end end def test_reordering assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) ListMixin.where(id: 2).first.move_lower assert_equal [1, 3, 2, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) ListMixin.where(id: 2).first.move_higher assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) ListMixin.where(id: 1).first.move_to_bottom assert_equal [2, 3, 4, 1], ListMixin.where(parent_id: 5).order('pos').map(&:id) ListMixin.where(id: 1).first.move_to_top assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) ListMixin.where(id: 2).first.move_to_bottom assert_equal [1, 3, 4, 2], ListMixin.where(parent_id: 5).order('pos').map(&:id) ListMixin.where(id: 4).first.move_to_top assert_equal [4, 1, 3, 2], ListMixin.where(parent_id: 5).order('pos').map(&:id) end def test_move_to_bottom_with_next_to_last_item assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) ListMixin.where(id: 3).first.move_to_bottom assert_equal [1, 2, 4, 3], ListMixin.where(parent_id: 5).order('pos').map(&:id) end def test_next_prev assert_equal ListMixin.where(id: 2).first, ListMixin.where(id: 1).first.lower_item assert_nil ListMixin.where(id: 1).first.higher_item assert_equal ListMixin.where(id: 3).first, ListMixin.where(id: 4).first.higher_item assert_nil ListMixin.where(id: 4).first.lower_item end def test_injection item = ListMixin.new(parent_id: 1) assert_equal({ parent_id: 1 }, item.scope_condition) assert_equal "pos", item.position_column end def test_insert new = ListMixin.create(parent_id: 20) assert_equal 1, new.pos assert new.first? assert new.last? new = ListMixin.create(parent_id: 20) assert_equal 2, new.pos assert !new.first? assert new.last? new = ListMixin.create(parent_id: 20) assert_equal 3, new.pos assert !new.first? assert new.last? new = ListMixin.create(parent_id: 0) assert_equal 1, new.pos assert new.first? assert new.last? end def test_insert_at new = ListMixin.create(parent_id: 20) assert_equal 1, new.pos new = ListMixin.create(parent_id: 20) assert_equal 2, new.pos new = ListMixin.create(parent_id: 20) assert_equal 3, new.pos new4 = ListMixin.create(parent_id: 20) assert_equal 4, new4.pos new4.insert_at(3) assert_equal 3, new4.pos new.reload assert_equal 4, new.pos new.insert_at(2) assert_equal 2, new.pos new4.reload assert_equal 4, new4.pos new5 = ListMixin.create(parent_id: 20) assert_equal 5, new5.pos new5.insert_at(1) assert_equal 1, new5.pos new4.reload assert_equal 5, new4.pos end def test_delete_middle assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) ListMixin.where(id: 2).first.destroy assert_equal [1, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) assert_equal 1, ListMixin.where(id: 1).first.pos assert_equal 2, ListMixin.where(id: 3).first.pos assert_equal 3, ListMixin.where(id: 4).first.pos ListMixin.where(id: 1).first.destroy assert_equal [3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) assert_equal 1, ListMixin.where(id: 3).first.pos assert_equal 2, ListMixin.where(id: 4).first.pos end def test_with_string_based_scope new = ListWithStringScopeMixin.create(parent_id: 500) assert_equal 1, new.pos assert new.first? assert new.last? end def test_nil_scope new1, new2, new3 = ListMixin.create, ListMixin.create, ListMixin.create new2.move_higher assert_equal [new2, new1, new3].map(&:id), ListMixin.where(parent_id: nil).order('pos').map(&:id) end def test_update_position_when_scope_changes assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) parent = ListMixin.create(parent_id: 6) ListMixin.where(id: 2).first.move_within_scope(6) assert_equal 2, ListMixin.where(id: 2).first.pos assert_equal [1, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) assert_equal 1, ListMixin.where(id: 1).first.pos assert_equal 2, ListMixin.where(id: 3).first.pos assert_equal 3, ListMixin.where(id: 4).first.pos ListMixin.where(id: 2).first.move_within_scope(5) assert_equal [1, 3, 4, 2], ListMixin.where(parent_id: 5).order('pos').map(&:id) end def test_remove_from_list_should_then_fail_in_list? assert_equal true, ListMixin.where(id: 1).first.in_list? ListMixin.where(id: 1).first.remove_from_list assert_equal false, ListMixin.where(id: 1).first.in_list? end def test_remove_from_list_should_set_position_to_nil assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) ListMixin.where(id: 2).first.remove_from_list assert_equal [2, 1, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) assert_equal 1, ListMixin.where(id: 1).first.pos assert_equal nil, ListMixin.where(id: 2).first.pos assert_equal 2, ListMixin.where(id: 3).first.pos assert_equal 3, ListMixin.where(id: 4).first.pos end def test_remove_before_destroy_does_not_shift_lower_items_twice assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) ListMixin.where(id: 2).first.remove_from_list ListMixin.where(id: 2).first.destroy assert_equal [1, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) assert_equal 1, ListMixin.where(id: 1).first.pos assert_equal 2, ListMixin.where(id: 3).first.pos assert_equal 3, ListMixin.where(id: 4).first.pos end def test_before_destroy_callbacks_do_not_update_position_to_nil_before_deleting_the_record assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) # We need to trigger all the before_destroy callbacks without actually # destroying the record so we can see the affect the callbacks have on # the record. # NOTE: Hotfix for rails3 ActiveRecord list = ListMixin.where(id: 2).first if list.respond_to?(:run_callbacks) # Refactored to work according to Rails3 ActiveRSupport Callbacks list.run_callbacks(:destroy) if rails_3 list.run_callbacks(:before_destroy) if !rails_3 else list.send(:callback, :before_destroy) end assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) assert_equal 1, ListMixin.where(id: 1).first.pos assert_equal 2, ListMixin.where(id: 2).first.pos assert_equal 2, ListMixin.where(id: 3).first.pos assert_equal 3, ListMixin.where(id: 4).first.pos end def test_before_create_callback_adds_to_bottom assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) new = ListMixin.create(parent_id: 5) assert_equal 5, new.pos assert !new.first? assert new.last? assert_equal [1, 2, 3, 4, 5], ListMixin.where(parent_id: 5).order('pos').map(&:id) end def test_before_create_callback_adds_to_given_position assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) new = ListMixin.new(parent_id: 5) new.pos = 1 new.save! assert_equal 1, new.pos assert new.first? assert !new.last? assert_equal [5, 1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) new = ListMixin.new(parent_id: 5) new.pos = 3 new.save! assert_equal 3, new.pos assert !new.first? assert !new.last? assert_equal [5, 1, 6, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id) end end end acts-as-list-0.7.2/test/shared_array_scope_list.rb0000644000175000017500000001514312634606423020630 0ustar aleealeemodule Shared module ArrayScopeList def setup (1..4).each { |counter| ArrayScopeListMixin.create! pos: counter, parent_id: 5, parent_type: 'ParentClass' } (1..4).each { |counter| ArrayScopeListMixin.create! pos: counter, parent_id: 6, parent_type: 'ParentClass' } end def test_reordering assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id) ArrayScopeListMixin.where(id: 2).first.move_lower assert_equal [1, 3, 2, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id) ArrayScopeListMixin.where(id: 2).first.move_higher assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id) ArrayScopeListMixin.where(id: 1).first.move_to_bottom assert_equal [2, 3, 4, 1], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id) ArrayScopeListMixin.where(id: 1).first.move_to_top assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id) ArrayScopeListMixin.where(id: 2).first.move_to_bottom assert_equal [1, 3, 4, 2], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id) ArrayScopeListMixin.where(id: 4).first.move_to_top assert_equal [4, 1, 3, 2], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id) ArrayScopeListMixin.where(id: 4).first.insert_at(4) assert_equal [1, 3, 2, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id) assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:pos) end def test_move_to_bottom_with_next_to_last_item assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id) ArrayScopeListMixin.where(id: 3).first.move_to_bottom assert_equal [1, 2, 4, 3], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id) end def test_next_prev assert_equal ArrayScopeListMixin.where(id: 2).first, ArrayScopeListMixin.where(id: 1).first.lower_item assert_nil ArrayScopeListMixin.where(id: 1).first.higher_item assert_equal ArrayScopeListMixin.where(id: 3).first, ArrayScopeListMixin.where(id: 4).first.higher_item assert_nil ArrayScopeListMixin.where(id: 4).first.lower_item end def test_injection item = ArrayScopeListMixin.new(parent_id: 1, parent_type: 'ParentClass') assert_equal "pos", item.position_column end def test_insert new = ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass') assert_equal 1, new.pos assert new.first? assert new.last? new = ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass') assert_equal 2, new.pos assert !new.first? assert new.last? new = ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass') assert_equal 3, new.pos assert !new.first? assert new.last? new = ArrayScopeListMixin.create(parent_id: 0, parent_type: 'ParentClass') assert_equal 1, new.pos assert new.first? assert new.last? end def test_insert_at new = ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass') assert_equal 1, new.pos new = ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass') assert_equal 2, new.pos new = ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass') assert_equal 3, new.pos new4 = ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass') assert_equal 4, new4.pos new4.insert_at(3) assert_equal 3, new4.pos new.reload assert_equal 4, new.pos new.insert_at(2) assert_equal 2, new.pos new4.reload assert_equal 4, new4.pos new5 = ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass') assert_equal 5, new5.pos new5.insert_at(1) assert_equal 1, new5.pos new4.reload assert_equal 5, new4.pos end def test_delete_middle assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id) ArrayScopeListMixin.where(id: 2).first.destroy assert_equal [1, 3, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id) assert_equal 1, ArrayScopeListMixin.where(id: 1).first.pos assert_equal 2, ArrayScopeListMixin.where(id: 3).first.pos assert_equal 3, ArrayScopeListMixin.where(id: 4).first.pos ArrayScopeListMixin.where(id: 1).first.destroy assert_equal [3, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id) assert_equal 1, ArrayScopeListMixin.where(id: 3).first.pos assert_equal 2, ArrayScopeListMixin.where(id: 4).first.pos end def test_remove_from_list_should_then_fail_in_list? assert_equal true, ArrayScopeListMixin.where(id: 1).first.in_list? ArrayScopeListMixin.where(id: 1).first.remove_from_list assert_equal false, ArrayScopeListMixin.where(id: 1).first.in_list? end def test_remove_from_list_should_set_position_to_nil assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id) ArrayScopeListMixin.where(id: 2).first.remove_from_list assert_equal [2, 1, 3, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id) assert_equal 1, ArrayScopeListMixin.where(id: 1).first.pos assert_equal nil, ArrayScopeListMixin.where(id: 2).first.pos assert_equal 2, ArrayScopeListMixin.where(id: 3).first.pos assert_equal 3, ArrayScopeListMixin.where(id: 4).first.pos end def test_remove_before_destroy_does_not_shift_lower_items_twice assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id) ArrayScopeListMixin.where(id: 2).first.remove_from_list ArrayScopeListMixin.where(id: 2).first.destroy assert_equal [1, 3, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id) assert_equal 1, ArrayScopeListMixin.where(id: 1).first.pos assert_equal 2, ArrayScopeListMixin.where(id: 3).first.pos assert_equal 3, ArrayScopeListMixin.where(id: 4).first.pos end end end acts-as-list-0.7.2/MIT-LICENSE0000644000175000017500000000205412634606423014065 0ustar aleealeeCopyright (c) 2007 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. acts-as-list-0.7.2/.travis.yml0000644000175000017500000000026312634606423014542 0ustar aleealeelanguage: ruby rvm: - 1.9.3 - 2.0.0 - 2.1.0 - jruby-19mode - rbx-2 gemfile: - gemfiles/rails_3_2.gemfile - gemfiles/rails_4_1.gemfile - gemfiles/rails_4_2.gemfile acts-as-list-0.7.2/README.md0000644000175000017500000001121312634606423013705 0ustar aleealee# ActsAsList ## Description This `acts_as` extension provides the capabilities for sorting and reordering a number of objects in a list. The class that has this specified needs to have a `position` column defined as an integer on the mapped database table. ## Installation In your Gemfile: gem 'acts_as_list' Or, from the command line: gem install acts_as_list ## Example At first, you need to add a `position` column to desired table: rails g migration AddPositionToTodoItem position:integer rake db:migrate After that you can use `acts_as_list` method in the model: ```ruby class TodoList < ActiveRecord::Base has_many :todo_items, -> { order(position: :asc) } end class TodoItem < ActiveRecord::Base belongs_to :todo_list acts_as_list scope: :todo_list end todo_list.first.move_to_bottom todo_list.last.move_higher ``` ## Instance Methods Added To ActiveRecord Models You'll have a number of methods added to each instance of the ActiveRecord model that to which `acts_as_list` is added. In `acts_as_list`, "higher" means further up the list (a lower `position`), and "lower" means further down the list (a higher `position`). That can be confusing, so it might make sense to add tests that validate that you're using the right method given your context. ### Methods That Change Position and Reorder List - `list_item.insert_at(2)` - `list_item.move_lower` will do nothing if the item is the lowest item - `list_item.move_higher` will do nothing if the item is the highest item - `list_item.move_to_bottom` - `list_item.move_to_top` - `list_item.remove_from_list` ### Methods That Change Position Without Reordering List - `list_item.increment_position` - `list_item.decrement_position` - `list_item.set_list_position(3)` ### Methods That Return Attributes of the Item's List Position - `list_item.first?` - `list_item.last?` - `list_item.in_list?` - `list_item.not_in_list?` - `list_item.default_position?` - `list_item.higher_item` - `list_item.higher_items` will return all the items above `list_item` in the list (ordered by the position, ascending) - `list_item.lower_item` - `list_item.lower_items` will return all the items below `list_item` in the list (ordered by the position, ascending) ## Notes If the `position` column has a default value, then there is a slight change in behavior, i.e if you have 4 items in the list, and you insert 1, with a default position 0, it would be pushed to the bottom of the list. Please look at the tests for this and some recent pull requests for discussions related to this. All `position` queries (select, update, etc.) inside gem methods are executed without the default scope (i.e. `Model.unscoped`), this will prevent nasty issues when the default scope is different from `acts_as_list` scope. The `position` column is set after validations are called, so you should not put a `presence` validation on the `position` column. If you need a scope by a non-association field you should pass an array, containing field name, to a scope: ```ruby class TodoItem < ActiveRecord::Base # `kind` is a plain text field (e.g. 'work', 'shopping', 'meeting'), not an association acts_as_list scope: [:kind] end ``` ## Versions All versions `0.1.5` onwards require Rails 3.0.x and higher. ## Build Status [![Build Status](https://secure.travis-ci.org/swanandp/acts_as_list.png)](https://secure.travis-ci.org/swanandp/acts_as_list) ## Workflow Status [![WIP Issues](https://badge.waffle.io/swanandp/acts_as_list.png)](http://waffle.io/swanandp/acts_as_list) ## Roadmap 1. Sort based feature 2. Rails 4 compatibility and bye bye Rails 2! Older versions would of course continue to work with Rails 2, but there won't be any support on those. ## Contributing to `acts_as_list` - Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet - Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it - Fork the project - Start a feature/bugfix branch - Commit and push until you are happy with your contribution - Make sure to add tests for it. This is important so I don't break it in a future version unintentionally. - Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it. - I would recommend using Rails 3.1.x and higher for testing the build before a pull request. The current test harness does not quite work with 3.0.x. The plugin itself works, but the issue lies with testing infrastructure. ## Copyright Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license acts-as-list-0.7.2/Gemfile0000644000175000017500000000056012634606423013724 0ustar aleealeesource "http://rubygems.org" gem "sqlite3", platforms: [:ruby] gem "activerecord-jdbcsqlite3-adapter", platforms: [:jruby] platforms :rbx do gem "rubysl", "~> 2.0" gem "rubinius-developer_tools" gem "rubysl-test-unit" end # Specify your gem"s dependencies in acts_as_list-rails3.gemspec gemspec gem "rake" gem "appraisal" group :test do gem "minitest" end acts-as-list-0.7.2/metadata.yml0000644000175000017500000000546712634606423014747 0ustar aleealee--- !ruby/object:Gem::Specification name: acts_as_list version: !ruby/object:Gem::Version version: 0.7.2 platform: ruby authors: - David Heinemeier Hansson - Swanand Pagnis - Quinn Chaffee autorequire: bindir: bin cert_chain: [] date: 2015-05-06 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: activerecord requirement: !ruby/object:Gem::Requirement requirements: - - ! '>=' - !ruby/object:Gem::Version version: '3.0' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ! '>=' - !ruby/object:Gem::Version version: '3.0' - !ruby/object:Gem::Dependency name: bundler requirement: !ruby/object:Gem::Requirement requirements: - - ! '>=' - !ruby/object:Gem::Version version: 1.0.0 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ! '>=' - !ruby/object:Gem::Version version: 1.0.0 description: This "acts_as" extension provides the capabilities for sorting and reordering a number of objects in a list. The class that has this specified needs to have a "position" column defined as an integer on the mapped database table. email: - swanand.pagnis@gmail.com executables: [] extensions: [] extra_rdoc_files: [] files: - .gemtest - .gitignore - .travis.yml - Appraisals - Gemfile - MIT-LICENSE - README.md - Rakefile - acts_as_list.gemspec - gemfiles/rails_3_2.gemfile - gemfiles/rails_3_2.gemfile.lock - gemfiles/rails_4_1.gemfile - gemfiles/rails_4_1.gemfile.lock - gemfiles/rails_4_2.gemfile - gemfiles/rails_4_2.gemfile.lock - init.rb - lib/acts_as_list.rb - lib/acts_as_list/active_record/acts/list.rb - lib/acts_as_list/version.rb - test/helper.rb - test/shared.rb - test/shared_array_scope_list.rb - test/shared_list.rb - test/shared_list_sub.rb - test/shared_no_addition.rb - test/shared_top_addition.rb - test/shared_zero_based.rb - test/test_list.rb homepage: http://github.com/swanandp/acts_as_list licenses: - MIT metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ! '>=' - !ruby/object:Gem::Version version: 1.9.2 required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: acts_as_list rubygems_version: 2.4.5 signing_key: specification_version: 4 summary: A gem adding sorting, reordering capabilities to an active_record model, allowing it to act as a list test_files: - test/helper.rb - test/shared.rb - test/shared_array_scope_list.rb - test/shared_list.rb - test/shared_list_sub.rb - test/shared_no_addition.rb - test/shared_top_addition.rb - test/shared_zero_based.rb - test/test_list.rb acts-as-list-0.7.2/Appraisals0000644000175000017500000000026212634606423014452 0ustar aleealeeappraise "rails-3-2" do gem "activerecord", "3.2.21" end appraise "rails-4-1" do gem "activerecord", "4.1.10" end appraise "rails-4-2" do gem "activerecord", "4.2.1" end