0
+require 'will_paginate/core_ext'
0
+ # A mixin for ActiveRecord::Base. Provides +per_page+ class method
0
+ # and makes +paginate+ finders possible with some method_missing magic.
0
+ # Find out more in WillPaginate::Finder::ClassMethods
0
+ def self.included(base)
0
+ base.extend ClassMethods
0
+ alias_method_chain :method_missing, :paginate
0
+ define_method(:per_page) { 30 } unless respond_to?(:per_page)
0
+ # = Paginating finders for ActiveRecord models
0
+ # WillPaginate doesn't really add extra methods to your ActiveRecord models
0
+ # (except +per_page+ unless it's already available). It simply intercepts
0
+ # the calls to paginating finders such as +paginate+, +paginate_by_user_id+
0
+ # (and so on) and translates them to ordinary finders: +find+,
0
+ # +find_by_user_id+, etc. It does so with some +method_missing+ magic, but
0
+ # you don't need to care for that. You simply use paginating finders same
0
+ # way you used ordinary ones. You only need to specify what page do you want:
0
+ # @posts = Post.paginate :page => params[:page]
0
+ # In paginating finders, "all" is implicit. No sense in paginating a single
0
+ # Post.paginate => Post.find :all
0
+ # Post.paginate_all_by_something => Post.find_all_by_something
0
+ # Post.paginate_by_something => Post.find_all_by_something
0
+ # Don't forget to pass the +page+ parameter! Without it, paginating finders
0
+ # will raise an error.
0
+ # == Options for paginating finders
0
+ # * <tt>:page</tt> -- REQUIRED, but defaults to 1 if false or nil
0
+ # * <tt>:per_page</tt> -- defaults to <tt>CurrentModel.per_page</tt> (which is 30 if not overridden)
0
+ # * <tt>:total_entries</tt> -- use only if you manually count total entries
0
+ # * <tt>:count</tt> -- additional options that are passed on to +count+
0
+ # This methods wraps +find_by_sql+ by simply adding LIMIT and OFFSET to your SQL string
0
+ # based on the params otherwise used by paginating finds: +page+ and +per_page+.
0
+ # @developers = Developer.paginate_by_sql ['select * from developers where salary > ?', 80000],
0
+ # :page => params[:page], :per_page => 3
0
+ # A query for counting rows will automatically be generated if you don't
0
+ # supply <tt>:total_entries</tt>. If you experience problems with this
0
+ # generated SQL, you might want to perform the count manually in your
0
+ def paginate_by_sql(sql, options)
0
+ WillPaginate::Collection.create(*wp_parse_options!(options)) do |pager|
0
+ query = sanitize_sql(sql)
0
+ options.update :offset => pager.offset, :limit => pager.per_page
0
+ original_query = query.dup
0
+ add_limit! query, options
0
+ pager.replace find_by_sql(query)
0
+ unless pager.total_entries
0
+ count_query = original_query.sub /\bORDER\s+BY\s+[\w`,\s]+$/mi, ''
0
+ count_query = "SELECT COUNT(*) FROM (#{count_query}) AS count_table"
0
+ # perform the count query
0
+ pager.total_entries = count_by_sql(count_query)
0
+ def respond_to?(method, include_priv = false)
0
+ when :paginate, :paginate_by_sql
0
+ super(method.to_s.sub(/^paginate/, 'find'), include_priv)
0
+ def method_missing_with_paginate(method, *args, &block)
0
+ # did somebody tried to paginate? if not, let them be
0
+ unless method.to_s.index('paginate') == 0
0
+ return method_missing_without_paginate(method, *args, &block)
0
+ page, per_page, total_entries = wp_parse_options!(options)
0
+ # an array of IDs may have been given:
0
+ total_entries ||= (Array === args.first and args.first.size)
0
+ # paginate finders are really just find_* with limit and offset
0
+ finder = method.to_s.sub /^paginate/, 'find'
0
+ args.unshift(:all) if args.empty?
0
+ elsif finder.index('find_by_') == 0
0
+ finder.sub! /^find/, 'find_all'
0
+ WillPaginate::Collection.create(page, per_page, total_entries) do |pager|
0
+ args << options.except(:count).merge(:offset => pager.offset, :limit => pager.per_page)
0
+ pager.replace send(finder, *args)
0
+ # magic counting for user convenience:
0
+ pager.total_entries = wp_count!(options, args, finder) unless pager.total_entries
0
+ def wp_count!(options, args, finder)
0
+ excludees = [:count, :order, :limit, :offset]
0
+ unless options[:select] and options[:select] =~ /^\s*DISTINCT/i
0
+ excludees << :select # only exclude the select param if it doesn't begin with DISTINCT
0
+ # count expects (almost) the same options as find
0
+ count_options = options.except *excludees
0
+ # merge the hash found in :count
0
+ # this allows you to specify :select, :order, or anything else just for the count query
0
+ count_options.update(options.delete(:count) || {}) if options.key? :count
0
+ # we may have to scope ...
0
+ counter = Proc.new { count(count_options) }
0
+ # we may be in a model or an association proxy!
0
+ klass = (@owner and @reflection) ? @reflection.klass : self
0
+ count = if finder =~ /^find_/ and klass.respond_to?(scoper = finder.sub(/^find_/, 'with_'))
0
+ # scope_out adds a 'with_finder' method which acts like with_scope, if it's present
0
+ # then execute the count with the scoping provided by the with_finder
0
+ send(scoper, &counter)
0
+ elsif conditions = wp_extract_finder_conditions(finder, args)
0
+ # extracted the conditions from calls like "paginate_by_foo_and_bar"
0
+ with_scope(:find => { :conditions => conditions }, &counter)
0
+ count.respond_to?(:length) ? count.length : count
0
+ def wp_parse_options!(options)
0
+ raise ArgumentError, 'hash parameters expected' unless options.respond_to? :symbolize_keys!
0
+ options.symbolize_keys!
0
+ raise ArgumentError, ':page parameter required' unless options.key? :page
0
+ if options[:count] and options[:total_entries]
0
+ raise ArgumentError, ':count and :total_entries are mutually exclusive parameters'
0
+ page = options.delete(:page) || 1
0
+ per_page = options.delete(:per_page) || self.per_page
0
+ total = options.delete(:total_entries)
0
+ [page, per_page, total]
0
+ # thanks to active record for making us duplicate this code
0
+ def wp_extract_finder_conditions(finder, arguments)
0
+ return unless match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match(finder.to_s)
0
+ attribute_names = extract_attribute_names_from_match(match)
0
+ unless all_attributes_exists?(attribute_names)
0
+ raise "I can't make sense of `#{finder}`. Try doing the count manually"
0
+ construct_attributes_from_arguments(attribute_names, arguments)