浏览 2670 次
|
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
|---|---|
| 作者 | 正文 |
|
时间:2007-06-27 关键字: ActionController pagination 源码
1,action_controller\pagination.rb:
module ActionController
module Pagination
def paginate(collection_id, options={})
Pagination.validate_options!(collection_id, options, true)
paginator_and_collection_for(collection_id, options)
end
def self.validate_options!(collection_id, options, in_action)
options.merge!(DEFAULT_OPTIONS) {|key, old, new| old}
valid_options = DEFAULT_OPTIONS.keys
valid_options << :actions unless in_action
unknown_option_keys = options.keys - valid_options
raise ActionController::ActionControllerError,
"Unknown options: #{unknown_option_keys.join(', ')}" unless
unknown_option_keys.empty?
options[:singular_name] ||= Inflector.singularize(collection_id.to_s)
options[:class_name] ||= Inflector.camelize(options[:singular_name])
end
def paginator_and_collection_for(collection_id, options)
klass = options[:class_name].constantize
page = params[options[:parameter]]
count = count_collection_for_pagination(klass, options)
paginator = Paginator.new(self, count, options[:per_page], page)
collection = find_collection_for_pagination(klass, options, paginator)
return paginator, collection
end
module ClassMethods
def paginate(collection_id, options={})
Pagination.validate_options!(collection_id, options, false)
module_eval do
before_filter :create_paginators_and_retrieve_collections
OPTIONS[self] ||= Hash.new
OPTIONS[self][collection_id] = options
end
end
end
def create_paginators_and_retrieve_collections
Pagination::OPTIONS[self.class].each do |collection_id, options|
next unless options[:actions].include? action_name if
options[:actions]
paginator, collection =
paginator_and_collection_for(collection_id, options)
paginator_name = "@#{options[:singular_name]}_pages"
self.instance_variable_set(paginator_name, paginator)
collection_name = "@#{collection_id.to_s}"
self.instance_variable_set(collection_name, collection)
end
end
def find_collection_for_pagination(model, options, paginator)
model.find(:all, :conditions => options[:conditions],
:order => options[:order_by] || options[:order],
:joins => options[:join] || options[:joins], :include => options[:include],
:select => options[:select], :limit => options[:per_page],
:offset => paginator.current.offset)
end
class Paginator
include Enumerable
def current_page=(page)
if page.is_a? Page
raise ArgumentError, 'Page/Paginator mismatch' unless
page.paginator == self
end
page = page.to_i
@current_page_number = has_page_number?(page) ? page : 1
end
def current_page
@current_page ||= self[@current_page_number]
end
alias current :current_page
def first_page
@first_page ||= self[1]
end
alias first :first_page
def last_page
@last_page ||= self[page_count]
end
alias last :last_page
def page_count
@page_count ||= @item_count.zero? ? 1 :
(q,r=@item_count.divmod(@items_per_page); r==0? q : q+1)
end
alias length :page_count
def has_page_number?(number)
number >= 1 and number <= page_count
end
def [](number)
@pages[number] ||= Page.new(self, number)
end
class Page
include Comparable
def initialize(paginator, number)
@paginator = paginator
@number = number.to_i
@number = 1 unless @paginator.has_page_number? @number
end
attr_reader :paginator, :number
alias to_i :number
def ==(page)
return false if page.nil?
@paginator == page.paginator and
@number == page.number
end
def <=>(page)
raise ArgumentError unless @paginator == page.paginator
@number <=> page.number
end
def previous
if first? then nil else @paginator[@number - 1] end
end
def next
if last? then nil else @paginator[@number + 1] end
end
def window(padding=2)
Window.new(self, padding)
end
end
class Window
def initialize(page, padding=2)
@paginator = page.paginator
@page = page
self.padding = padding
end
attr_reader :paginator, :page
def padding=(padding)
@padding = padding < 0 ? 0 : padding
@first = @paginator.has_page_number?(@page.number - @padding) ?
@paginator[@page.number - @padding] : @paginator.first
@last = @paginator.has_page_number?(@page.number + @padding) ?
@paginator[@page.number + @padding] : @paginator.last
end
attr_reader :padding, :first, :last
def pages
(@first.number..@last.number).to_a.collect! {|n| @paginator[n]}
end
alias to_a :pages
end
end
end
end
paginate方法的参数有: :singular_name -- the singular name to use, if it can't be inferred by singularizing the collection name :class_name -- the class name to use, if it can't be inferred by camelizing the singular name :per_page -- the maximum number of items to include in a single page. Defaults to 10 :conditions -- optional conditions passed to Model.find(:all, *params) and Model.count :order -- optional order parameter passed to Model.find(:all, *params) :order_by -- (deprecated, used :order) optional order parameter passed to Model.find(:all, *params) :joins -- optional joins parameter passed to Model.find(:all, *params) and Model.count :join -- (deprecated, used :joins or :include) optional join parameter passed to Model.find(:all, *params) and Model.count :include -- optional eager loading parameter passed to Model.find(:all, *params) and Model.count :select -- :select parameter passed to Model.find(:all, *params) :count -- parameter passed as :select option to Model.count(*params) 调用流程为:paginate -> paginator_and_collection_for -> return paginator, collection 其中paginator = Paginator.new,collection = find_collection_for_pagination -> model.find 2,action_view\helpers\pagination_helper.rb:
module ActionView
module Helpers
module PaginationHelper
def pagination_links(paginator, options={}, html_options={})
name = options[:name] || DEFAULT_OPTIONS[:name]
params = (options[:params] || DEFAULT_OPTIONS[:params]).clone
pagination_links_each(paginator, options) do |n|
params[name] = n
link_to(n.to_s, params, html_options)
end
end
def pagination_links_each(paginator, options)
options = DEFAULT_OPTIONS.merge(options)
link_to_current_page = options[:link_to_current_page]
always_show_anchors = options[:always_show_anchors]
current_page = paginator.current_page
window_pages = current_page.window(options[:window_size]).pages
return if window_pages.length <= 1 unless link_to_current_page
first, last = paginator.first, paginator.last
html = ''
if always_show_anchors and not (wp_first = window_pages[0]).first?
html << yield(first.number)
html << ' ... ' if wp_first.number - first.number > 1
html << ' '
end
window_pages.each do |page|
if current_page == page && !link_to_current_page
html << page.number.to_s
else
html << yield(page.number)
end
html << ' '
end
if always_show_anchors and not (wp_last = window_pages[-1]).last?
html << ' ... ' if last.number - wp_last.number > 1
html << yield(last.number)
end
html
end
end
end
end
pagination_links方法的参数有: :name -- the routing name for this paginator(defaults to +page+) :window_size -- the number of pages to show around the current page (defaults to +2+) :always_show_anchors -- whether or not the first and last pages should always be shown(defaults to +true+) :link_to_current_page -- whether or not the current page should be linked to (defaults to +false+) :params -- any additional routing parameters for page URLs 我们可以这样来使用 Controller: def list @post_pages, @posts = paginate(:posts, :per_page => 20, \:order => 'posts.created_at', :include => :user, :conditions => ['posts.topic_id = ?', params[:id]]) end View:
<%= link_to "Previous page", { :page => @post_pages.current.previous } if @post_pages.current.previous %>
<%= pagination_links @post_pages, :window_size => 10 %>
<%= link_to "Next page", { :page => @post_pages.current.next } if @post_pages.current.next %>
注意,在Rails 2.0中Pagination将成为一个插件 ActionController到此为止,下一步看ActionView源码 声明:JavaEye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
|
|
| 返回顶楼 | |
|
时间:2007-08-10
def paginate(collection_id, options={})
Pagination.validate_options!(collection_id, options, false) module_eval do before_filter :create_paginators_and_ retrieve_collections OPTIONS[self] ||= Hash.new OPTIONS[self][collection_id] = options end end 我不明白,怎么在方法体中可以有module_eval,我的理解是只在module或class中才能调用,望解惑! |
|
| 返回顶楼 | |
|
时间:2007-08-11
module_eval或class_eval都是Module类的方法,只能在Module或Class中作为static方法被调用,比如Thing是一个模块或类,Thing.module_eval或Thing.class_eval是可以的而且可以放在任何地方(放在Thing内部或外面都可以,因为是static方法),当这两个方法在Thing内部调用时就不用写Thing.module_eval或Thing.class_eval了,直接调用module_eval和class_eval即可,因为Thing内部对自己可见嘛。
而对应的instance_eval只能被对象实例调用,比如t = Thing.new t.instance_eval是可以的,但是只是对t这个实例本身起作用,对t2 = Thing.new,t2不会受t.instance_eval的影响 |
|
| 返回顶楼 | |
|
时间:2007-08-11
你说的就是在模块或类内部中调用可以省略接收者(模块或类),但我不明白什么是static方法,我的理解是如果在方法定义中调用module_eval(class_eval)且省略了模块或类名,那么其接收者是self才对,看我做的测试:
class Demo def test Demo.class_eval do def talk puts "Hello, World!" end end end end d = Demo.new d.test => nil d.talk => “Hello, World!“ 这是显示的加了类名,但当省略了类名执行结果如下 d = Demo.new d.test =>undefined method "calss_eval" for #<Demo:*****> 也就是说并没有默认的将类作为class_eval的接收者 |
|
| 返回顶楼 | |
|
时间:2007-08-11
当class_eval定义在一个方法中时,必须调用这个方法才能触发class_eval,这是显然的
|
|
| 返回顶楼 | |
|
时间:2007-08-11
可问题是这个方法已不合法了,它所调用的class_eval是无法找到的,得显示加上类名才行啊,你试试吧
|
|
| 返回顶楼 | |
|
时间:2007-08-11
这里的问题比较特殊,因为用了Object类的extend方法
先看这几个: person = Person.new person.extend Demographics
module Demographics
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def years_since(date)
(Date.today - date).to_i / 365
end
end
end
module Mod
def hello
"Hello from Mod.\n"
end
end
class Klass
def hello
"Hello from Klass.\n"
end
end
k = Klass.new
k.hello #=> "Hello from Klass.\n"
k.extend(Mod) #=> #<Klass:0x401b3bc8>
k.hello #=> "Hello from Mod.\n"
最后看这个:
module B
def test
module_eval do
def talk
puts "Hello"
end
end
end
end
class A
end
A.extend B
A.test
A.new.talk
|
|
| 返回顶楼 | |
|
时间:2007-08-12
原来我忽略了included方法,当执行obj.extend(mod)方法时,mod中的实例方法就成为obj的单例方法,当obj为一个类时,mod中相应的方法就变成obj的类方法,那么在类方法中调用module_eval(class_eval)自然可以省略接收者。
我是个初学者,正尝试着学习rails源码,其中有很多在你看来不是问题的问题,真诚地谢谢你不厌其烦的帮助! |
|
| 返回顶楼 | |




