doozer 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +57 -0
- data/Rakefile +62 -0
- data/VERSION +1 -0
- data/bin/doozer +6 -0
- data/doozer.gemspec +156 -0
- data/lib/doozer.rb +35 -0
- data/lib/doozer/active_support/array.rb +14 -0
- data/lib/doozer/active_support/class.rb +221 -0
- data/lib/doozer/active_support/date_time.rb +23 -0
- data/lib/doozer/active_support/object.rb +43 -0
- data/lib/doozer/active_support/time.rb +32 -0
- data/lib/doozer/app.rb +294 -0
- data/lib/doozer/configs.rb +146 -0
- data/lib/doozer/controller.rb +340 -0
- data/lib/doozer/exceptions.rb +12 -0
- data/lib/doozer/extend.rb +10 -0
- data/lib/doozer/initializer.rb +104 -0
- data/lib/doozer/lib.rb +32 -0
- data/lib/doozer/logger.rb +12 -0
- data/lib/doozer/orm/active_record.rb +28 -0
- data/lib/doozer/orm/data_mapper.rb +22 -0
- data/lib/doozer/orm/sequel.rb +21 -0
- data/lib/doozer/partial.rb +99 -0
- data/lib/doozer/plugins/paginate/init.rb +2 -0
- data/lib/doozer/plugins/paginate/lib/paginate.rb +32 -0
- data/lib/doozer/plugins/paginate/lib/paginate/collection.rb +60 -0
- data/lib/doozer/plugins/paginate/lib/paginate/finder.rb +116 -0
- data/lib/doozer/plugins/paginate/lib/paginate/view_helpers.rb +37 -0
- data/lib/doozer/rackup/server.ru +35 -0
- data/lib/doozer/rackup/test.rb +20 -0
- data/lib/doozer/redirect.rb +12 -0
- data/lib/doozer/route.rb +292 -0
- data/lib/doozer/scripts/cluster.rb +126 -0
- data/lib/doozer/scripts/console.rb +2 -0
- data/lib/doozer/scripts/migrate.rb +108 -0
- data/lib/doozer/scripts/task.rb +60 -0
- data/lib/doozer/scripts/test.rb +23 -0
- data/lib/doozer/version.rb +8 -0
- data/lib/doozer/view_helpers.rb +251 -0
- data/lib/doozer/watcher.rb +369 -0
- data/lib/generator/generator.rb +548 -0
- data/templates/skeleton/Rakefile +3 -0
- data/templates/skeleton/app/controllers/application_controller.rb +2 -0
- data/templates/skeleton/app/controllers/index_controller.rb +7 -0
- data/templates/skeleton/app/helpers/application_helper.rb +17 -0
- data/templates/skeleton/app/views/global/_header.html.erb +7 -0
- data/templates/skeleton/app/views/global/_navigation.html.erb +6 -0
- data/templates/skeleton/app/views/index/index.html.erb +108 -0
- data/templates/skeleton/app/views/layouts/default.html.erb +23 -0
- data/templates/skeleton/config/app.yml +31 -0
- data/templates/skeleton/config/boot.rb +17 -0
- data/templates/skeleton/config/database.yml +25 -0
- data/templates/skeleton/config/environment.rb +11 -0
- data/templates/skeleton/config/rack.rb +30 -0
- data/templates/skeleton/config/routes.rb +69 -0
- data/templates/skeleton/script/cluster +4 -0
- data/templates/skeleton/script/console +15 -0
- data/templates/skeleton/script/migrate +4 -0
- data/templates/skeleton/script/task +4 -0
- data/templates/skeleton/script/test +4 -0
- data/templates/skeleton/static/404.html +16 -0
- data/templates/skeleton/static/500.html +16 -0
- data/templates/skeleton/static/css/style.css +32 -0
- data/templates/skeleton/static/favicon.ico +0 -0
- data/templates/skeleton/static/js/application.js +1 -0
- data/templates/skeleton/static/js/jquery-1.3.min.js +19 -0
- data/templates/skeleton/static/robots.txt +5 -0
- data/templates/skeleton/test/fixtures/setup.rb +6 -0
- data/templates/skeleton/test/setup.rb +33 -0
- data/test/doozer_test.rb +7 -0
- data/test/project/Rakefile +3 -0
- data/test/project/app/controllers/application_controller.rb +2 -0
- data/test/project/app/controllers/index_controller.rb +7 -0
- data/test/project/app/helpers/application_helper.rb +17 -0
- data/test/project/app/views/global/_header.html.erb +7 -0
- data/test/project/app/views/global/_navigation.html.erb +6 -0
- data/test/project/app/views/index/index.html.erb +108 -0
- data/test/project/app/views/layouts/default.html.erb +23 -0
- data/test/project/config/app.yml +31 -0
- data/test/project/config/boot.rb +17 -0
- data/test/project/config/database.yml +25 -0
- data/test/project/config/environment.rb +11 -0
- data/test/project/config/rack.rb +30 -0
- data/test/project/config/routes.rb +72 -0
- data/test/project/script/cluster +4 -0
- data/test/project/script/console +15 -0
- data/test/project/script/migrate +4 -0
- data/test/project/script/task +4 -0
- data/test/project/script/test +4 -0
- data/test/project/static/404.html +16 -0
- data/test/project/static/500.html +16 -0
- data/test/project/static/css/style.css +32 -0
- data/test/project/static/favicon.ico +0 -0
- data/test/project/static/js/application.js +1 -0
- data/test/project/static/js/jquery-1.3.min.js +19 -0
- data/test/project/static/robots.txt +5 -0
- data/test/project/test/fixtures/setup.rb +6 -0
- data/test/project/test/setup.rb +33 -0
- data/test/routing_test.rb +66 -0
- data/test/test_helper.rb +26 -0
- metadata +169 -0
data/lib/doozer/lib.rb
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Doozer
|
|
2
|
+
class Lib
|
|
3
|
+
|
|
4
|
+
#Return a ClassName as string from an underscored string.
|
|
5
|
+
# example: input "example_class" > "ExampleClass"
|
|
6
|
+
def self.classify(klass)
|
|
7
|
+
if klass.index('_')
|
|
8
|
+
klass = klass.split('_')
|
|
9
|
+
parts = []
|
|
10
|
+
klass = klass.each { | part |
|
|
11
|
+
parts.push(part.capitalize)
|
|
12
|
+
}
|
|
13
|
+
klass = parts.join('')
|
|
14
|
+
else
|
|
15
|
+
klass.capitalize!
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
#Returns a one-level deep folder/file structure and preservers underscores for filename.
|
|
20
|
+
# example: input "folder_some_file_name" > "folder/some_file_name"
|
|
21
|
+
def self.pathify_first(s)
|
|
22
|
+
if s.index('_')
|
|
23
|
+
parts = s.split('_')
|
|
24
|
+
folder = parts[0]
|
|
25
|
+
file = parts.slice(1, parts.length).join('_')
|
|
26
|
+
s = "#{folder}/#{file}"
|
|
27
|
+
end
|
|
28
|
+
s
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'active_record'
|
|
2
|
+
module Doozer
|
|
3
|
+
module ORM
|
|
4
|
+
def self.load
|
|
5
|
+
db_config = Doozer::Configs.db()
|
|
6
|
+
#ActiveRecord::Base.allow_concurrency = true
|
|
7
|
+
config = {
|
|
8
|
+
:adapter => db_config["adapter"],
|
|
9
|
+
:host => db_config["host"],
|
|
10
|
+
:username => db_config["username"],
|
|
11
|
+
:password => db_config["password"],
|
|
12
|
+
:database => db_config["database"]
|
|
13
|
+
}
|
|
14
|
+
config[:pool] = db_config["pool"] if db_config["pool"]
|
|
15
|
+
config[:reconnect] = db_config["reconnect"] if db_config["reconnect"]
|
|
16
|
+
|
|
17
|
+
ActiveRecord::Base.establish_connection(config)
|
|
18
|
+
printf "ORM: #{Doozer::Configs.orm()} initialized...\n"
|
|
19
|
+
# printf "ORM: logging initialized"
|
|
20
|
+
ActiveRecord::Base.logger = Doozer::Configs.logger
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.after_request
|
|
24
|
+
ActiveRecord::Base.clear_active_connections!
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'dm-core'
|
|
2
|
+
module Doozer
|
|
3
|
+
|
|
4
|
+
# See for more info => http://datamapper.org/doku.php?id=getting_started_with_datamapper
|
|
5
|
+
module ORM
|
|
6
|
+
def self.load
|
|
7
|
+
db_config = Doozer::Configs.db()
|
|
8
|
+
DataMapper.setup(:default, {
|
|
9
|
+
:adapter => db_config["adapter"],
|
|
10
|
+
:database => db_config["database"],
|
|
11
|
+
:username => db_config["username"],
|
|
12
|
+
:password => db_config["password"],
|
|
13
|
+
:host => db_config["host"]
|
|
14
|
+
})
|
|
15
|
+
printf "ORM: #{Doozer::Configs.orm()} initialized...\n"
|
|
16
|
+
DataMapper::Logger.new(STDOUT, :debug)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.after_request; end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'sequel'
|
|
2
|
+
module Doozer
|
|
3
|
+
module ORM
|
|
4
|
+
|
|
5
|
+
# See for details => http://sequel.rubyforge.org/rdoc/index.html
|
|
6
|
+
def self.load
|
|
7
|
+
db_config = Doozer::Configs.db()
|
|
8
|
+
Doozer::Configs.db_conn = Sequel.connect({
|
|
9
|
+
:adapter => db_config["adapter"],
|
|
10
|
+
:database => db_config["database"],
|
|
11
|
+
:username => db_config["username"],
|
|
12
|
+
:password => db_config["password"],
|
|
13
|
+
:host => db_config["host"]
|
|
14
|
+
})
|
|
15
|
+
printf "ORM: #{Doozer::Configs.orm} initialized...\n"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.after_request; end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
require "erb"
|
|
2
|
+
require "doozer/lib"
|
|
3
|
+
require "doozer/view_helpers"
|
|
4
|
+
|
|
5
|
+
module Doozer
|
|
6
|
+
|
|
7
|
+
# This class facilitates loading and rendering of partials.
|
|
8
|
+
#
|
|
9
|
+
# A partial is an ERB template which starts with an underscore. They behave the same as action view ERB template with the only difference of not having access to Controller instance variables.
|
|
10
|
+
#
|
|
11
|
+
# An example partial: app/views/controller_name/_partial.html.erb
|
|
12
|
+
#
|
|
13
|
+
# By default, the Doozer scaffold creates an app/views/global folder which can be used to place global partials like headers, footers, etc.
|
|
14
|
+
#
|
|
15
|
+
# Partials have access to Doozer::ViewHelpers.
|
|
16
|
+
#
|
|
17
|
+
# All view helpers in app/helpers are automatically included in the Partial class during app initialize.
|
|
18
|
+
#
|
|
19
|
+
# A partial can render another partial and so on and so on.
|
|
20
|
+
class Partial
|
|
21
|
+
attr_accessor :erb, :route
|
|
22
|
+
|
|
23
|
+
include ERB::Util
|
|
24
|
+
include Doozer::Util::Logger
|
|
25
|
+
include Doozer::ViewHelpers
|
|
26
|
+
|
|
27
|
+
# APP_PATH = Dir.pwd
|
|
28
|
+
@@partials={}
|
|
29
|
+
|
|
30
|
+
def initialize(erb, locals, route)
|
|
31
|
+
@erb = erb
|
|
32
|
+
@route = route
|
|
33
|
+
if locals.kind_of? Hash
|
|
34
|
+
locals.each_pair {|key, value|
|
|
35
|
+
#p "#{key}:#{value}"
|
|
36
|
+
self.instance_variable_set("@#{key}".to_sym, value) # :@a, value
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def bind
|
|
42
|
+
@erb.result(binding)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# This class method lazily loads and caches the erb templates of the requested partials
|
|
46
|
+
def self.partial(file=nil, locals={}, route=route)
|
|
47
|
+
#p "Class method: Doozer::Partial#partial"
|
|
48
|
+
if file.index("/").nil?
|
|
49
|
+
name = "#{route.controller}/_#{file}"
|
|
50
|
+
else
|
|
51
|
+
name = "#{file.gsub(/\//,'/_')}"
|
|
52
|
+
end
|
|
53
|
+
load_partial(name) if @@partials[name].nil?
|
|
54
|
+
erb = @@partials[name]
|
|
55
|
+
if erb
|
|
56
|
+
partial = Doozer::Partial.new(erb, locals, route)
|
|
57
|
+
partial.bind()
|
|
58
|
+
else
|
|
59
|
+
printf "--no partial exists for #{file}\n"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Renders and returns a partial template with the given file_name and local variables.
|
|
64
|
+
#
|
|
65
|
+
# * file - expects a string. By default, if you don't pass a controller, it's assumed the lookup location is the current route.controller path in the views folder.
|
|
66
|
+
# You must omit the underscore when passing the file_name.
|
|
67
|
+
# A partial is automatically assumed to be html format. It shouldn't matter if you display an html partial inside a view with a different format.
|
|
68
|
+
#
|
|
69
|
+
# * locals - All local key/values are instantiated as instance variables acessable from the partial template. The controller.request variable is appended to locals and is also accessable as an instance variable from the partial template.
|
|
70
|
+
def partial(file=nil, locals={})
|
|
71
|
+
locals[:request] = @request if not @request.nil?
|
|
72
|
+
Doozer::Partial.partial(file, locals, route=@route)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Load and cache partial ERB template with the given file_name.
|
|
76
|
+
def self.load_partial(name)
|
|
77
|
+
file = File.join(Doozer::Configs.app_path,"app/views/#{name}.html.erb")
|
|
78
|
+
results = []
|
|
79
|
+
begin
|
|
80
|
+
File.new(file, "r").each { |line| results << line }
|
|
81
|
+
# TODO: throw error if doesn't exist
|
|
82
|
+
@@partials[name] = ERB.new(results.join(""))
|
|
83
|
+
rescue
|
|
84
|
+
printf "sorry couldn't load partial #{name} (#{file}) \n"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Class methods for clearing all cached partials. Mainly a dispatcher for the file watcher to pick up new changes without having to restart the appserver in development mode.
|
|
89
|
+
def self.clear_loaded_partials
|
|
90
|
+
@@partials = {}
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Class method for including a view helper.
|
|
94
|
+
def self.include_view_helper(helper)
|
|
95
|
+
m = Doozer::Lib.classify(helper)
|
|
96
|
+
include Object.const_get(m)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# = Doozer Paginate
|
|
2
|
+
# This code was lifted (i mean...ported) from the WillPaginate gem for ActiveRecord with a few modifications.
|
|
3
|
+
#
|
|
4
|
+
# Not supported by this plugin but in WillPAginate:
|
|
5
|
+
# * Page numbers between 'Previous' and 'Next' links
|
|
6
|
+
# * NamedScopes
|
|
7
|
+
#
|
|
8
|
+
# See http://wiki.github.com/mislav/will_paginate for more details on useage and license.
|
|
9
|
+
require "#{PAGINATE_PLUGIN_ROOT}/paginate/collection"
|
|
10
|
+
module Paginate
|
|
11
|
+
class << self
|
|
12
|
+
def enable_activerecord
|
|
13
|
+
return if ActiveRecord::Base.respond_to? :paginate
|
|
14
|
+
require "#{PAGINATE_PLUGIN_ROOT}/paginate/finder"
|
|
15
|
+
ActiveRecord::Base.send :include, Paginate::Finder
|
|
16
|
+
end
|
|
17
|
+
def enable_view_helpers
|
|
18
|
+
# return if Doozer::Initializer.respond_to? :paginate
|
|
19
|
+
require "#{PAGINATE_PLUGIN_ROOT}/paginate/view_helpers"
|
|
20
|
+
Doozer::Controller.send :include, Paginate::ViewHelpers
|
|
21
|
+
Doozer::Partial.send :include, Paginate::ViewHelpers
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Enable ActiveRecord if it's defined
|
|
27
|
+
Paginate.enable_activerecord if defined? ActiveRecord
|
|
28
|
+
|
|
29
|
+
# Load the View Helpers if Doozer::Initializer is loaded
|
|
30
|
+
Doozer::Initializer.before_rackup do | config |
|
|
31
|
+
Paginate.enable_view_helpers
|
|
32
|
+
end if defined? Doozer::Initializer
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module Paginate
|
|
2
|
+
|
|
3
|
+
class InvalidPage < ArgumentError
|
|
4
|
+
def initialize(page, page_num)
|
|
5
|
+
super "#{page.inspect} given as value, which translates to '#{page_num}' as page number"
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class Collection < Array
|
|
10
|
+
attr_reader :current_page, :per_page, :total_entries, :total_pages
|
|
11
|
+
|
|
12
|
+
def initialize(page, per_page, total = nil)
|
|
13
|
+
@current_page = page.to_i
|
|
14
|
+
raise InvalidPage.new(page, @current_page) if @current_page < 1
|
|
15
|
+
@per_page = per_page.to_i
|
|
16
|
+
raise ArgumentError, "`per_page` setting cannot be less than 1 (#{@per_page} given)" if @per_page < 1
|
|
17
|
+
|
|
18
|
+
self.total_entries = total if total
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.create(page, per_page, total = nil)
|
|
22
|
+
pager = new(page, per_page, total)
|
|
23
|
+
yield pager
|
|
24
|
+
pager
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def out_of_bounds?
|
|
28
|
+
current_page > total_pages
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def offset
|
|
32
|
+
(current_page - 1) * per_page
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def previous_page
|
|
36
|
+
current_page > 1 ? (current_page - 1) : nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def next_page
|
|
40
|
+
current_page < total_pages ? (current_page + 1) : nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def total_entries=(number)
|
|
44
|
+
@total_entries = number.to_i
|
|
45
|
+
@total_pages = (@total_entries / per_page.to_f).ceil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def replace(array)
|
|
49
|
+
result = super
|
|
50
|
+
|
|
51
|
+
# The collection is shorter then page limit? Rejoice, because
|
|
52
|
+
# then we know that we are on the last page!
|
|
53
|
+
if total_entries.nil? and length < per_page and (current_page == 1 or length > 0)
|
|
54
|
+
self.total_entries = offset + length
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
result
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
module Paginate
|
|
2
|
+
module Finder
|
|
3
|
+
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.extend ClassMethods
|
|
6
|
+
class << base
|
|
7
|
+
# alias_method_chain :method_missing, :paginate
|
|
8
|
+
# # alias_method_chain :find_every, :paginate
|
|
9
|
+
define_method(:per_page) { 30 } unless respond_to?(:per_page)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module ClassMethods
|
|
14
|
+
|
|
15
|
+
def paginate(*args)
|
|
16
|
+
options = args.pop
|
|
17
|
+
page, per_page, total_entries = paginate_parse_options(options)
|
|
18
|
+
|
|
19
|
+
finder = (options[:finder] || 'find').to_s
|
|
20
|
+
|
|
21
|
+
if finder == 'find'
|
|
22
|
+
# an array of IDs may have been given:
|
|
23
|
+
total_entries ||= (Array === args.first and args.first.size)
|
|
24
|
+
# :all is implicit
|
|
25
|
+
args.unshift(:all) if args.empty?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Paginate::Collection.create(page, per_page, total_entries) do |pager|
|
|
29
|
+
count_options = options.except :page, :per_page, :total_entries, :finder
|
|
30
|
+
find_options = count_options.except(:count).update(:offset => pager.offset, :limit => pager.per_page)
|
|
31
|
+
|
|
32
|
+
args << find_options
|
|
33
|
+
# @options_from_last_find = nil
|
|
34
|
+
pager.replace(send(finder, *args) { |*a| yield(*a) if block_given? })
|
|
35
|
+
|
|
36
|
+
# magic counting for user convenience:
|
|
37
|
+
pager.total_entries = paginate_count(count_options, args, finder) unless pager.total_entries
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def paginate_parse_options(options)
|
|
44
|
+
raise ArgumentError, 'parameter hash expected' unless options.respond_to? :symbolize_keys
|
|
45
|
+
options = options.symbolize_keys
|
|
46
|
+
raise ArgumentError, ':page parameter required' unless options.key? :page
|
|
47
|
+
|
|
48
|
+
if options[:count] and options[:total_entries]
|
|
49
|
+
raise ArgumentError, ':count and :total_entries are mutually exclusive'
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
page = options[:page] || 1
|
|
53
|
+
per_page = options[:per_page] || self.per_page
|
|
54
|
+
total = options[:total_entries]
|
|
55
|
+
[page, per_page, total]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Does the not-so-trivial job of finding out the total number of entries
|
|
59
|
+
# in the database. It relies on the ActiveRecord +count+ method.
|
|
60
|
+
def paginate_count(options, args, finder)
|
|
61
|
+
excludees = [:count, :order, :limit, :offset, :readonly]
|
|
62
|
+
excludees << :from unless ActiveRecord::Calculations::CALCULATIONS_OPTIONS.include?(:from)
|
|
63
|
+
|
|
64
|
+
# we may be in a model or an association proxy
|
|
65
|
+
klass = (@owner and @reflection) ? @reflection.klass : self
|
|
66
|
+
|
|
67
|
+
# Use :select from scope if it isn't already present.
|
|
68
|
+
options[:select] = scope(:find, :select) unless options[:select]
|
|
69
|
+
|
|
70
|
+
if options[:select] and options[:select] =~ /^\s*DISTINCT\b/i
|
|
71
|
+
# Remove quoting and check for table_name.*-like statement.
|
|
72
|
+
if options[:select].gsub('`', '') =~ /\w+\.\*/
|
|
73
|
+
options[:select] = "DISTINCT #{klass.table_name}.#{klass.primary_key}"
|
|
74
|
+
end
|
|
75
|
+
else
|
|
76
|
+
excludees << :select # only exclude the select param if it doesn't begin with DISTINCT
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# count expects (almost) the same options as find
|
|
80
|
+
count_options = options.except *excludees
|
|
81
|
+
|
|
82
|
+
# merge the hash found in :count
|
|
83
|
+
# this allows you to specify :select, :order, or anything else just for the count query
|
|
84
|
+
count_options.update options[:count] if options[:count]
|
|
85
|
+
|
|
86
|
+
# forget about includes if they are irrelevant (Rails 2.1)
|
|
87
|
+
# if count_options[:include] and
|
|
88
|
+
# klass.private_methods.include_method?(:references_eager_loaded_tables?) and
|
|
89
|
+
# !klass.send(:references_eager_loaded_tables?, count_options)
|
|
90
|
+
# count_options.delete :include
|
|
91
|
+
# end
|
|
92
|
+
|
|
93
|
+
# we may have to scope ...
|
|
94
|
+
counter = Proc.new { count(count_options) }
|
|
95
|
+
|
|
96
|
+
count = if finder.index('find_') == 0 and klass.respond_to?(scoper = finder.sub('find', 'with'))
|
|
97
|
+
# scope_out adds a 'with_finder' method which acts like with_scope, if it's present
|
|
98
|
+
# then execute the count with the scoping provided by the with_finder
|
|
99
|
+
send(scoper, &counter)
|
|
100
|
+
# elsif finder =~ /^find_(all_by|by)_([_a-zA-Z]\w*)$/
|
|
101
|
+
# # extract conditions from calls like "paginate_by_foo_and_bar"
|
|
102
|
+
# attribute_names = $2.split('_and_')
|
|
103
|
+
# conditions = construct_attributes_from_arguments(attribute_names, args)
|
|
104
|
+
# with_scope(:find => { :conditions => conditions }, &counter)
|
|
105
|
+
else
|
|
106
|
+
counter.call
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
count.respond_to?(:length) ? count.length : count
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
end # ClassMethods
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Paginate
|
|
2
|
+
module ViewHelpers
|
|
3
|
+
# default options that can be overridden on the global level
|
|
4
|
+
@@pagination_options = {
|
|
5
|
+
:class => 'pagination',
|
|
6
|
+
:previous_label => '« Previous',
|
|
7
|
+
:next_label => 'Next »',
|
|
8
|
+
:inner_window => 4, # links around the current page
|
|
9
|
+
:outer_window => 1, # links around beginning and end
|
|
10
|
+
:separator => ' ', # single space is friendly to spiders and non-graphic browsers
|
|
11
|
+
:param_name => :page,
|
|
12
|
+
:params => {},
|
|
13
|
+
:page_links => true,
|
|
14
|
+
:container => true,
|
|
15
|
+
:debug => false
|
|
16
|
+
}
|
|
17
|
+
mattr_reader :pagination_options
|
|
18
|
+
|
|
19
|
+
def paginate(collection, options={})
|
|
20
|
+
#Collection => :current_page, :per_page, :total_entries, :total_pages
|
|
21
|
+
opt = @@pagination_options
|
|
22
|
+
opt.update(options)
|
|
23
|
+
out=[]
|
|
24
|
+
if opt[:debug]
|
|
25
|
+
out.push("current_page:#{collection.current_page} / ")
|
|
26
|
+
out.push("per_page:#{collection.per_page} / ")
|
|
27
|
+
out.push("total_entries:#{collection.total_entries} / ")
|
|
28
|
+
out.push("total_pages:#{collection.total_pages} <br />")
|
|
29
|
+
end
|
|
30
|
+
out.push("<div class=\"pagination_container\">") if opt[:container]
|
|
31
|
+
out.push(link(opt[:previous_label], {:page=>collection.previous_page}.update(opt[:params]), {:class=>opt[:class]}) ) if collection.previous_page
|
|
32
|
+
out.push(link(opt[:next_label], {:page=>collection.next_page}.update(opt[:params]), {:class=>opt[:class]}) ) if collection.next_page
|
|
33
|
+
out.push("</div>") if opt[:container]
|
|
34
|
+
return out.join(opt[:separator])
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|