rughetto-merb_paginate 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,64 @@
1
+ ########################################
2
+ # There have been significant changes. #
3
+ # Read below before upgrading. #
4
+ ########################################
5
+
6
+ merb_paginate
7
+ =============
8
+
9
+ Pagination that relies on the awesomeness that is the will_paginate gem.
10
+ http://errtheblog.com/posts/56-im-paginating-again
11
+
12
+ Lots of credit goes to those dudes. It's a fantastic plugin, it just is too
13
+ rails centric to use with merb out of the box. A lot of things had to be
14
+ changed to not rely on the magicness of rails (which is why it's not a fork
15
+ of the project).
16
+
17
+ This merb plugin is very incomplete and has no specs (Hint: contribute!).
18
+
19
+ Install
20
+ =======
21
+
22
+ git clone git://github.com/myobie/merb_paginate.git
23
+ cd merb_paginate
24
+ sudo rake install
25
+
26
+ Example App
27
+ ===========
28
+
29
+ An example app is now included in the example folder. It comes with a sqlite3
30
+ database that has some posts already in it. I am not sure if the db is cross-
31
+ platform, let me know.
32
+
33
+ Usage
34
+ =====
35
+
36
+ Using this plugin is like so in init.rb:
37
+
38
+ Merb::BootLoader.after_app_loads do
39
+ dependency 'merb_paginate'
40
+ end
41
+
42
+ Right now, DataMapper and ActiveRecord work (Sequel might work, let me know).
43
+ I hope to have a fully speced paginate finder for each orm evenutally.
44
+ (Hint: contribute!)
45
+
46
+ Using the view helper is like so:
47
+
48
+ <%= merb_paginate @posts %>
49
+
50
+ If you need to generate a more complex url you will need to do:
51
+
52
+ <%= merb_paginate @comments, :named_route => :post_comments %>
53
+
54
+ It is almost exactly like will_paginate, except not as smart about the current collection.
55
+
56
+ There are also:
57
+
58
+ <% paginated_section @posts do %>
59
+ ...
60
+ <% end %>
61
+
62
+ and:
63
+
64
+ <%= page_entries_info @posts %>
data/Rakefile ADDED
@@ -0,0 +1,72 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/rdoctask'
4
+ require 'spec/rake/spectask'
5
+
6
+ PLUGIN = "merb_paginate"
7
+ NAME = "merb_paginate"
8
+ GEM_VERSION = "0.0.4"
9
+ AUTHOR = "Nathan Herald"
10
+ EMAIL = "nathan@myobie.com"
11
+ HOMEPAGE = "http://github.com/myobie/merb_paginate"
12
+ SUMMARY = "A pagination library for Merb that uses will_paginate internally"
13
+
14
+ windows = (PLATFORM =~ /win32|cygwin/)
15
+
16
+ SUDO = windows ? "" : "sudo"
17
+
18
+ spec = Gem::Specification.new do |s|
19
+ s.name = NAME
20
+ s.version = GEM_VERSION
21
+ s.platform = Gem::Platform::RUBY
22
+ s.has_rdoc = true
23
+ s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
24
+ s.summary = SUMMARY
25
+ s.description = s.summary
26
+ s.author = AUTHOR
27
+ s.email = EMAIL
28
+ s.homepage = HOMEPAGE
29
+ s.add_dependency("merb-core", ">=0.9")
30
+ s.add_dependency("will_paginate", ">=2.2.0")
31
+ s.require_path = 'lib'
32
+ s.autorequire = PLUGIN
33
+ s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,specs}/**/*")
34
+ end
35
+
36
+
37
+ Rake::GemPackageTask.new(spec) do |pkg|
38
+ pkg.gem_spec = spec
39
+ end
40
+
41
+ desc "Run :package and install resulting .gem"
42
+ task :install => [:package] do
43
+ sh %{#{SUDO} gem install pkg/#{NAME}-#{GEM_VERSION} --no-rdoc --no-ri}
44
+ end
45
+
46
+ Rake::RDocTask.new do |rdoc|
47
+ files = ['README', 'LICENSE',
48
+ 'lib/**/*.rb']
49
+ rdoc.rdoc_files.add(files)
50
+ rdoc.main = 'README'
51
+ rdoc.title = 'Merb Helper Docs'
52
+ rdoc.rdoc_dir = 'doc/rdoc'
53
+ rdoc.options << '--line-numbers' << '--inline-source'
54
+ end
55
+
56
+
57
+ Spec::Rake::SpecTask.new do |t|
58
+ t.warning = true
59
+ t.spec_opts = ["--format", "specdoc", "--colour"]
60
+ t.spec_files = Dir['spec/**/*_spec.rb'].sort
61
+ end
62
+
63
+ desc "Run all specs and generate an rcov report"
64
+ Spec::Rake::SpecTask.new('rcov') do |t|
65
+ t.spec_files = FileList['spec/**/*_spec.rb']
66
+ t.spec_opts = ["--format", "specdoc", "--colour"]
67
+ t.rcov = true
68
+ t.rcov_dir = 'coverage'
69
+ t.rcov_opts = ['--exclude', 'gems', '--exclude', 'spec']
70
+ end
71
+
72
+
data/TODO ADDED
@@ -0,0 +1,5 @@
1
+ TODO:
2
+ * Add specs!
3
+ * Complete dm_finder!
4
+ * Add finders for other orms
5
+ * Clean up the code
@@ -0,0 +1,23 @@
1
+ require 'will_paginate/collection'
2
+ require 'merb_paginate/finders'
3
+ require 'merb_paginate/view_helpers'
4
+
5
+ if defined?(Merb::Plugins)
6
+ Merb::BootLoader.after_app_loads do
7
+ # Load in the ORM Helpers as needed
8
+ if Object.const_defined? "DataMapper"
9
+ require 'merb_paginate/finders/datamapper'
10
+ DataMapper::Resource.class_eval { include MerbPaginate::Finders::Datamapper }
11
+ end
12
+
13
+ if Object.const_defined? "Sequel"
14
+ require 'merb_paginate/finders/sequel'
15
+ SequelModel::Base.class_eval { include MerbPaginate::Finders::Sequel }
16
+ end
17
+
18
+ if Object.const_defined? "ActiveRecord"
19
+ require 'merb_paginate/finders/activerecord'
20
+ ActiveRecord::Base.class_eval { include MerbPaginate::Finders::Activerecord }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,92 @@
1
+ require 'set'
2
+ require 'will_paginate/array'
3
+
4
+ # copied from rails
5
+ def mattr_reader(*syms)
6
+ syms.each do |sym|
7
+ next if sym.is_a?(Hash)
8
+ class_eval("unless defined? @@\#{sym}\n@@\#{sym} = nil\nend\n\ndef self.\#{sym}\n@@\#{sym}\nend\n\ndef \#{sym}\n@@\#{sym}\nend\n", __FILE__, __LINE__)
9
+ end
10
+ end
11
+
12
+ def returning(value)
13
+ yield(value)
14
+ value
15
+ end
16
+
17
+ # copied from will_paginate
18
+ unless Hash.instance_methods.include? 'except'
19
+ Hash.class_eval do
20
+ # Returns a new hash without the given keys.
21
+ def except(*keys)
22
+ rejected = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
23
+ reject { |key,| rejected.include?(key) }
24
+ end
25
+
26
+ # Replaces the hash without only the given keys.
27
+ def except!(*keys)
28
+ replace(except(*keys))
29
+ end
30
+ end
31
+ end
32
+
33
+ unless Hash.instance_methods.include? 'reverse_merge'
34
+ Hash.class_eval do
35
+ def reverse_merge(other_hash)
36
+ other_hash.merge(self)
37
+ end
38
+ end
39
+ end
40
+
41
+ unless Hash.instance_methods.include? 'slice'
42
+ Hash.class_eval do
43
+ # Returns a new hash with only the given keys.
44
+ def slice(*keys)
45
+ allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
46
+ reject { |key,| !allowed.include?(key) }
47
+ end
48
+
49
+ # Replaces the hash with only the given keys.
50
+ def slice!(*keys)
51
+ replace(slice(*keys))
52
+ end
53
+ end
54
+ end
55
+
56
+ unless Hash.instance_methods.include? 'rec_merge!'
57
+ Hash.class_eval do
58
+ # Same as Hash#merge!, but recursively merges sub-hashes
59
+ # (stolen from Haml)
60
+ def rec_merge!(other)
61
+ other.each do |key, other_value|
62
+ value = self[key]
63
+ if value.is_a?(Hash) and other_value.is_a?(Hash)
64
+ value.rec_merge! other_value
65
+ else
66
+ self[key] = other_value
67
+ end
68
+ end
69
+ self
70
+ end
71
+ end
72
+ end
73
+
74
+ class String
75
+
76
+ def underscore
77
+ self.gsub(/::/, '/').
78
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
79
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
80
+ tr("-", "_").
81
+ downcase
82
+ end
83
+
84
+ def constantize
85
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ self
86
+ raise NameError, "#{self.inspect} is not a valid constant name!"
87
+ end
88
+
89
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
90
+ end
91
+
92
+ end
@@ -0,0 +1 @@
1
+ require 'merb_paginate/core_ext'
@@ -0,0 +1,184 @@
1
+ require 'merb_paginate/finders/generic'
2
+
3
+ module MerbPaginate
4
+ module Finders
5
+
6
+ module Activerecord
7
+ def self.included(base)
8
+ base.extend ClassMethods
9
+ class << base
10
+ alias_method_chain :method_missing, :paginate
11
+ # alias_method_chain :find_every, :paginate
12
+ define_method(:per_page) { 30 } unless respond_to?(:per_page)
13
+ end
14
+ end
15
+
16
+ # = Paginating finders for ActiveRecord models
17
+ #
18
+ # WillPaginate adds +paginate+ and +per_page+ methods to ActiveRecord::Base
19
+ # class methods and associations. It also hooks into +method_missing+ to
20
+ # intercept pagination calls to dynamic finders such as
21
+ # +paginate_by_user_id+ and translate them to ordinary finders
22
+ # (+find_all_by_user_id+ in this case).
23
+ #
24
+ # In short, paginating finders are equivalent to ActiveRecord finders; the
25
+ # only difference is that we start with "paginate" instead of "find" and
26
+ # that <tt>:page</tt> is required parameter:
27
+ #
28
+ # @posts = Post.paginate :all, :page => params[:page], :order => 'created_at DESC'
29
+ #
30
+ # In paginating finders, "all" is implicit. There is no sense in paginating
31
+ # a single record, right? So, you can drop the <tt>:all</tt> argument:
32
+ #
33
+ # Post.paginate(...) => Post.find :all
34
+ # Post.paginate_all_by_something => Post.find_all_by_something
35
+ # Post.paginate_by_something => Post.find_all_by_something
36
+ #
37
+ # == The importance of the <tt>:order</tt> parameter
38
+ #
39
+ # In ActiveRecord finders, <tt>:order</tt> parameter specifies columns for the
40
+ # <tt>ORDER BY</tt> clause in SQL. It is important to have it, since pagination only makes
41
+ # sense with ordered sets. Without the <tt>ORDER BY</tt> clause, databases aren't required
42
+ # to do consistent ordering when performing <tt>SELECT</tt> queries; this is especially true
43
+ # for PostgreSQL.
44
+ #
45
+ module ClassMethods
46
+ include MerbPaginate::Finders::GenericOrmMethods # include the things that are shared
47
+
48
+ # This is the main paginating finder.
49
+ #
50
+ # == Special parameters for paginating finders
51
+ # * <tt>:page</tt> -- REQUIRED, but defaults to 1 if false or nil
52
+ # * <tt>:per_page</tt> -- defaults to <tt>CurrentModel.per_page</tt> (which is 30 if not overridden)
53
+ # * <tt>:total_entries</tt> -- use only if you manually count total entries
54
+ # * <tt>:count</tt> -- additional options that are passed on to +count+
55
+ # * <tt>:finder</tt> -- name of the ActiveRecord finder used (default: "find")
56
+ #
57
+ # All other options (+conditions+, +order+, ...) are forwarded to +find+
58
+ # and +count+ calls.
59
+ def paginate(*args, &block)
60
+ options = args.pop
61
+ page, per_page, total_entries = wp_parse_options(options)
62
+ finder = (options[:finder] || 'find').to_s
63
+
64
+ if finder == 'find'
65
+ # an array of IDs may have been given:
66
+ total_entries ||= (Array === args.first and args.first.size)
67
+ # :all is implicit
68
+ args.unshift(:all) if args.empty?
69
+ end
70
+
71
+ WillPaginate::Collection.create(page, per_page, total_entries) do |pager|
72
+ count_options = options.except :page, :per_page, :total_entries, :finder
73
+ find_options = count_options.except(:count).update(:offset => pager.offset, :limit => pager.per_page)
74
+
75
+ args << find_options
76
+ # @options_from_last_find = nil
77
+ pager.replace send(finder, *args, &block)
78
+
79
+ # magic counting for user convenience:
80
+ pager.total_entries = wp_count(count_options, args, finder) unless pager.total_entries
81
+ end
82
+ end
83
+
84
+ # Wraps +find_by_sql+ by simply adding LIMIT and OFFSET to your SQL string
85
+ # based on the params otherwise used by paginating finds: +page+ and
86
+ # +per_page+.
87
+ #
88
+ # Example:
89
+ #
90
+ # @developers = Developer.paginate_by_sql ['select * from developers where salary > ?', 80000],
91
+ # :page => params[:page], :per_page => 3
92
+ #
93
+ # A query for counting rows will automatically be generated if you don't
94
+ # supply <tt>:total_entries</tt>. If you experience problems with this
95
+ # generated SQL, you might want to perform the count manually in your
96
+ # application.
97
+ #
98
+ def paginate_by_sql(sql, options)
99
+ WillPaginate::Collection.create(*wp_parse_options(options)) do |pager|
100
+ query = sanitize_sql(sql)
101
+ original_query = query.dup
102
+ # add limit, offset
103
+ add_limit! query, :offset => pager.offset, :limit => pager.per_page
104
+ # perfom the find
105
+ pager.replace find_by_sql(query)
106
+
107
+ unless pager.total_entries
108
+ count_query = original_query.sub /\bORDER\s+BY\s+[\w`,\s]+$/mi, ''
109
+ count_query = "SELECT COUNT(*) FROM (#{count_query}) AS count_table"
110
+ # perform the count query
111
+ pager.total_entries = count_by_sql(count_query)
112
+ end
113
+ end
114
+ end
115
+
116
+ # def respond_to?(method, include_priv = false) #:nodoc:
117
+ # case method.to_sym
118
+ # when :paginate, :paginate_by_sql
119
+ # true
120
+ # else
121
+ # super(method.to_s.sub(/^paginate/, 'find'), include_priv)
122
+ # end
123
+ # end
124
+
125
+ protected
126
+
127
+ def method_missing_with_paginate(method, *args, &block) #:nodoc:
128
+ #did somebody tried to paginate? if not, let them be
129
+ unless method.to_s.index('paginate') == 0
130
+ return method_missing_without_paginate(method, *args, &block)
131
+ end
132
+
133
+ # paginate finders are really just find_* with limit and offset
134
+ finder = method.to_s.sub('paginate', 'find')
135
+ finder.sub!('find', 'find_all') if finder.index('find_by_') == 0
136
+
137
+ options = args.pop
138
+ raise ArgumentError, 'parameter hash expected' unless options.respond_to? :symbolize_keys
139
+ options = options.dup
140
+ options[:finder] = finder
141
+ args << options
142
+
143
+ paginate(*args, &block)
144
+ end
145
+
146
+ # Does the not-so-trivial job of finding out the total number of entries
147
+ # in the database. It relies on the ActiveRecord +count+ method.
148
+ def wp_count(options, args, finder)
149
+ excludees = [:count, :order, :limit, :offset, :readonly]
150
+ unless options[:select] and options[:select] =~ /^\s*DISTINCT\b/i
151
+ excludees << :select # only exclude the select param if it doesn't begin with DISTINCT
152
+ end
153
+ # count expects (almost) the same options as find
154
+ count_options = options.except *excludees
155
+
156
+ # merge the hash found in :count
157
+ # this allows you to specify :select, :order, or anything else just for the count query
158
+ count_options.update options[:count] if options[:count]
159
+
160
+ # we may have to scope ...
161
+ counter = Proc.new { count(count_options) }
162
+
163
+ # we may be in a model or an association proxy!
164
+ klass = (@owner and @reflection) ? @reflection.klass : self
165
+
166
+ count = if finder.index('find_') == 0 and klass.respond_to?(scoper = finder.sub('find', 'with'))
167
+ # scope_out adds a 'with_finder' method which acts like with_scope, if it's present
168
+ # then execute the count with the scoping provided by the with_finder
169
+ send(scoper, &counter)
170
+ elsif match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match(finder)
171
+ # extract conditions from calls like "paginate_by_foo_and_bar"
172
+ attribute_names = extract_attribute_names_from_match(match)
173
+ conditions = construct_attributes_from_arguments(attribute_names, args)
174
+ with_scope(:find => { :conditions => conditions }, &counter)
175
+ else
176
+ counter.call
177
+ end
178
+
179
+ count.respond_to?(:length) ? count.length : count
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,138 @@
1
+ require 'merb_paginate/finders/generic'
2
+
3
+ module MerbPaginate
4
+ module Finders
5
+ module Datamapper
6
+
7
+ def self.included(base)
8
+ base.extend ClassMethods
9
+ class << base
10
+ define_method(:per_page) { 30 } unless respond_to?(:per_page)
11
+ end
12
+ end
13
+
14
+ module ClassMethods
15
+ include MerbPaginate::Finders::GenericOrmMethods # include the things that are shared
16
+
17
+ define_method(:per_page) { 30 } unless respond_to?(:per_page)
18
+
19
+ # This is the main paginating finder.
20
+ #
21
+ # == Special parameters for paginating finders
22
+ # * <tt>:page</tt> -- REQUIRED, but defaults to 1 if false or nil
23
+ # * <tt>:per_page</tt> -- defaults to <tt>CurrentModel.per_page</tt> (which is 30 if not overridden)
24
+ # * <tt>:total_entries</tt> -- use only if you manually count total entries
25
+ # * <tt>:count</tt> -- additional options that are passed on to +count+
26
+ #
27
+ # All other options (+conditions+, +order+, ...) are forwarded to +all+
28
+ # and +count+ calls.
29
+ def paginate(options = {})
30
+ page, per_page, total_entries = wp_parse_options(options)
31
+
32
+ WillPaginate::Collection.create(page, per_page, total_entries) do |pager|
33
+ count_options = options.except :page, :per_page, :total_entries
34
+ find_options = count_options.except(:count).update(:offset => pager.offset, :limit => pager.per_page)
35
+
36
+ pager.replace all(find_options)
37
+
38
+ # magic counting for user convenience:
39
+ pager.total_entries = wp_count(count_options) unless pager.total_entries
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+ end
47
+ end
48
+
49
+ # module MerbPaginate
50
+ # module Finders
51
+ #
52
+ # module Datamapper
53
+ # def self.included(base)
54
+ # base.extend ClassMethods
55
+ # class << base
56
+ # define_method(:per_page) { 30 } unless respond_to?(:per_page)
57
+ # end
58
+ # end
59
+ #
60
+ # module ClassMethods
61
+ # include MerbPaginate::Finders::GenericOrmMethods # include the things that are shared
62
+ #
63
+ # # This is the main paginating finder.
64
+ # #
65
+ # # == Special parameters for paginating finders
66
+ # # * <tt>:page</tt> -- REQUIRED, but defaults to 1 if false or nil
67
+ # # * <tt>:per_page</tt> -- defaults to <tt>CurrentModel.per_page</tt> (which is 30 if not overridden)
68
+ # # * <tt>:total_entries</tt> -- use only if you manually count total entries
69
+ # # * <tt>:count</tt> -- additional options that are passed on to +count+
70
+ # #
71
+ # # All other options (+conditions+, +order+, ...) are forwarded to +all+
72
+ # # and +count+ calls.
73
+ # def paginate(options = {})
74
+ # page, per_page, total_entries = wp_parse_options(options)
75
+ #
76
+ # WillPaginate::Collection.create(page, per_page, total_entries) do |pager|
77
+ # count_options = options.except :page, :per_page, :total_entries
78
+ # find_options = count_options.except(:count).update(:offset => pager.offset, :limit => pager.per_page)
79
+ #
80
+ # pager.replace all(find_options)
81
+ #
82
+ # # magic counting for user convenience:
83
+ # pager.total_entries = wp_count(count_options) unless pager.total_entries
84
+ # end
85
+ # end
86
+ #
87
+ # # Wraps +find_by_sql+ by simply adding LIMIT and OFFSET to your SQL string
88
+ # # based on the params otherwise used by paginating finds: +page+ and
89
+ # # +per_page+.
90
+ # #
91
+ # # Example:
92
+ # #
93
+ # # @developers = Developer.paginate_by_sql ['select * from developers where salary > ?', 80000],
94
+ # # :page => params[:page], :per_page => 3
95
+ # #
96
+ # # A query for counting rows will automatically be generated if you don't
97
+ # # supply <tt>:total_entries</tt>. If you experience problems with this
98
+ # # generated SQL, you might want to perform the count manually in your
99
+ # # application.
100
+ # #
101
+ # # def paginate_by_sql(sql, options)
102
+ # # WillPaginate::Collection.create(*wp_parse_options(options)) do |pager|
103
+ # # query = sanitize_sql(sql)
104
+ # # original_query = query.dup
105
+ # # # add limit, offset
106
+ # # add_limit! query, :offset => pager.offset, :limit => pager.per_page
107
+ # # # perfom the find
108
+ # # pager.replace find_by_sql(query)
109
+ # #
110
+ # # unless pager.total_entries
111
+ # # count_query = original_query.sub /\bORDER\s+BY\s+[\w`,\s]+$/mi, ''
112
+ # # count_query = "SELECT COUNT(*) FROM (#{count_query}) AS count_table"
113
+ # # # perform the count query
114
+ # # pager.total_entries = count_by_sql(count_query)
115
+ # # end
116
+ # # end
117
+ # # end
118
+ #
119
+ # # ^^^^^
120
+ # # FIXME: Too busy to do this right now, someone help me out
121
+ #
122
+ # # def respond_to?(method, include_priv = false) #:nodoc:
123
+ # # case method.to_sym
124
+ # # when :paginate, :paginate_by_sql
125
+ # # true
126
+ # # else
127
+ # # super(method, include_priv)
128
+ # # end
129
+ # # end
130
+ #
131
+ # # ^^^^^
132
+ # # FIXME: Probly don't need this?
133
+ #
134
+ # end
135
+ # end
136
+ #
137
+ # end
138
+ # end
@@ -0,0 +1,50 @@
1
+ module MerbPaginate
2
+ module Finders
3
+
4
+ # A mixin for ORMs. Provides +per_page+ class method
5
+ module GenericOrmMethods
6
+ protected
7
+
8
+ # Count it up!
9
+ def wp_count(options)
10
+ excludees = [:count, :order, :limit, :offset, :readonly]
11
+
12
+ # unless options[:select] and options[:select] =~ /^\s*DISTINCT\b/i
13
+ # excludees << :select # only exclude the select param if it doesn't begin with DISTINCT
14
+ # end
15
+
16
+ # ^^^^^^
17
+ # FIXME: Don't need select right now and I'm not even sure how it works in datamapper to be honest
18
+
19
+ # count expects the same options as find
20
+ count_options = options.except *excludees
21
+
22
+ # merge the hash found in :count
23
+ # this allows you to specify :select, :order, or anything else just for the count query
24
+ count_options.update options[:count] if options[:count]
25
+
26
+ # !IMPORTANT!: this assumes all ORMs will have a count method
27
+ # if this assumtion turns out to be false, I will move this into the specific ORM finders
28
+ counter = count_options.empty? ? count : count(count_options) # don't pass in nothing (helps datamapper 0.2.5 and maybe others)
29
+ counter.respond_to?(:length) ? counter.length : counter # send back the length of the resulting count (either the length of the array or the number returned)
30
+ end
31
+
32
+ def wp_parse_options(options) #:nodoc:
33
+ raise ArgumentError, 'parameter hash expected' unless options.is_a? Hash
34
+ options = options.to_mash # FIXME: where is this defined? I don't want to use anything that is not part of core_ext.rb
35
+ raise ArgumentError, ':page parameter required' unless options.key? :page # TODO: it's set as a default below, why is it needed here?
36
+
37
+ if options[:count] and options[:total_entries]
38
+ raise ArgumentError, ':count and :total_entries are mutually exclusive'
39
+ end
40
+
41
+ page = options[:page] || 1
42
+ per_page = options[:per_page] || self.per_page
43
+ total = options[:total_entries]
44
+ [page, per_page, total]
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,38 @@
1
+ # someone please show me some love :(
2
+
3
+ require 'merb_paginate/finders/generic'
4
+
5
+ module MerbPaginate
6
+ module Finders
7
+
8
+ module Datamapper
9
+ def self.included(base)
10
+ base.extend ClassMethods
11
+ class << base
12
+ define_method(:per_page) { 30 } unless respond_to?(:per_page)
13
+ end
14
+ end
15
+
16
+ module ClassMethods
17
+ include MerbPaginate::Finders::GenericOrmMethods # include the things that are shared
18
+
19
+ def paginate(options = {})
20
+ Merb.logger.info(" $$$ Sequel support in merb_paginate has not been tested at all. Please let me know if it works.")
21
+ page, per_page, total_entries = wp_parse_options(options)
22
+
23
+ WillPaginate::Collection.create(page, per_page, total_entries) do |pager|
24
+ count_options = options.except :page, :per_page, :total_entries
25
+ find_options = count_options.except(:count).update(:offset => pager.offset, :limit => pager.per_page)
26
+
27
+ pager.replace all(find_options)
28
+
29
+ # magic counting for user convenience:
30
+ pager.total_entries = wp_count(count_options) unless pager.total_entries
31
+ end
32
+ end
33
+
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,278 @@
1
+ # This file is very merb specific, but it only includes the ViewHelpers module if merb is already loaded
2
+
3
+ require 'merb_paginate/core_ext'
4
+
5
+ module MerbPaginate
6
+ # = MerbPaginate view helpers
7
+ #
8
+ # Currently there is only one view helper: +merb_paginate+. It renders the
9
+ # pagination links for the given collection. The helper itself is lightweight
10
+ # and serves only as a wrapper around link renderer instantiation; the
11
+ # renderer then does all the hard work of generating the HTML.
12
+ #
13
+ # == Global options for helpers
14
+ #
15
+ # Options for pagination helpers are optional and get their default values from the
16
+ # MerbPaginate::ViewHelpers.pagination_options hash. You can write to this hash to
17
+ # override default options on the global level:
18
+ #
19
+ # MerbPaginate::ViewHelpers.pagination_options[:prev_label] = 'Previous page'
20
+ #
21
+ # By putting this into your init.rb you can easily translate link texts to previous
22
+ # and next pages, as well as override some other defaults to your liking.
23
+ module ViewHelpers
24
+ # default options that can be overridden on the global level
25
+ @@pagination_options = {
26
+ :class => 'pagination',
27
+ :prev_label => '&laquo; Previous',
28
+ :next_label => 'Next &raquo;',
29
+ :inner_window => 4, # links around the current page
30
+ :outer_window => 1, # links around beginning and end
31
+ :separator => ' ', # single space is friendly to spiders and non-graphic browsers
32
+ :param_name => :page,
33
+ :params => nil,
34
+ :renderer => 'MerbPaginate::LinkRenderer',
35
+ :page_links => true,
36
+ :container => true,
37
+ :namespace => nil
38
+ }
39
+
40
+ # there is not mattr_accessor here and it's not a big deal to just make these two getter/setter methods for now
41
+
42
+ def self.pagination_options
43
+ @@pagination_options
44
+ end
45
+
46
+ def self.pagination_options=(options)
47
+ @@pagination_options = options
48
+ end
49
+
50
+ # Renders Digg/Flickr-style pagination for a MerbPaginate::Collection
51
+ # object. Nil is returned if there is only one page in total; no point in
52
+ # rendering the pagination in that case...
53
+ #
54
+ # ==== Options
55
+ # * <tt>:class</tt> -- CSS class name for the generated DIV (default: "pagination")
56
+ # * <tt>:prev_label</tt> -- default: "« Previous"
57
+ # * <tt>:next_label</tt> -- default: "Next »"
58
+ # * <tt>:inner_window</tt> -- how many links are shown around the current page (default: 4)
59
+ # * <tt>:outer_window</tt> -- how many links are around the first and the last page (default: 1)
60
+ # * <tt>:separator</tt> -- string separator for page HTML elements (default: single space)
61
+ # * <tt>:param_name</tt> -- parameter name for page number in URLs (default: <tt>:page</tt>)
62
+ # * <tt>:named_route</tt> -- optional named route used to generate the pagination url
63
+ # * <tt>:params</tt> -- additional parameters when generating pagination links
64
+ # (eg. <tt>:controller => "foo", :action => nil</tt>)
65
+ # * <tt>:renderer</tt> -- class name of the link renderer (default: WillPaginate::LinkRenderer)
66
+ # * <tt>:page_links</tt> -- when false, only previous/next links are rendered (default: true)
67
+ # * <tt>:container</tt> -- toggles rendering of the DIV container for pagination links, set to
68
+ # false only when you are rendering your own pagination markup (default: true)
69
+ # * <tt>:id</tt> -- HTML ID for the container (default: nil). Pass +true+ to have the ID automatically
70
+ # generated from the class name of objects in collection: for example, paginating
71
+ # ArticleComment models would yield an ID of "article_comments_pagination".
72
+ #
73
+ # All options beside listed ones are passed as HTML attributes to the container
74
+ # element for pagination links (the DIV). For example:
75
+ #
76
+ # <%= merb_paginate @posts, :id => 'wp_posts' %>
77
+ #
78
+ # ... will result in:
79
+ #
80
+ # <div class="pagination" id="wp_posts"> ... </div>
81
+ #
82
+ # There is not magic controller inference anynmore. Pass in the variable lazy.
83
+ #
84
+ def merb_paginate(collection, options = {}) # collection is required now! Booya!
85
+ # early exit if there is nothing to render
86
+ return nil unless collection.total_pages > 1
87
+
88
+ options = options.to_mash.reverse_merge MerbPaginate::ViewHelpers.pagination_options.to_mash
89
+ # create the renderer instance
90
+ renderer_class = options[:renderer].to_s.constantize
91
+ renderer = renderer_class.new collection, options, self
92
+ # render HTML for pagination
93
+ renderer.to_html
94
+ end
95
+
96
+ # Wrapper for rendering pagination links at both top and bottom of a block
97
+ # of content.
98
+ #
99
+ # <% paginated_section @posts do %>
100
+ # <ol id="posts">
101
+ # <% for post in @posts %>
102
+ # <li> ... </li>
103
+ # <% end %>
104
+ # </ol>
105
+ # <% end %>
106
+ #
107
+ # will result in:
108
+ #
109
+ # <div class="pagination"> ... </div>
110
+ # <ol id="posts">
111
+ # ...
112
+ # </ol>
113
+ # <div class="pagination"> ... </div>
114
+ #
115
+ # Arguments are passed to a <tt>will_paginate</tt> call, so the same options
116
+ # apply. Don't use the <tt>:id</tt> option; otherwise you'll finish with two
117
+ # blocks of pagination links sharing the same ID (which is invalid HTML).
118
+ def paginated_section(*args, &block)
119
+ pagination = merb_paginate(*args).to_s
120
+ content = pagination + capture(&block) + pagination
121
+ #concat content, block.binding
122
+ content
123
+ end
124
+
125
+ # Renders a helpful message with numbers of displayed vs. total entries.
126
+ # You can use this as a blueprint for your own, similar helpers.
127
+ #
128
+ # <%= page_entries_info @posts %>
129
+ # #-> Displaying entries 6 - 10 of 26 in total
130
+ def page_entries_info(collection)
131
+ if collection.total_pages < 2
132
+ case collection.size
133
+ when 0; 'No entries found'
134
+ when 1; 'Displaying <b>1</b> entry'
135
+ else; "Displaying <b>all #{collection.size}</b> entries"
136
+ end
137
+ else
138
+ %{Displaying entries <b>%d&nbsp;-&nbsp;%d</b> of <b>%d</b> in total} % [
139
+ collection.offset + 1,
140
+ collection.offset + collection.length,
141
+ collection.total_entries
142
+ ]
143
+ end
144
+ end
145
+
146
+ end
147
+
148
+ # Copied mostly from will_paginate, but changed to work with the merb url helpers
149
+ class LinkRenderer
150
+
151
+ def initialize(collection, options, template)
152
+ @collection = collection
153
+ @options = options
154
+ @template = template
155
+ end
156
+
157
+ def to_html
158
+ links = @options[:page_links] ? windowed_links : []
159
+ # previous/next buttons
160
+ links.unshift page_link_or_span(@collection.previous_page, 'disabled', @options[:prev_label])
161
+ links.push page_link_or_span(@collection.next_page, 'disabled', @options[:next_label])
162
+
163
+ html = links.join(@options[:separator])
164
+ @options[:container] ? "<div#{html_attributes_string}>#{html}</div>" : html
165
+ end
166
+
167
+ def html_attributes
168
+ return @html_attributes if @html_attributes
169
+ @html_attributes = @options.except *(MerbPaginate::ViewHelpers.pagination_options.keys - [:class])
170
+ # pagination of Post models will have the ID of "posts_pagination"
171
+ if @options[:container] and @options[:id] === true
172
+ @html_attributes[:id] = @collection.first.class.name.underscore.pluralize + '_pagination'
173
+ end
174
+ @html_attributes
175
+ end
176
+
177
+ def html_attributes_string
178
+ string = ""
179
+ if html_attributes
180
+ html_attributes.each do |key, value|
181
+ string << " #{key}='#{value}'"
182
+ end
183
+ end
184
+ string
185
+ end
186
+
187
+ protected
188
+
189
+ def gap_marker; '...'; end
190
+
191
+ def windowed_links
192
+ prev = nil
193
+
194
+ visible_page_numbers.inject [] do |links, n|
195
+ # detect gaps:
196
+ links << gap_marker if prev and n > prev + 1
197
+ links << page_link_or_span(n)
198
+ prev = n
199
+ links
200
+ end
201
+ end
202
+
203
+ def visible_page_numbers
204
+ inner_window, outer_window = @options[:inner_window].to_i, @options[:outer_window].to_i
205
+ window_from = current_page - inner_window
206
+ window_to = current_page + inner_window
207
+
208
+ # adjust lower or upper limit if other is out of bounds
209
+ if window_to > total_pages
210
+ window_from -= window_to - total_pages
211
+ window_to = total_pages
212
+ elsif window_from < 1
213
+ window_to += 1 - window_from
214
+ window_from = 1
215
+ end
216
+
217
+ visible = (1..total_pages).to_a
218
+ left_gap = (2 + outer_window)...window_from
219
+ right_gap = (window_to + 1)...(total_pages - outer_window)
220
+ visible -= left_gap.to_a if left_gap.last - left_gap.first > 1
221
+ visible -= right_gap.to_a if right_gap.last - right_gap.first > 1
222
+
223
+ visible
224
+ end
225
+
226
+ def page_link_or_span(page, span_class = 'current', text = nil)
227
+ text ||= page.to_s
228
+ if page and page != current_page
229
+ "<a href=\"#{url_options_string(page)}\">#{text}</a>"
230
+ else
231
+ "<span class=\"#{span_class}\">#{text}</span>"
232
+ end
233
+ end
234
+
235
+ # TODO: I am not sure if I like the way this is working. It will always do /posts/index?page=1 for page one when it could do /
236
+ # Would be nice if it was smarter
237
+ def url_options(page)
238
+ options = { param_name => page }
239
+
240
+ # page links should preserve GET parameters
241
+ options = @template.request.params.merge(options).except('action', 'controller', 'format', *Array(@options[:except])) if @template.request.method == :get
242
+ options.rec_merge!(@options[:params]) if @options[:params]
243
+ return options
244
+ end
245
+
246
+ def url_options_string(page)
247
+ if @options[:named_route]
248
+ @template.url(@options[:named_route], url_options(page))
249
+ else
250
+ @template.url(url_options(page))
251
+ end
252
+ end
253
+
254
+ private
255
+
256
+ def current_page
257
+ @collection.current_page
258
+ end
259
+
260
+ def total_pages
261
+ @collection.total_pages
262
+ end
263
+
264
+ def param_name
265
+ @param_name ||= @options[:param_name].to_sym
266
+ end
267
+
268
+ def params
269
+ @params ||= @template.params.to_mash
270
+ end
271
+ end
272
+ end
273
+
274
+ if Object.const_defined? "Merb"
275
+ Merb::Controller.class_eval do
276
+ include MerbPaginate::ViewHelpers
277
+ end
278
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rughetto-merb_paginate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - Nathan Herald
8
+ autorequire: merb_paginate
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-13 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: merb-core
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0.9"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: will_paginate
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.2.0
34
+ version:
35
+ description: A pagination library for Merb that uses will_paginate internally
36
+ email: nathan@myobie.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - README
43
+ - LICENSE
44
+ - TODO
45
+ files:
46
+ - LICENSE
47
+ - README
48
+ - Rakefile
49
+ - TODO
50
+ - lib/merb_paginate
51
+ - lib/merb_paginate/core_ext.rb
52
+ - lib/merb_paginate/finders
53
+ - lib/merb_paginate/finders/activerecord.rb
54
+ - lib/merb_paginate/finders/datamapper.rb
55
+ - lib/merb_paginate/finders/generic.rb
56
+ - lib/merb_paginate/finders/sequel.rb
57
+ - lib/merb_paginate/finders.rb
58
+ - lib/merb_paginate/view_helpers.rb
59
+ - lib/merb_paginate.rb
60
+ has_rdoc: true
61
+ homepage: http://github.com/myobie/merb_paginate
62
+ post_install_message:
63
+ rdoc_options: []
64
+
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ version:
79
+ requirements: []
80
+
81
+ rubyforge_project:
82
+ rubygems_version: 1.2.0
83
+ signing_key:
84
+ specification_version: 2
85
+ summary: A pagination library for Merb that uses will_paginate internally
86
+ test_files: []
87
+